Find my Ip address in python [duplicate] - python

How can I find local IP addresses (i.e. 192.168.x.x or 10.0.x.x) in Python platform independently and using only the standard library?

I just found this but it seems a bit hackish, however they say tried it on *nix and I did on windows and it worked.
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
print(s.getsockname()[0])
s.close()
This assumes you have an internet access, and that there is no local proxy.

import socket
socket.gethostbyname(socket.gethostname())
This won't work always (returns 127.0.0.1 on machines having the hostname in /etc/hosts as 127.0.0.1), a paliative would be what gimel shows, use socket.getfqdn() instead. Of course your machine needs a resolvable hostname.

This method returns the "primary" IP on the local box (the one with a default route).
Does NOT need routable net access or any connection at all.
Works even if all interfaces are unplugged from the network.
Does NOT need or even try to get anywhere else.
Works with NAT, public, private, external, and internal IP's
Pure Python 2 (or 3) with no external dependencies.
Works on Linux, Windows, and OSX.
Python 3 or 2:
import socket
def get_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(0)
try:
# doesn't even have to be reachable
s.connect(('10.254.254.254', 1))
IP = s.getsockname()[0]
except Exception:
IP = '127.0.0.1'
finally:
s.close()
return IP
print(get_ip())
This returns a single IP which is the primary (the one with a default route). If you need instead all IP's attached to all interfaces (including localhost, etc), see something like this answer.
If you are behind a NAT firewall like your wifi router at home, then this will not show your public NAT IP, but instead your private IP on the local network which has a default route to your local WIFI router. If you instead need your external IP:
running this function on THAT external device (wifi router), or
connecting to an external service such as https://www.ipify.org/ that could reflect back the IP as it's seen from the outside world
... but those ideas are completely different from the original question. :)

