Случайный генератор IP-адресов

Я знаю, что есть, вероятно, лучшие и простые способы сделать это. Это было всего лишь учебное упражнение для ознакомления с Python.

Требуется один аргумент (позиционный параметр):
Либо 4 (IPv4), либо 6 (IPv6).

Соответствующие выходы будут выглядеть примерно так:
61.104.170.242
b4fc:391d:3ec8:68ef:0ec8:529b:166d:ece2


#!/usr/bin/env python3

from sys import argv

from random import randint, choice
from string import hexdigits

def random_ip(v):

    if v == 4:
        octets = []
        for x in range(4):
            octets.append(str(randint(0,255)))
        return '.'.join(octets)

    elif v == 6:
        hextets = []
        for x in range(8):
            hextet = []
            for x in range(4):
                hextet.append(str(choice(hexdigits.lower())))
            hextets.append(''.join(hextet))
        return ':'.join(hextets)

    else:
       return 

def main(): 
    print(random_ip(int(argv[1])))

if __name__ == '__main__':
    main()
30 голосов | спросил tjt263 26 J000000Thursday18 2018, 15:05:28

6 ответов


51

Python часто описывается как язык с включенными «аккумуляторами», и это не исключение.

Существует только модуль для обработки IP-адресов и другой модуль для генерации случайных чисел. Составленные вместе, они делают именно то, что вы хотите, таким образом, что это немного читаемо (IMO).

В этом примере я буду считать, что переменная v содержит либо 4, либо 6.

from random import getrandbits
from ipaddress import IPv4Address, IPv6Address

if v == 4:
    bits = getrandbits(32) # generates an integer with 32 random bits
    addr = IPv4Address(bits) # instances an IPv4Address object from those bits
    addr_str = str(addr) # get the IPv4Address object's string representation
elif v == 6:
    bits = getrandbits(128) # generates an integer with 128 random bits
    addr = IPv6Address(bits) # instances an IPv6Address object from those bits
    # .compressed contains the short version of the IPv6 address
    # str(addr) always returns the short address
    # .exploded is the opposite of this, always returning the full address with all-zero groups and so on
    addr_str = addr.compressed 

print(addr_str)

Здесь addr_str будет содержать полностью случайный IPv4 или IPv6-адрес.

Вы даже можете генерировать случайные адреса из такой подсети:

from random import getrandbits
from ipaddress import IPv4Network, IPv4Address

# network containing all addresses from 10.0.0.0 to 10.0.0.255
subnet = IPv4Network("10.0.0.0/24") 

# subnet.max_prefixlen contains 32 for IPv4 subnets and 128 for IPv6 subnets
# subnet.prefixlen is 24 in this case, so we'll generate only 8 random bits
bits = getrandbits(subnet.max_prefixlen - subnet.prefixlen)

# here, we combine the subnet and the random bits
# to get an IP address from the previously specified subnet
addr = IPv4Address(subnet.network_address + bits)
addr_str = str(addr)

print(addr_str)

Здесь addr_str всегда будет содержать IP-адреса, такие как 10.0.0.184, 10.0.0.42 и так далее. Он работает так же с IPv6-адресами, за исключением того, что вам придется импортировать IPv6Network и IPv6Address.

ответил Peter W. 26 J000000Thursday18 2018, 16:25:21
24

Вот несколько идей о вашем коде.

Проверьте аргументы командной строки

Сбой кода с исключением, если он вызывается без аргументов командной строки, потому что он пытается использовать argv[1], а его нет. Я бы предположил, что было бы неплохо напечатать сообщение об использовании, если пользователь вводит неверный или отсутствующий аргумент.

Использовать понимание списка

Список понятий чрезвычайно полезен и очень Pythonic. Это действительно хорошо, чтобы овладеть ими. Вот как использовать один для генерации случайного IPv4-адреса:

'.'.join([str(randint(0,255)) for x in range(4)])

