tahoe-lafs/src/allmydata/util/iputil.py

125 lines
4.1 KiB
Python
Raw Normal View History

# adapted from nattraverso.ipdiscover
import subprocess
import re
import socket
from cStringIO import StringIO
from twisted.internet import reactor
from twisted.internet.protocol import DatagramProtocol
from twisted.internet.utils import getProcessOutput
from fcntl import ioctl
import struct
SIOCGIFADDR = 0x8915 # linux-specific
# inspired by scapy
def get_if_list():
f = open("/proc/net/dev","r")
f.readline(); f.readline()
names = []
for l in f.readlines():
names.append(l[:l.index(":")].strip())
return names
def get_if_addr(ifname):
try:
s=socket.socket()
ifreq = ioctl(s, SIOCGIFADDR, struct.pack("16s16x", ifname))
s.close()
naddr = ifreq[20:24]
return socket.inet_ntoa(naddr)
except IOError:
return None
def get_local_addresses():
"""Return a list of IPv4 addresses (as dotted-quad strings) that are
currently configured on this host.
This will only work under linux, because it uses both a linux-specific
/proc/net/dev devices (to get the interface names) and a SIOCGIFADDR
ioctl (to get their addresses). If the listing-the-interfaces were done
with an ioctl too (and if if you're lucky enough to be using the same
value for the ioctls), then it might work on other forms of unix too.
Windows is right out."""
ifnames = []
for ifname in get_if_list():
addr = get_if_addr(ifname)
if addr:
ifnames.append(addr)
return ifnames
def get_local_addresses_sync():
"""Return a list of IPv4 addresses (as dotted-quad strings) that are
currently configured on this host.
Unfortunately this is not compatible with Twisted: it catches SIGCHLD and
this usually results in errors about 'Interrupted system call'.
This will probably work on both linux and OS-X, but probably not windows.
"""
# eventually I want to use somebody else's cross-platform library for
# this. For right now, I'm running ifconfig and grepping for the 'inet '
# lines.
cmd = "/sbin/ifconfig"
#p = os.popen(cmd)
c = subprocess.Popen(["ifconfig"], stdout=subprocess.PIPE)
output = c.communicate()[0]
p = StringIO(output)
addresses = []
for line in p.readlines():
# linux shows: " inet addr:1.2.3.4 Bcast:1.2.3.255..."
# OS-X shows: " inet 1.2.3.4 ..."
m = re.match("^\s+inet\s+[a-z:]*([\d\.]+)\s", line)
if m:
addresses.append(m.group(1))
return addresses
def get_local_addresses_async():
"""Return a Deferred that fires with a list of IPv4 addresses (as
dotted-quad strings) that are currently configured on this host.
This will probably work on both linux and OS-X, but probably not windows.
"""
# eventually I want to use somebody else's cross-platform library for
# this. For right now, I'm running ifconfig and grepping for the 'inet '
# lines.
# I'd love to do this synchronously.
cmd = "/sbin/ifconfig"
d = getProcessOutput(cmd)
def _parse(output):
addresses = []
for line in StringIO(output).readlines():
# linux shows: " inet addr:1.2.3.4 Bcast:1.2.3.255..."
# OS-X shows: " inet 1.2.3.4 ..."
m = re.match("^\s+inet\s+[a-z:]*([\d\.]+)\s", line)
if m:
addresses.append(m.group(1))
return addresses
def _fallback(f):
return ["127.0.0.1", get_local_ip_for()]
d.addCallbacks(_parse, _fallback)
return d
def get_local_ip_for(target='A.ROOT-SERVERS.NET'):
"""Find out what our IP address is for use by a given target.
Returns a string that holds the IP address which could be used by
'target' to connect to us. It might work for them, it might not.
"""
try:
target_ipaddr = socket.gethostbyname(target)
except socket.gaierror:
return "127.0.0.1"
udpprot = DatagramProtocol()
port = reactor.listenUDP(0, udpprot)
udpprot.transport.connect(target_ipaddr, 7)
localip = udpprot.transport.getHost().host
port.stopListening() # note, this returns a Deferred
return localip