As an alias called myip:
alias myip="python -c 'import socket; print([l for l in ([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith(\"127.\")][:1], [[(s.connect((\"8.8.8.8\", 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) if l][0][0])'"
Works correctly with Python 2.x, Python 3.x, modern and old Linux distros, OSX/macOS and Windows for finding the current IPv4 address.
Will not return the correct result for machines with multiple IP addresses, IPv6, no configured IP address or no internet access.
Reportedly, this does not work on the latest releases of macOS.
NOTE: If you intend to use something like this within a Python program, the proper way is to make use of a Python module that has IPv6 support.
Same as above, but only the Python code:
import socket
print([l for l in ([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")][:1], [[(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) if l][0][0])
This will throw an exception if no IP address is configured.
Version that will also work on LANs without an internet connection:
import socket
print((([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")] or [[(s.connect(("8.8.8.8", 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) + ["no IP found"])[0])
(thanks #ccpizza)
Background:
Using socket.gethostbyname(socket.gethostname()) did not work here, because one of the computers I was on had an /etc/hosts with duplicate entries and references to itself. socket.gethostbyname() only returns the last entry in /etc/hosts.
This was my initial attempt, which weeds out all addresses starting with "127.":
import socket
print([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")][:1])
This works with Python 2 and 3, on Linux and Windows, but does not deal with several network devices or IPv6. However, it stopped working on recent Linux distros, so I tried this alternative technique instead. It tries to connect to the Google DNS server at 8.8.8.8 at port 53:
import socket
print([(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1])
Then I combined the two above techniques into a one-liner that should work everywhere, and created the myip alias and Python snippet at the top of this answer.
With the increasing popularity of IPv6, and for servers with multiple network interfaces, using a third-party Python module for finding the IP address is probably both more robust and reliable than any of the methods listed here.

You can use the netifaces module. Just type:
pip install netifaces
in your command shell and it will install itself on default Python installation.
Then you can use it like this:
from netifaces import interfaces, ifaddresses, AF_INET
for ifaceName in interfaces():
addresses = [i['addr'] for i in ifaddresses(ifaceName).setdefault(AF_INET, [{'addr':'No IP addr'}] )]
print '%s: %s' % (ifaceName, ', '.join(addresses))
On my computer it printed:
{45639BDC-1050-46E0-9BE9-075C30DE1FBC}: 192.168.0.100
{D43A468B-F3AE-4BF9-9391-4863A4500583}: 10.5.9.207
Author of this module claims it should work on Windows, UNIX and Mac OS X.

If the computer has a route to the Internet, this will always work to get the preferred local ip address, even if /etc/hosts is not set correctly.
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 1)) # connect() for UDP doesn't send packets
local_ip_address = s.getsockname()[0]

Socket API method
see https://stackoverflow.com/a/28950776/711085
Downsides:
Not cross-platform.
Requires more fallback code, tied to existence of particular addresses on the internet
This will also not work if you're behind a NAT
Probably creates a UDP connection, not independent of (usually ISP's) DNS availability (see other answers for ideas like using 8.8.8.8: Google's (coincidentally also DNS) server)
Make sure you make the destination address UNREACHABLE, like a numeric IP address that is spec-guaranteed to be unused. Do NOT use some domain like fakesubdomain.google.com or somefakewebsite.com; you'll still be spamming that party (now or in the future), and spamming your own network boxes as well in the process.
Reflector method
(Do note that this does not answer the OP's question of the local IP address, e.g. 192.168...; it gives you your public IP address, which might be more desirable depending on use case.)
You can query some site like whatismyip.com (but with an API), such as:
from urllib.request import urlopen
import re
def getPublicIp():
data = str(urlopen('http://checkip.dyndns.com/').read())
# data = '<html><head><title>Current IP Check</title></head><body>Current IP Address: 65.96.168.198</body></html>\r\n'
return re.compile(r'Address: (\d+\.\d+\.\d+\.\d+)').search(data).group(1)
or if using python2:
from urllib import urlopen
import re
def getPublicIp():
data = str(urlopen('http://checkip.dyndns.com/').read())
# data = '<html><head><title>Current IP Check</title></head><body>Current IP Address: 65.96.168.198</body></html>\r\n'
return re.compile(r'Address: (\d+\.\d+\.\d+\.\d+)').search(data).group(1)
Advantages:
One upside of this method is it's cross-platform
It works from behind ugly NATs (e.g. your home router).
Disadvantages (and workarounds):
Requires this website to be up, the format to not change (almost certainly won't), and your DNS servers to be working. One can mitigate this issue by also querying other third-party IP address reflectors in case of failure.
Possible attack vector if you don't query multiple reflectors (to prevent a compromised reflector from telling you that your address is something it's not), or if you don't use HTTPS (to prevent a man-in-the-middle attack pretending to be the server)
edit: Though initially I thought these methods were really bad (unless you use many fallbacks, the code may be irrelevant many years from now), it does pose the question "what is the internet?". A computer may have many interfaces pointing to many different networks. For a more thorough description of the topic, google for gateways and routes. A computer may be able to access an internal network via an internal gateway, or access the world-wide web via a gateway on for example a router (usually the case). The local IP address that the OP asks about is only well-defined with respect to a single link layer, so you have to specify that ("is it the network card, or the ethernet cable, which we're talking about?"). There may be multiple non-unique answers to this question as posed. However the global IP address on the world-wide web is probably well-defined (in the absence of massive network fragmentation): probably the return path via the gateway which can access the TLDs.

On Linux:
>>> import socket, struct, fcntl
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> sockfd = sock.fileno()
>>> SIOCGIFADDR = 0x8915
>>>
>>> def get_ip(iface = 'eth0'):
... ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)
... try:
... res = fcntl.ioctl(sockfd, SIOCGIFADDR, ifreq)
... except:
... return None
... ip = struct.unpack('16sH2x4s8x', res)[2]
... return socket.inet_ntoa(ip)
...
>>> get_ip('eth0')
'10.80.40.234'
>>>

im using following module:
#!/usr/bin/python
# module for getting the lan ip address of the computer
import os
import socket
if os.name != "nt":
import fcntl
import struct
def get_interface_ip(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', bytes(ifname[:15], 'utf-8'))
# Python 2.7: remove the second argument for the bytes call
)[20:24])
def get_lan_ip():
ip = socket.gethostbyname(socket.gethostname())
if ip.startswith("127.") and os.name != "nt":
interfaces = ["eth0","eth1","eth2","wlan0","wlan1","wifi0","ath0","ath1","ppp0"]
for ifname in interfaces:
try:
ip = get_interface_ip(ifname)
break;
except IOError:
pass
return ip
Tested with windows and linux (and doesnt require additional modules for those)
intended for use on systems which are in a single IPv4 based LAN.
The fixed list of interface names does not work for recent linux versions, which have adopted the systemd v197 change regarding predictable interface names as pointed out by Alexander.
In such cases, you need to manually replace the list with the interface names on your system, or use another solution like netifaces.

[Windows only] If you don't want to use external packages and don't want to rely on outside Internet servers, this might help. It's a code sample that I found on Google Code Search and modified to return required information:
def getIPAddresses():
from ctypes import Structure, windll, sizeof
from ctypes import POINTER, byref
from ctypes import c_ulong, c_uint, c_ubyte, c_char
MAX_ADAPTER_DESCRIPTION_LENGTH = 128
MAX_ADAPTER_NAME_LENGTH = 256
MAX_ADAPTER_ADDRESS_LENGTH = 8
class IP_ADDR_STRING(Structure):
pass
LP_IP_ADDR_STRING = POINTER(IP_ADDR_STRING)
IP_ADDR_STRING._fields_ = [
("next", LP_IP_ADDR_STRING),
("ipAddress", c_char * 16),
("ipMask", c_char * 16),
("context", c_ulong)]
class IP_ADAPTER_INFO (Structure):
pass
LP_IP_ADAPTER_INFO = POINTER(IP_ADAPTER_INFO)
IP_ADAPTER_INFO._fields_ = [
("next", LP_IP_ADAPTER_INFO),
("comboIndex", c_ulong),
("adapterName", c_char * (MAX_ADAPTER_NAME_LENGTH + 4)),
("description", c_char * (MAX_ADAPTER_DESCRIPTION_LENGTH + 4)),
("addressLength", c_uint),
("address", c_ubyte * MAX_ADAPTER_ADDRESS_LENGTH),
("index", c_ulong),
("type", c_uint),
("dhcpEnabled", c_uint),
("currentIpAddress", LP_IP_ADDR_STRING),
("ipAddressList", IP_ADDR_STRING),
("gatewayList", IP_ADDR_STRING),
("dhcpServer", IP_ADDR_STRING),
("haveWins", c_uint),
("primaryWinsServer", IP_ADDR_STRING),
("secondaryWinsServer", IP_ADDR_STRING),
("leaseObtained", c_ulong),
("leaseExpires", c_ulong)]
GetAdaptersInfo = windll.iphlpapi.GetAdaptersInfo
GetAdaptersInfo.restype = c_ulong
GetAdaptersInfo.argtypes = [LP_IP_ADAPTER_INFO, POINTER(c_ulong)]
adapterList = (IP_ADAPTER_INFO * 10)()
buflen = c_ulong(sizeof(adapterList))
rc = GetAdaptersInfo(byref(adapterList[0]), byref(buflen))
if rc == 0:
for a in adapterList:
adNode = a.ipAddressList
while True:
ipAddr = adNode.ipAddress
if ipAddr:
yield ipAddr
adNode = adNode.next
if not adNode:
break
Usage:
>>> for addr in getIPAddresses():
>>> print addr
192.168.0.100
10.5.9.207
As it relies on windll, this will work only on Windows.

I use this on my ubuntu machines:
import commands
commands.getoutput("/sbin/ifconfig").split("\n")[1].split()[1][5:]
This doesn't work.

Variation on ninjagecko's answer. This should work on any LAN that allows UDP broadcast and doesn't require access to an address on the LAN or internet.
import socket
def getNetworkIp():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.connect(('<broadcast>', 0))
return s.getsockname()[0]
print (getNetworkIp())

On Debian (tested) and I suspect most Linux's..
import commands
RetMyIP = commands.getoutput("hostname -I")
On MS Windows (tested)
import socket
socket.gethostbyname(socket.gethostname())

A version I do not believe that has been posted yet.
I tested with python 2.7 on Ubuntu 12.04.
Found this solution at : http://code.activestate.com/recipes/439094-get-the-ip-address-associated-with-a-network-inter/
import socket
import fcntl
import struct
def get_ip_address(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', ifname[:15])
)[20:24])
Example Result:
>>> get_ip_address('eth0')
'38.113.228.130'

For linux, you can just use check_output of the hostname -I system command like so:
from subprocess import check_output
check_output(['hostname', '-I'])

This is a variant of UnkwnTech's answer -- it provides a get_local_addr() function, which returns the primary LAN ip address of the host. I'm posting it because this adds a number of things: ipv6 support, error handling, ignoring localhost/linklocal addrs, and uses a TESTNET addr (rfc5737) to connect to.
# imports
import errno
import socket
import logging
# localhost prefixes
_local_networks = ("127.", "0:0:0:0:0:0:0:1")
# ignore these prefixes -- localhost, unspecified, and link-local
_ignored_networks = _local_networks + ("0.", "0:0:0:0:0:0:0:0", "169.254.", "fe80:")
def detect_family(addr):
if "." in addr:
assert ":" not in addr
return socket.AF_INET
elif ":" in addr:
return socket.AF_INET6
else:
raise ValueError("invalid ipv4/6 address: %r" % addr)
def expand_addr(addr):
"""convert address into canonical expanded form --
no leading zeroes in groups, and for ipv6: lowercase hex, no collapsed groups.
"""
family = detect_family(addr)
addr = socket.inet_ntop(family, socket.inet_pton(family, addr))
if "::" in addr:
count = 8-addr.count(":")
addr = addr.replace("::", (":0" * count) + ":")
if addr.startswith(":"):
addr = "0" + addr
return addr
def _get_local_addr(family, remote):
try:
s = socket.socket(family, socket.SOCK_DGRAM)
try:
s.connect((remote, 9))
return s.getsockname()[0]
finally:
s.close()
except socket.error:
# log.info("trapped error connecting to %r via %r", remote, family, exc_info=True)
return None
def get_local_addr(remote=None, ipv6=True):
"""get LAN address of host
:param remote:
return LAN address that host would use to access that specific remote address.
by default, returns address it would use to access the public internet.
:param ipv6:
by default, attempts to find an ipv6 address first.
if set to False, only checks ipv4.
:returns:
primary LAN address for host, or ``None`` if couldn't be determined.
"""
if remote:
family = detect_family(remote)
local = _get_local_addr(family, remote)
if not local:
return None
if family == socket.AF_INET6:
# expand zero groups so the startswith() test works.
local = expand_addr(local)
if local.startswith(_local_networks):
# border case where remote addr belongs to host
return local
else:
# NOTE: the two addresses used here are TESTNET addresses,
# which should never exist in the real world.
if ipv6:
local = _get_local_addr(socket.AF_INET6, "2001:db8::1234")
# expand zero groups so the startswith() test works.
if local:
local = expand_addr(local)
else:
local = None
if not local:
local = _get_local_addr(socket.AF_INET, "192.0.2.123")
if not local:
return None
if local.startswith(_ignored_networks):
return None
return local

I'm afraid there aren't any good platform independent ways to do this other than connecting to another computer and having it send you your IP address. For example: findmyipaddress. Note that this won't work if you need an IP address that's behind NAT unless the computer you're connecting to is behind NAT as well.
Here's one solution that works in Linux: get the IP address associated with a network interface.

FYI I can verify that the method:
import socket
addr = socket.gethostbyname(socket.gethostname())
Works in OS X (10.6,10.5), Windows XP, and on a well administered RHEL department server. It did not work on a very minimal CentOS VM that I just do some kernel hacking on. So for that instance you can just check for a 127.0.0.1 address and in that case do the following:
if addr == "127.0.0.1":
import commands
output = commands.getoutput("/sbin/ifconfig")
addr = parseaddress(output)
And then parse the ip address from the output. It should be noted that ifconfig is not in a normal user's PATH by default and that is why I give the full path in the command. I hope this helps.

One simple way to produce "clean" output via command line utils:
import commands
ips = commands.getoutput("/sbin/ifconfig | grep -i \"inet\" | grep -iv \"inet6\" | " +
"awk {'print $2'} | sed -ne 's/addr\:/ /p'")
print ips
It will show all IPv4 addresses on the system.

import socket
[i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]

This will work on most linux boxes:
import socket, subprocess, re
def get_ipv4_address():
"""
Returns IP address(es) of current machine.
:return:
"""
p = subprocess.Popen(["ifconfig"], stdout=subprocess.PIPE)
ifc_resp = p.communicate()
patt = re.compile(r'inet\s*\w*\S*:\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
resp = patt.findall(ifc_resp[0])
print resp
get_ipv4_address()

This answer is my personal attempt to solve the problem of getting the LAN IP, since socket.gethostbyname(socket.gethostname()) also returned 127.0.0.1. This method does not require Internet just a LAN connection. Code is for Python 3.x but could easily be converted for 2.x. Using UDP Broadcast:
import select
import socket
import threading
from queue import Queue, Empty
def get_local_ip():
def udp_listening_server():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('<broadcast>', 8888))
s.setblocking(0)
while True:
result = select.select([s],[],[])
msg, address = result[0][0].recvfrom(1024)
msg = str(msg, 'UTF-8')
if msg == 'What is my LAN IP address?':
break
queue.put(address)
queue = Queue()
thread = threading.Thread(target=udp_listening_server)
thread.queue = queue
thread.start()
s2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s2.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
waiting = True
while waiting:
s2.sendto(bytes('What is my LAN IP address?', 'UTF-8'), ('<broadcast>', 8888))
try:
address = queue.get(False)
except Empty:
pass
else:
waiting = False
return address[0]
if __name__ == '__main__':
print(get_local_ip())

If you're looking for an IPV4 address different from your localhost IP address 127.0.0.1, here is a neat piece of python codes:
import subprocess
address = subprocess.check_output(['hostname', '-s', '-I'])
address = address.decode('utf-8')
address=address[:-1]
Which can also be written in a single line:
address = subprocess.check_output(['hostname', '-s', '-I']).decode('utf-8')[:-1]
Even if you put localhost in /etc/hostname, the code will still give your local IP address.

127.0.1.1 is your real IP address. More generally speaking, a computer can have any number of IP addresses. You can filter them for private networks - 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16.
However, there is no cross-platform way to get all IP addresses. On Linux, you can use the SIOCGIFCONF ioctl.

A slight refinement of the commands version that uses the IP command, and returns IPv4 and IPv6 addresses:
import commands,re,socket
#A generator that returns stripped lines of output from "ip address show"
iplines=(line.strip() for line in commands.getoutput("ip address show").split('\n'))
#Turn that into a list of IPv4 and IPv6 address/mask strings
addresses1=reduce(lambda a,v:a+v,(re.findall(r"inet ([\d.]+/\d+)",line)+re.findall(r"inet6 ([\:\da-f]+/\d+)",line) for line in iplines))
#addresses1 now looks like ['127.0.0.1/8', '::1/128', '10.160.114.60/23', 'fe80::1031:3fff:fe00:6dce/64']
#Get a list of IPv4 addresses as (IPstring,subnetsize) tuples
ipv4s=[(ip,int(subnet)) for ip,subnet in (addr.split('/') for addr in addresses1 if '.' in addr)]
#ipv4s now looks like [('127.0.0.1', 8), ('10.160.114.60', 23)]
#Get IPv6 addresses
ipv6s=[(ip,int(subnet)) for ip,subnet in (addr.split('/') for addr in addresses1 if ':' in addr)]

Well you can use the command "ip route" on GNU/Linux to know your current IP address.
This shows the IP given to the interface by the DHCP server running on the router/modem. Usually "192.168.1.1/24" is the IP for local network where "24" means the range of posible IP addresses given by the DHCP server within the mask range.
Here's an example: Note that PyNotify is just an addition to get my point straight and is not required at all
#! /usr/bin/env python
import sys , pynotify
if sys.version_info[1] != 7:
raise RuntimeError('Python 2.7 And Above Only')
from subprocess import check_output # Available on Python 2.7+ | N/A
IP = check_output(['ip', 'route'])
Split_Result = IP.split()
# print Split_Result[2] # Remove "#" to enable
pynotify.init("image")
notify = pynotify.Notification("Ip", "Server Running At:" + Split_Result[2] , "/home/User/wireless.png")
notify.show()
The advantage of this is that you don't need to specify the network interface. That's pretty useful when running a socket server
You can install PyNotify using easy_install or even Pip:
easy_install py-notify
or
pip install py-notify
or within python script/interpreter
from pip import main
main(['install', 'py-notify'])

netifaces is available via pip and easy_install. (I know, it's not in base, but it could be worth the install.)
netifaces does have some oddities across platforms:
The localhost/loop-back interface may not always be included (Cygwin).
Addresses are listed per-protocol (e.g., IPv4, IPv6) and protocols are listed per-interface. On some systems (Linux) each protocol-interface pair has its own associated interface (using the interface_name:n notation) while on other systems (Windows) a single interface will have a list of addresses for each protocol. In both cases there is a protocol list, but it may contain only a single element.
Here's some netifaces code to play with:
import netifaces
PROTO = netifaces.AF_INET # We want only IPv4, for now at least
# Get list of network interfaces
# Note: Can't filter for 'lo' here because Windows lacks it.
ifaces = netifaces.interfaces()
# Get all addresses (of all kinds) for each interface
if_addrs = [netifaces.ifaddresses(iface) for iface in ifaces]
# Filter for the desired address type
if_inet_addrs = [addr[PROTO] for addr in if_addrs if PROTO in addr]
iface_addrs = [s['addr'] for a in if_inet_addrs for s in a if 'addr' in s]
# Can filter for '127.0.0.1' here.
The above code doesn't map an address back to its interface name (useful for generating ebtables/iptables rules on the fly). So here's a version that keeps the above information with the interface name in a tuple:
import netifaces
PROTO = netifaces.AF_INET # We want only IPv4, for now at least
# Get list of network interfaces
ifaces = netifaces.interfaces()
# Get addresses for each interface
if_addrs = [(netifaces.ifaddresses(iface), iface) for iface in ifaces]
# Filter for only IPv4 addresses
if_inet_addrs = [(tup[0][PROTO], tup[1]) for tup in if_addrs if PROTO in tup[0]]
iface_addrs = [(s['addr'], tup[1]) for tup in if_inet_addrs for s in tup[0] if 'addr' in s]
And, no, I'm not in love with list comprehensions. It's just the way my brain works these days.
The following snippet will print it all out:
from __future__ import print_function # For 2.x folks
from pprint import pprint as pp
print('\nifaces = ', end='')
pp(ifaces)
print('\nif_addrs = ', end='')
pp(if_addrs)
print('\nif_inet_addrs = ', end='')
pp(if_inet_addrs)
print('\niface_addrs = ', end='')
pp(iface_addrs)
Enjoy!

import netifaces as ni
ni.ifaddresses('eth0')
ip = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr']
print(ip)
This will return you the IP address in the Ubuntu system as well as MacOS. The output will be the system IP address as like my IP: 192.168.1.10.

To get the ip address you can use a shell command directly in python:
import socket, subprocess
def get_ip_and_hostname():
hostname = socket.gethostname()
shell_cmd = "ifconfig | awk '/inet addr/{print substr($2,6)}'"
proc = subprocess.Popen([shell_cmd], stdout=subprocess.PIPE, shell=True)
(out, err) = proc.communicate()
ip_list = out.split('\n')
ip = ip_list[0]
for _ip in ip_list:
try:
if _ip != "127.0.0.1" and _ip.split(".")[3] != "1":
ip = _ip
except:
pass
return ip, hostname
ip_addr, hostname = get_ip_and_hostname()

A Python 3.4 version utilizing the newly introduced asyncio package.
async def get_local_ip():
loop = asyncio.get_event_loop()
transport, protocol = await loop.create_datagram_endpoint(
asyncio.DatagramProtocol,
remote_addr=('8.8.8.8', 80))
result = transport.get_extra_info('sockname')[0]
transport.close()
return result
This is based on UnkwnTech's excellent answer.

Related

UDP Hole Punching fails on non-symmetric NATs

For a little while now, I got a serious issue on my UDP hole punching system which may involve things that are out of my reach now. I'll try to explain as precisely as possible because this is really bothering me :
UDP Hole punching method involves a third host S, which basically is public (not behind any kind of NAT or persistent firewall), that will be used by two clients A and B to get to know each other. Client A sends a udp packet to S, such that S can know its public IP and Port (which were mapped by A's NAT). Client B does the same. Then S matches A and B by sending to A B's public address, and A's public address to B. Therefore, they each know each other's address and can proceed to communicate.
So far so good, but here is the catch : it works only on special cases.
I am aware of the problem caused by Symmetric NATs (NATs that map your private IP and Port to a specific different address for each destination address you want to reach, so let's say I send a packet to S1 and S2, S1 will see a public address with port 50263 whereas S2 will see 50264). I am also aware that some Non-Symmetric NAT require you to send a UDP packet to a specific address before allowing to receive one from the same address.
So what I've understood so far is that as long as your NAT is not Symmetric, when opening a new socket, and send whatever byte array, your NAT will allocate a public IP and port to it. The public server tells your peer this public address such that it can communicate with you. You then have to send a packet to that peer in order to allow him to reply.
But it actually doesn't work everytime. I've tried at my university appartment, and managed to connect to myself using my public IP address, even by being behind a NAT. I also managed to send a UDP packet to a friend's, but it worked only once in the hundred of tests I made. I am now trying at home and it doesn't work at all.
I've setup 2 public VPS on the internet, and sent one packet to each one of them to see if my NAT was Symmetric, but no, the port that gets displayed on both of them is the same. I just can reach myself only from those public servers. Using Wireshark, I observe the packets leaving my computer but it seems that they get dropped beofre going back.
I've written simple python scripts to try out sending simply to myself :
Server
import socket
import sys
from helper import *
# This script is used to allow a client to know which external address it has.
def main():
# Port used to receive client requests
port = 6666
# We create a UDP socket on this port
sockfd = socket.socket (socket.AF_INET, socket.SOCK_DGRAM)
sockfd.bind (("", port))
print ("Address discoverer started on port %d" % port)
# Server loop
while True:
# We wait for any client to request for his address.
data, addr = sockfd.recvfrom(1)
# Once a client has sent a request, we send him back his external address, which we obtained using the packet he sent us.
sockfd.sendto (addr2bytes(addr), addr)
print ("Client %s:%d requested his address" % addr)
if __name__ == "__main__":
main()
Client
import sys
import socket
from helper import *
def main():
master = ("xxx.xxx.xxx.xxx", 6666)
me = ("yyy.yyy.yyy.yyy", 6666)
sockfd = socket.socket (socket.AF_INET, socket.SOCK_DGRAM)
sockfd.bind(('',6666))
sockfd.sendto (bytearray([0]), master)
data, addr = sockfd.recvfrom (6)
a = bytes2addr(data)
print ('My address is %s:%d' % a)
for i in range (0, 10):
sockfd.sendto ('hi myself'.encode('utf-8'), me)
data, addr = sockfd.recvfrom (1024)
print ("Received : " + data.decode('utf-8'))
if __name__ == "__main__":
main()
Executed on one of my public VPS, it works. Executed at home, I don't receive anything. It seems that sending from a public host grants all accesses, but behind a NAT is completely different.
Am I missing something fundamental ? I think I did pretty much every possible test but no success.
Depending on what you want to achieve, you may want to use IGD (spec) to retrieve your public IP and/or configure port forwarding automatically. Most home routers support UPnP IGD, from what I can tell.
Here's an example script that uses miniupnpc, a minimal UPnP IGD client, and the ipaddress and socket modules from python's standard library:
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
IGDTestScript.py
================
Description: UPnP IGD example script
Author: shaggyrogers
Creation Date: 2018-03-28
License: None / Public domain
"""
import ipaddress
import socket
import sys
import miniupnpc
def main(args):
# Get IP address for default interface
allhosts = ipaddress.ip_address(socket.INADDR_ALLHOSTS_GROUP)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.connect((allhosts.compressed, 0))
localIP = ipaddress.ip_address(sock.getsockname()[0])
if localIP.is_global:
print(f'Public IP: {localIP} (no NAT)')
return 0
print(f'Local IP: {localIP}')
# Discover and connect to IGD device
igd = miniupnpc.UPnP()
assert igd.discover() > 0
assert igd.selectigd() != ''
# Get public IP
publicIP = ipaddress.ip_address(igd.externalipaddress())
assert publicIP.is_global
print(f'Public IP: {publicIP}')
# Dump port mappings
i, mapping = 0, igd.getgenericportmapping(0)
while mapping:
print(f'Port mapping {i}: {mapping}')
i += 1
mapping = igd.getgenericportmapping(i)
# Create/update port mapping
externalPort, internalPort, protocol = 1234, 4321, 'TCP'
assert igd.addportmapping(externalPort, protocol, localIP.compressed,
internalPort, 'test mapping', '')
print((f'Added port mapping: {protocol} {publicIP}:{externalPort}'
f' --> {localIP.compressed}:{internalPort}'))
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
Sample output:
Local IP: 192.168.1.123
Public IP: 203.0.113.1
Port mapping 0: (8908, 'TCP', ('192.168.1.123', 9100), 'tcp_map', '1', '', 0)
Port mapping 1: (22222, 'UDP', ('192.168.1.123', 22222), 'udp_map', '1', '', 0)
Added port mapping: TCP 203.0.113.1:1234 -> 192.168.1.123:4321

How to set up a local DNS server in python

I am using windows 7 and python 2.7 I want to map 172.16.45.84 IP address to myapp.nobies.in without mapping in hosts file.
I have the required certificate to this hostname. I don't want to map in hosts file as it requires administrative privileges.
So, how to create a DNS python server for it, which can be shipped with my app.
see this post How can I do DNS lookups in Python, including referring to /etc/hosts? for how to do dns lookups in python.
You could do something like:
import socket
name = raw_input("hostname:")
if name == "mpapp.nobies.in":
print "172.16.45.84"
else:
print socket.gethostbyname(name)
This will perform normal DNS lookups unless you lookup "myapp.nobies.in" which will return 172.16.45.84
Note: this is not a functioning DNS server application. It is however a (very basic) nslookup-like command alternative. To make this an actual server you need to listen for DNS packets on port 53 (which will require admin rights as it's a privileged port. I guess you could use a higher one but you'd have to configure that on your DNS client too). Investigate socket server programming in python. Good reading here from the python docs:
https://docs.python.org/2/howto/sockets.html
and here:
https://docs.python.org/2/library/socket.html
I'd also suggest looking up dnslib and/or dnspython for parsing DNS packets
EDIT:
try this code as to get you going: (start in a command prompt and minimize)
#!/usr/bin/python
import socket
def resolve(name):
if name == "mpapp.nobies.in":
return "172.16.45.84"
else :
# you ought to add some basic checking of name here
return socket.gethostbyname(name)
host = ''
port = 50000
backlog = 5
size = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host,port))
s.listen(backlog)
while 1:
client, address = s.accept()
data = client.recv(size)
if data:
bits = data.split(":")
if bits[0] == 'h':
client.send(resolve(bits[1]))
client.close()
and use this as a client: (customize variables and run after you've started the server)
#!/usr/bin/python
import socket
### configure me ###
dns_server_ip = '127.0.0.1'
dns_server_port = 50000
query = 'mpapp.nobies.in' # change this to the hostname you want to lookup
### configure me ###
size = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((dns_server_ip,dns_server_port))
s.send('h:' + query)
data = s.recv(size)
s.close()
print data
Note: this is not really a dns server, it doesn't not understand dns packets it just takes a hostname string prefixed with 'h:' on port 50000 and returns an ip address. I hope this meets your needs.
Usage:
$ START "" .\dns-server.py
$ .\dns-client.py
172.16.45.84

Can't assign requested address : Python Multicasting

I just started with networking and am writing a very simple code for multicasting. I am still not sure about the different interfaces. Some examples used "0.0.0.0" while others have used "127.0.0.1".
Code for Server
import socket
import sys
import time
ANY = socket.gethostbyname('localhost')
S_PORT = 1501
M_ADDR = "224.168.2.9"
M_PORT = 1600
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEPORT,1)
sock.bind((ANY,S_PORT))
sock.setsockopt(socket.IPPROTO_IP,socket.IP_MULTICAST_TTL,255)
while 1:
message = raw_input("Enter message: ")
sock.sendto(message,(M_ADDR,M_PORT))
if message == "exit":
break
sock.close()
Code for Client
import socket
import time
import sys
ANY = socket.gethostbyname('localhost')
M_ADDR = "224.168.2.9"
M_PORT = 1600
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEPORT,1)
sock.bind((ANY,M_PORT))
sock.setsockopt(socket.IPPROTO_IP,socket.IP_MULTICAST_TTL,255)
status = sock.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,socket.inet_aton(M_ADDR) + socket.inet_aton(ANY))
while 1:
data,addr = sock.recvfrom(1024)
print "Received message from " + str(addr) + " : " + data
if data == "exit":
break
sock.close()
The Client code runs properly and is waiting to receive message on the socket. But the Code Server crashes as soon as I enter any message.
Traceback (most recent call last):
File "multicast_server.py", line 17, in <module>
sock.sendto(message,(M_ADDR,M_PORT))
socket.error: [Errno 49] Can't assign requested address
What is causing this issue ?
The above code works if I use ANY = "0.0.0.0". Why is that ? What changes ?
In IPv4, 0.0.0.0 is a special address, aka INADDR_ANY, that means "bind every possible address on every interface".
So, the multicast network at 224.168.2.9, if it's reachable at all, will certainly be reachable from a socket bound to 0.0.0.0.
Meanwhile, 127.0.0.1 is a special address, aka INADDR_LOOPBACK, that means "bind localhost only on the loopback device". There's no way to reach anything but the local host itself on that socket. In particular, you can't reach your multicast network. Whether you get an ENETUNREACH, ENETDOWN, or EADDRNOTAVAIL is platform-specific, but whether it works is not—it can't possibly work.
If you want to test multicasting without testing across multiple computers, you will need to set up a loopback network with more than one address, so you can bind the client, the server, and the multicast group all to different addresses within that network.
When you use "0.0.0.0" or "" for networking in python, it opens up to any IP inbound. For your case, I would use "0.0.0.0" or "127.0.0.1" (if you are not comfortable opening up to the world.)
get your device ip address
ubuntu: ifcongfig
choose the ip address from any ethernet ,loop, wlan
replace M_ADDR with that ip address

Python - Get localhost IP [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Finding local IP addresses using Python's stdlib
To get my localhost IP address I do socket.gethostbyname(socket.gethostname()). But it gives me the answer 127.0.0.1.
If I do an_existing_socket.getsockname()[0] I get the answer 0.0.0.0.
I need my 'real' ip address (for instance 192.168.x.x) to modify a configuration file. How could I get it?
I generally use this code:
import os
import socket
if os.name != "nt":
import fcntl
import struct
def get_interface_ip(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s',
ifname[:15]))[20:24])
def get_lan_ip():
ip = socket.gethostbyname(socket.gethostname())
if ip.startswith("127.") and os.name != "nt":
interfaces = [
"eth0",
"eth1",
"eth2",
"wlan0",
"wlan1",
"wifi0",
"ath0",
"ath1",
"ppp0",
]
for ifname in interfaces:
try:
ip = get_interface_ip(ifname)
break
except IOError:
pass
return ip
I don't know it's origin, but it works on Linux/Windows.
Edit:
This code is used by smerlin in this stackoverflow question.
There is a nifty module you can use. Its called netifaces. Just do a pip install netifaces into a virtualenv for testing and try the following code:
import netifaces
interfaces = netifaces.interfaces()
for i in interfaces:
if i == 'lo':
continue
iface = netifaces.ifaddresses(i).get(netifaces.AF_INET)
if iface != None:
for j in iface:
print j['addr']
It all depends on your environment. If you only have one interface with one IP address attached to it, you can simply do:
netifaces.ifaddresses('eth0')[netifaces.AF_INET][0]['addr']
If you are behind a NAT and want to know your public IP address, you can use something like:
import urllib2
ret = urllib2.urlopen('https://icanhazip.com/')
print ret.read()
Hope this helps.

Choosing multicast network interface in Python

I have a server with two separate Ethernet connections. When I bind a socket in python it defaults to one of the two networks. How do I pull a multicast stream from the second network in Python? I have tried calling bind using the server's IP address on the second network, but that hasn't worked.
I recommend you don't use INADDR_ANY. In production multicast environments you want to be very specific with your multicast sockets and don't want to be doing things like sending igmp joins out all interfaces. This leads to hack-job workarounds when things aren't working like "route add -host 239.1.1.1 dev eth3" to get multicast joins going correctly depending on the system in question. Use this instead:
def joinMcast(mcast_addr,port,if_ip):
"""
Returns a live multicast socket
mcast_addr is a dotted string format of the multicast group
port is an integer of the UDP port you want to receive
if_ip is a dotted string format of the interface you will use
"""
#create a UDP socket
mcastsock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#allow other sockets to bind this port too
mcastsock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
#explicitly join the multicast group on the interface specified
mcastsock.setsockopt(socket.SOL_IP,socket.IP_ADD_MEMBERSHIP,
socket.inet_aton(mcast_addr)+socket.inet_aton(if_ip))
#finally bind the socket to start getting data into your socket
mcastsock.bind((mcast_addr,port))
return mcastsock
In the mcastsock.bind you can also use '' instead of the address string, but I advise against this. With '', if you have another socket using the same port, both sockets will get eachothers data.
When binding your socket, try the values mentioned here:
For IPv4 addresses, two special forms
are accepted instead of a host
address: the empty string represents
INADDR_ANY, and the string
'' represents
INADDR_BROADCAST.
INADDR_ANY is also known as the wildcard address:
Sockets with wildcarded local addresses may receive messages directed to the specified port number and addressed to any of the possible addresses assigned to a host`
More here.
I figured it out. It turns out that the piece I was missing was adding the interface to the mreq structure that is used in adding membership to a multicast group.
For IPv4, the index of the network interface is the IP address; for IPv6 the index of the network interface is returned by the method socket.getaddrinfo.
The code below shows how to listen to multicast on all network interfaces:
from socket import AF_INET6, AF_INET
import socket
import struct
# Bugfix for Python 3.6 for Windows ... missing IPPROTO_IPV6 constant
if not hasattr(socket, 'IPPROTO_IPV6'):
socket.IPPROTO_IPV6 = 41
multicast_address = {
AF_INET: ["224.0.1.187"],
AF_INET6: ["FF00::FD"]
}
multicast_port = 5683
addr_info = socket.getaddrinfo('', None) # get all ip
for addr in addr_info:
family = addr[0]
local_address = addr[4][0]
sock = socket.socket(family, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((local_address, multicast_port))
if family == AF_INET:
for multicast_group in multicast_address[family]:
sock.setsockopt(
socket.IPPROTO_IP,
socket.IP_ADD_MEMBERSHIP,
socket.inet_aton(multicast_group) + socket.inet_aton(local_address)
)
elif family == AF_INET6:
for multicast_group in multicast_address[family]:
ipv6mr_interface = struct.pack('i', addr[4][3])
ipv6_mreq = socket.inet_pton(socket.AF_INET6, multicast_group) + ipv6mr_interface
sock.setsockopt(
socket.IPPROTO_IPV6,
socket.IPV6_JOIN_GROUP,
ipv6_mreq
)
# _transport, _protocol = await loop.create_datagram_endpoint(
# lambda: protocol_factory(), sock=sock)

Categories

Resources