You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

netutil.py 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. #!/usr/bin/env python3
  2. # Copyright (c) 2014-2016 The Bitcoin Core developers
  3. # Distributed under the MIT software license, see the accompanying
  4. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
  5. """Linux network utilities.
  6. Roughly based on http://voorloopnul.com/blog/a-python-netstat-in-less-than-100-lines-of-code/ by Ricardo Pascal
  7. """
  8. import sys
  9. import socket
  10. import fcntl
  11. import struct
  12. import array
  13. import os
  14. from binascii import unhexlify, hexlify
  15. STATE_ESTABLISHED = '01'
  16. STATE_SYN_SENT = '02'
  17. STATE_SYN_RECV = '03'
  18. STATE_FIN_WAIT1 = '04'
  19. STATE_FIN_WAIT2 = '05'
  20. STATE_TIME_WAIT = '06'
  21. STATE_CLOSE = '07'
  22. STATE_CLOSE_WAIT = '08'
  23. STATE_LAST_ACK = '09'
  24. STATE_LISTEN = '0A'
  25. STATE_CLOSING = '0B'
  26. def get_socket_inodes(pid):
  27. '''
  28. Get list of socket inodes for process pid.
  29. '''
  30. base = '/proc/%i/fd' % pid
  31. inodes = []
  32. for item in os.listdir(base):
  33. target = os.readlink(os.path.join(base, item))
  34. if target.startswith('socket:'):
  35. inodes.append(int(target[8:-1]))
  36. return inodes
  37. def _remove_empty(array):
  38. return [x for x in array if x !='']
  39. def _convert_ip_port(array):
  40. host,port = array.split(':')
  41. # convert host from mangled-per-four-bytes form as used by kernel
  42. host = unhexlify(host)
  43. host_out = ''
  44. for x in range(0, len(host) // 4):
  45. (val,) = struct.unpack('=I', host[x*4:(x+1)*4])
  46. host_out += '%08x' % val
  47. return host_out,int(port,16)
  48. def netstat(typ='tcp'):
  49. '''
  50. Function to return a list with status of tcp connections at linux systems
  51. To get pid of all network process running on system, you must run this script
  52. as superuser
  53. '''
  54. with open('/proc/net/'+typ,'r',encoding='utf8') as f:
  55. content = f.readlines()
  56. content.pop(0)
  57. result = []
  58. for line in content:
  59. line_array = _remove_empty(line.split(' ')) # Split lines and remove empty spaces.
  60. tcp_id = line_array[0]
  61. l_addr = _convert_ip_port(line_array[1])
  62. r_addr = _convert_ip_port(line_array[2])
  63. state = line_array[3]
  64. inode = int(line_array[9]) # Need the inode to match with process pid.
  65. nline = [tcp_id, l_addr, r_addr, state, inode]
  66. result.append(nline)
  67. return result
  68. def get_bind_addrs(pid):
  69. '''
  70. Get bind addresses as (host,port) tuples for process pid.
  71. '''
  72. inodes = get_socket_inodes(pid)
  73. bind_addrs = []
  74. for conn in netstat('tcp') + netstat('tcp6'):
  75. if conn[3] == STATE_LISTEN and conn[4] in inodes:
  76. bind_addrs.append(conn[1])
  77. return bind_addrs
  78. # from: http://code.activestate.com/recipes/439093/
  79. def all_interfaces():
  80. '''
  81. Return all interfaces that are up
  82. '''
  83. is_64bits = sys.maxsize > 2**32
  84. struct_size = 40 if is_64bits else 32
  85. s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  86. max_possible = 8 # initial value
  87. while True:
  88. bytes = max_possible * struct_size
  89. names = array.array('B', b'\0' * bytes)
  90. outbytes = struct.unpack('iL', fcntl.ioctl(
  91. s.fileno(),
  92. 0x8912, # SIOCGIFCONF
  93. struct.pack('iL', bytes, names.buffer_info()[0])
  94. ))[0]
  95. if outbytes == bytes:
  96. max_possible *= 2
  97. else:
  98. break
  99. namestr = names.tostring()
  100. return [(namestr[i:i+16].split(b'\0', 1)[0],
  101. socket.inet_ntoa(namestr[i+20:i+24]))
  102. for i in range(0, outbytes, struct_size)]
  103. def addr_to_hex(addr):
  104. '''
  105. Convert string IPv4 or IPv6 address to binary address as returned by
  106. get_bind_addrs.
  107. Very naive implementation that certainly doesn't work for all IPv6 variants.
  108. '''
  109. if '.' in addr: # IPv4
  110. addr = [int(x) for x in addr.split('.')]
  111. elif ':' in addr: # IPv6
  112. sub = [[], []] # prefix, suffix
  113. x = 0
  114. addr = addr.split(':')
  115. for i,comp in enumerate(addr):
  116. if comp == '':
  117. if i == 0 or i == (len(addr)-1): # skip empty component at beginning or end
  118. continue
  119. x += 1 # :: skips to suffix
  120. assert(x < 2)
  121. else: # two bytes per component
  122. val = int(comp, 16)
  123. sub[x].append(val >> 8)
  124. sub[x].append(val & 0xff)
  125. nullbytes = 16 - len(sub[0]) - len(sub[1])
  126. assert((x == 0 and nullbytes == 0) or (x == 1 and nullbytes > 0))
  127. addr = sub[0] + ([0] * nullbytes) + sub[1]
  128. else:
  129. raise ValueError('Could not parse address %s' % addr)
  130. return hexlify(bytearray(addr)).decode('ascii')
  131. def test_ipv6_local():
  132. '''
  133. Check for (local) IPv6 support.
  134. '''
  135. import socket
  136. # By using SOCK_DGRAM this will not actually make a connection, but it will
  137. # fail if there is no route to IPv6 localhost.
  138. have_ipv6 = True
  139. try:
  140. s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
  141. s.connect(('::1', 0))
  142. except socket.error:
  143. have_ipv6 = False
  144. return have_ipv6