И адрес IPv6 немного сложнее, потому что нам нужны шестнадцатеричные цифры.

':'.join([hex(randint(2**16,2**17))[-4:] for x in range(8)])

Это работает, потому что randint генерирует число в диапазоне от 0x10000 до 0x20000, и затем мы выбираем последние четыре шестнадцатеричных цифры.

ответил Edward 26 J000000Thursday18 2018, 15:41:45
19

Для кого-то, не знакомого с Python, вы выбрали довольно хорошие привычки. Не все используют функции или код if __name__ == '__main__' сначала попытаются.

Говоря, я думаю, что было бы разумнее предоставить 2 функции вместо одного: random_ipv4 и random_ipv6.

Вы также можете передать выражения генератора в join. Они быстрее обрабатываются, чем [] + for + append и легче читать:

def random_ipv4():
    return '.'.join(str(randint(0,255)) for _ in range(4))


def random_ipv6():
    return ':'.join(
            ''.join(choice(hexdigits).lower() for _ in range(4))
            for _ in range(8)
    )

Осталось только проверить правильность подтверждения ввода 4 или 6:

if __name__ == '__main__':
    if len(sys.argv) < 2:
        sys.exit('Usage: python random_ip.py VERSION')

    version = sys.argv[1]
    if version == '4':
        print(random_ipv4())
    elif version == '6':
        print(random_ipv6())
    else:
        sys.exit('VERSION should be 4 or 6, not {}'.format(version))
ответил Mathias Ettinger 26 J000000Thursday18 2018, 15:48:59
13

Проверьте наличие недопустимых выходов и восстановите их.

Возможно, вы создадите вывод, зарезервированный для определенной цели, например loopback (127.0.0.1 или ::1)) или трансляцию (255.255.255.255, ff02::1, ff02::2). Пополняйте знание таких адресов, и если вы обнаружите, что создали его, замените его. Вы можете сделать это рекурсивно:

def make_address(v):
    address = random_ip(v)
    if is_reserved(address):
        return make_address(v)
    return address
ответил Toby Speight 26 J000000Thursday18 2018, 15:50:19
12

Подтвердите ввод: В настоящее время ваша программа

  • прерывает с помощью IndexError, если вызывается без аргументов,
  • прерывает с помощью ValueError, если вызывается с нецелым аргументом,
  • prints None, если вызывается с целым аргументом, который не является 4 или 6.

Отсутствующие или недопустимые аргументы должны печатать полезное сообщение об ошибке. Большинство инструментов командной строки Unix выводит сообщение на стандартную ошибку и завершение с ненулевым статусом выхода в случае сбоя.

Было бы проще сравнить данный аргумент с строки "4" и "6" вместо преобразования его в целое число (который может выйти из строя).


Используйте понимание списка вместо добавления к массиву в цикле. Используйте _ как переменную итератора, если конкретное значение не требуется.

В качестве примера, случай «IPv4» может быть реализован как

if v == 4:
    return '.'.join(str(randint(0,255)) for _ in range(4))
ответил Martin R 26 J000000Thursday18 2018, 15:41:58
7

Поскольку у вас уже есть несколько ответов, в которых говорится, что вы проверяете свои входы, вот как это сделать, используя модуль argparse:

import argparse

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('-v',
                        type=int,
                        choices=(4, 6),
                        default=4,
                        help="Whether to generate a random IPv4 or IPv6 address. Default: IPv4.")
    args = parser.parse_args()
    print(random_ip(args.v))

Затем вы можете использовать его в командной строке следующим образом:

./randip
./randip -v 4
./randip -v4
./randip -v 6

Если вы сделаете что-нибудь еще, опубликовано полезное сообщение об использовании:

usage: [-h] [-v {4,6}]

optional arguments:
  -h, --help  show this help message and exit
  -v {4,6}    Whether to generate a random IPv4 or IPv6 address. Default:
              IPv4.
ответил Graipher 26 J000000Thursday18 2018, 18:27:22

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132