In Python, qual è il modo migliore per determinare se un indirizzo IP (ad es., '127.0.0.1'
o '10.98.76.6'
) è su un private network? Il codice non sembra difficile da scrivere. Ma ci possono essere più casi limite che sono immediatamente evidenti, e c'è il supporto IPv6 da considerare, ecc. Esiste una libreria esistente che lo fa?Come si determina se un indirizzo IP è privato, in Python?
risposta
Controllare il modulo IPy. Se ha una funzione iptype()
che sembra fare quello che vuoi:
>>> from IPy import IP
>>> ip = IP('127.0.0.0/30')
>>> ip.iptype()
'PRIVATE'
Se si vuole evitare l'importazione di un modulo che si può solo applicare una semplice espressione regolare:.
- ^127 \ d {1, 3}. \ D {1,3}. \ D {1,3} $
- ^10. \ D {1,3}. \ D {1,3}. \ D {1,3} $
- ^192.168. \ D {1,3} $
- ^172. (1 [6-9] | 2 [0-9] | 3 [0-1]). [0-9] {1, 3}. [0-9] {1,3} $
Non dovrebbe quelli. i caratteri possono essere sfuggiti tramite \. ? Immagino che il modello funzionerà comunque in entrambi i modi. –
Questa espressione regolare non ha funzionato per me, ma se si sostituisce {123} di {1,3}, funziona correttamente. – Tk421
Alcuni giorni dopo aver fatto questa domanda, ho scoperto questo progetto Google, ipaddr-py, che sembra avere alcune delle stesse funzionalità per determinare se un indirizzo è privato (is_rfc1918
). Apparentemente questo sarà standard in Python 3.1.
È provvisorio in 3.3 ora, consultare il modulo [indirizzoip] [1]. [1]: http://docs.python.org/3.3/library/ipaddress.html –
Vedere la proprietà "is_private". –
È possibile controllare se stessi utilizzando http://tools.ietf.org/html/rfc1918 e http://tools.ietf.org/html/rfc3330. Se hai 127.0.0.1 hai solo bisogno di &
con la maschera (diciamo 255.0.0.0
) e vedi se il valore corrisponde a uno qualsiasi della rete privata network address. Quindi, utilizzando inet_pton si può fare: 127.0.0.1 & 255.0.0.0 = 127.0.0.0
Ecco il codice che illustra che:
from struct import unpack
from socket import AF_INET, inet_pton
def lookup(ip):
f = unpack('!I',inet_pton(AF_INET,ip))[0]
private = (
[ 2130706432, 4278190080 ], # 127.0.0.0, 255.0.0.0 http://tools.ietf.org/html/rfc3330
[ 3232235520, 4294901760 ], # 192.168.0.0, 255.255.0.0 http://tools.ietf.org/html/rfc1918
[ 2886729728, 4293918720 ], # 172.16.0.0, 255.240.0.0 http://tools.ietf.org/html/rfc1918
[ 167772160, 4278190080 ], # 10.0.0.0, 255.0.0.0 http://tools.ietf.org/html/rfc1918
)
for net in private:
if (f & net[1]) == net[0]:
return True
return False
# example
print(lookup("127.0.0.1"))
print(lookup("192.168.10.1"))
print(lookup("10.10.10.10"))
print(lookup("172.17.255.255"))
# outputs True True True True
un'altra implementazione è quello di calcolare i valori int di tutti i blocchi privati:
from struct import unpack
from socket import AF_INET, inet_pton
lookup = "127.0.0.1"
f = unpack('!I',inet_pton(AF_INET,lookup))[0]
private = (["127.0.0.0","255.0.0.0"],["192.168.0.0","255.255.0.0"],["172.16.0.0","255.240.0.0"],["10.0.0.0","255.0.0.0"])
for net in private:
mask = unpack('!I',inet_aton(net[1]))[0]
p = unpack('!I',inet_aton(net[0]))[0]
if (f & mask) == p:
print lookup + " is private"
Questo è il versione fissa dell'approccio regex suggerito da @Kurt che include la correzione raccomandata da @RobEvans
- ^127. \ D {1,3}. \ D {1,3}. \ D {1,3} $
- ^10. \ D {1,3}. \ D {1,3} . \ d {1,3} $
- ^192.168. \ d {1,3}. \ d {1,3} $
^172. (1 [6-9] | 2 [0- 9] |.. 3 [0-1]) [0-9] {1,3} [0-9] {1,3} $
def is_ip_private(ip): # https://en.wikipedia.org/wiki/Private_network priv_lo = re.compile("^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$") priv_24 = re.compile("^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$") priv_20 = re.compile("^192\.168\.\d{1,3}.\d{1,3}$") priv_16 = re.compile("^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$") res = priv_lo.match(ip) or priv_24.match(ip) or priv_20.match(ip) or priv_16.match(ip) return res is not None
+1, suggerirei di restituire un "wrapper" booleano, per riflettere meglio il nome della funzione: return (priv_lo.match (ip) o priv_24.match (ip) o priv_20.match (ip) o priv_16.match (ip)) non è None – mork
Dal Python 3.3 c'è un ipaddress module nello stdlib che puoi usare.
>>> import ipaddress
>>> ipaddress.ip_address('192.168.0.1').is_private
True
Se si utilizza Python 2.6 o superiore vi consiglio vivamente di utilizzare a backport of this module.
Lo trovo in cuckoo. Non è necessario installare nuovi moduli. Importare solo due moduli incorporati: socket e struct. E usa la funzione qui sotto.
def _is_private_ip(self, ip):
"""Check if the IP belongs to private network blocks.
@param ip: IP address to verify.
@return: boolean representing whether the IP belongs or not to
a private network block.
"""
networks = [
"0.0.0.0/8",
"10.0.0.0/8",
"100.64.0.0/10",
"127.0.0.0/8",
"169.254.0.0/16",
"172.16.0.0/12",
"192.0.0.0/24",
"192.0.2.0/24",
"192.88.99.0/24",
"192.168.0.0/16",
"198.18.0.0/15",
"198.51.100.0/24",
"203.0.113.0/24",
"240.0.0.0/4",
"255.255.255.255/32",
"224.0.0.0/4",
]
for network in networks:
try:
ipaddr = struct.unpack(">I", socket.inet_aton(ip))[0]
netaddr, bits = network.split("/")
network_low = struct.unpack(">I", socket.inet_aton(netaddr))[0]
network_high = network_low | 1 << (32 - int(bits)) - 1
if ipaddr <= network_high and ipaddr >= network_low:
return True
except Exception,err:
continue
return False
Come previsto per un IP pubblico, la stringa è "PUBLIC". Ci sono alcuni risultati inaspettati: 'print (IP ('127.0.0.1'). Iptype())' produce 'PRIVATO'. 'print (IP (':: 1'). iptype())' produce 'LOOPBACK'. 'print (IP ('2001: 0658: 022a: cafe: 0200 :: 1'). iptype())' produce 'ALLOCATED RIPE NCC' –
Questo è perfetto! – Ryan
Ci sono in effetti alcuni risultati imprevisti: 'print (IP ('0.0.0.0/0'). Iptype())' produce 'PRIVATE', mentre mi aspetterei che "l'intero IPv4 internet" sia PUBBLICO ... – Niobos