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.5KB

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