si_netstat.py_SAV_formatted 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. #!/usr/bin/python2
  2. # (c) 2016 by Siegrist(SystemLoesungen) <PSS@ZweierNet.ch>
  3. #
  4. # This program is based on a python netstat script that was written by da667 available on https://github.com/da667/netstat
  5. # who had it adapted from Ricardo Pascal, available on http://voorloopnul.com/blog/a-python-netstat-in-less-than-100-lines-of-code.
  6. #
  7. # This version of program has some improvements make it an acceptable alternative to the original netstat command.
  8. # So you can explore IPv4 in IPv6 listening sockets and much more verfied information then the original netstat.
  9. #
  10. # This program is free software. Use it as you like.
  11. #
  12. import pwd
  13. import os
  14. import re
  15. import glob
  16. import socket
  17. import sys
  18. import string
  19. import fcntl
  20. import struct
  21. import argparse
  22. VERSION = '0.95'
  23. PROC_TCP4 = "/proc/net/tcp"
  24. PROC_UDP4 = "/proc/net/udp"
  25. PROC_TCP6 = "/proc/net/tcp6"
  26. PROC_UDP6 = "/proc/net/udp6"
  27. PROC_PACKET = "/proc/net/packet"
  28. MAX_IPV4_ADDRESS = 0xffffffff
  29. MAX_IPV6_ADDRESS = 0xffffffffffffffffffffffffffffffff
  30. TCP_STATE = {
  31. '01':'ESTABLISHED',
  32. '02':'SYN_SENT',
  33. '03':'SYN_RECV',
  34. '04':'FIN_WAIT1',
  35. '05':'FIN_WAIT2',
  36. '06':'TIME_WAIT',
  37. '07':'CLOSE',
  38. '08':'CLOSE_WAIT',
  39. '09':'LAST_ACK',
  40. '0A':'LISTEN',
  41. '0B':'CLOSING'
  42. }
  43. #MYIPV4 = os.popen('ip addr show eth0 | grep "\<inet\>" | awk \'{ print $2 }\' | awk -F "/" \'{ print $1 }\'').read().strip()
  44. #MYIPV6 = os.popen('ip addr show eth0 | grep "\<inet\>" | awk \'{ print $2 }\' | awk -F "/" \'{ print $1 }\'').read().strip()
  45. #MYIFS = os.popen("ifconfig | grep '^[a-z0-9]' | awk '{print $1}'").read().strip()
  46. v4ports = []
  47. opt_l = True
  48. def xx():
  49. for a in MYIFS.split():
  50. print 'IFF:', a
  51. print 'v6:', os.popen('ip addr show ' + a + ' | grep "\<inet6\>" | awk \'{ print $2 }\' | awk -F "/" \'{ print $1 }\'').read().strip()
  52. def grep_b(list, search):
  53. return [True for i in list if search in i]
  54. def get_ip_address(ifname):
  55. s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  56. return socket.inet_ntoa(fcntl.ioctl(
  57. s.fileno(),
  58. 0x8915, # SIOCGIFADDR
  59. struct.pack('256s', ifname[:15])
  60. )[20:24])
  61. def _tcp4load():
  62. ''' Read the table of tcp connections & remove the header '''
  63. with open(PROC_TCP4,'r') as f:
  64. content = f.readlines()
  65. content.pop(0)
  66. return content
  67. def _tcp6load():
  68. ''' Read the table of tcpv6 connections & remove the header'''
  69. with open(PROC_TCP6,'r') as f:
  70. content = f.readlines()
  71. content.pop(0)
  72. return content
  73. def _udp4load():
  74. '''Read the table of udp connections & remove the header '''
  75. with open(PROC_UDP4,'r') as f:
  76. content = f.readlines()
  77. content.pop(0)
  78. return content
  79. def _udp6load():
  80. '''Read the table of udp connections & remove the header '''
  81. with open(PROC_UDP6,'r') as f:
  82. content = f.readlines()
  83. content.pop(0)
  84. return content
  85. def _hex2dec(s):
  86. return str(int(s,16))
  87. def _ip(s):
  88. ip = [(_hex2dec(s[6:8])),(_hex2dec(s[4:6])),(_hex2dec(s[2:4])),(_hex2dec(s[0:2]))]
  89. return '.'.join(ip)
  90. def _ip6(s):
  91. ip = [s[6:8],s[4:6],s[2:4],s[0:2],s[12:14],s[14:16],s[10:12],s[8:10],s[22:24],s[20:22],s[18:20],s[16:18],s[30:32],s[28:30],s[26:28],s[24:26]]
  92. #print '66666:', ':'.join(ip), s
  93. return ':'.join(ip)
  94. def _ip6q(s):
  95. ip = [s[6:8]+s[4:6],s[2:4]+s[0:2],s[12:14]+s[14:16],s[10:12]+s[8:10],s[22:24]+s[20:22],s[18:20]+s[16:18],s[30:32]+s[28:30],s[26:28]+s[24:26]]
  96. #print '666qqq:', ':'.join(ip), s
  97. return ':'.join(ip)
  98. def _conv_v6(s):
  99. return s
  100. def _remove_empty(array):
  101. return [x for x in array if x !='']
  102. def _convert_ipv4_port(array):
  103. host,port = array.split(':')
  104. return _ip(host),_hex2dec(port)
  105. def _convert_ipv6_port(array):
  106. host,port = array.split(':')
  107. return _ip6(host),_hex2dec(port)
  108. def _convert_ipv6(array):
  109. host,port = array.split(':')
  110. return _ip6q(host)
  111. def _addr_normal(s):
  112. return ':'.join(["%x" % x for x in [int(x, 16) for x in s.split(':')]])
  113. def _countFollowingZeros(l):
  114. """Return number of elements containing 0 at the beginning of the list."""
  115. #print 'aaa:', l
  116. if len(l) == 0:
  117. return 0
  118. elif l[0] != 0:
  119. return 0
  120. else:
  121. return 1 + _countFollowingZeros(l[1:])
  122. def _compress_v6(addr):
  123. hextets = [int(x, 16) for x in addr.split(':')]
  124. #print hextets
  125. followingzeros = [0] * 8
  126. for i in xrange(len(hextets)):
  127. followingzeros[i] = _countFollowingZeros(hextets[i:])
  128. # compressionpos is the position where we can start removing zeros
  129. compressionpos = followingzeros.index(max(followingzeros))
  130. if max(followingzeros) > 1:
  131. # genererate string with the longest number of zeros cut out
  132. # now we need hextets as strings
  133. hextets = [x for x in _addr_normal(addr).split(':')]
  134. while compressionpos < len(hextets) and hextets[compressionpos] == '0':
  135. del(hextets[compressionpos])
  136. hextets.insert(compressionpos, '')
  137. if compressionpos + 1 >= len(hextets):
  138. hextets.append('')
  139. if compressionpos == 0:
  140. hextets = [''] + hextets
  141. return ':'.join(hextets)
  142. else:
  143. return _addr_normal(addr)
  144. def _resolve_ip(host):
  145. """
  146. resolve ip und update dictionary res_cache {'ip': 'name'}.
  147. If resolution for a ip failed, 'name' is n_try ... 0.
  148. """
  149. try:
  150. hname = socket.gethostbyaddr(host)[0]
  151. return str(hname)
  152. except:
  153. return str(host)
  154. def netstat_tcp4():
  155. '''
  156. Function to return a list with status of tcp4 connections on Linux systems.
  157. '''
  158. tcpcontent =_tcp4load()
  159. tcpresult = []
  160. for line in tcpcontent:
  161. line_array = _remove_empty(line.split(' ')) # Split lines and remove empty spaces.
  162. l_host,l_port = _convert_ipv4_port(line_array[1]) # Convert ipaddress and port from hex to decimal.
  163. r_host,r_port = _convert_ipv4_port(line_array[2])
  164. tcp_id = line_array[0]
  165. state = TCP_STATE[line_array[3]]
  166. if state != 'LISTEN' and o_listen == True:
  167. continue
  168. if ( state == 'LISTEN' or state == 'SYN_SENT' or state == 'SYN_RECV' ) and o_estab == True:
  169. continue
  170. try:
  171. uid = pwd.getpwuid(int(line_array[7]))[0] # Get user from UID.
  172. except:
  173. uid = line_array[7]
  174. inode = line_array[9] # Need the inode to get process pid.
  175. if int(inode) > 0:
  176. pid = _get_pid_of_inode(inode)
  177. try: # try read the process name.
  178. exe = os.readlink('/proc/'+pid+'/exe').split('/')[-1]
  179. except:
  180. exe = '-'
  181. else:
  182. pid = '-'
  183. exe = '-'
  184. if o_numeric == False:
  185. r_host = _resolve_ip(r_host)
  186. nline = '%-7s %-32s %-32s %-11s %-8s %-6s %-s' % ('TCP4', l_host+':'+'\033[1m'+l_port+'\033[0m', r_host+':'+'\033[1m'+r_port+'\033[0m', state, uid, pid, exe)
  187. tcpresult.append(nline)
  188. # update v4inv6check list
  189. v4ports.append(l_port)
  190. return tcpresult
  191. def netstat_tcp6():
  192. '''
  193. This function returns a list of tcp connections utilizing ipv6.
  194. '''
  195. tcpcontent = _tcp6load()
  196. tcpresult = []
  197. for line in tcpcontent:
  198. line_array = _remove_empty(line.split(' '))
  199. l_host,l_port = _convert_ipv6_port(line_array[1])
  200. r_host,r_port = _convert_ipv6_port(line_array[2])
  201. tcp_id = line_array[0]
  202. state = TCP_STATE[line_array[3]]
  203. if state != 'LISTEN' and o_listen == True:
  204. continue
  205. if ( state == 'LISTEN' or state == 'SYN_SENT' or state == 'SYN_RECV' ) and o_estab == True:
  206. continue
  207. try:
  208. uid = pwd.getpwuid(int(line_array[7]))[0] # Get user from UID.
  209. except:
  210. uid = line_array[7]
  211. inode = line_array[9]
  212. if int(inode) > 0:
  213. pid = _get_pid_of_inode(inode)
  214. try: # try read the process name.
  215. exe = os.readlink('/proc/'+pid+'/exe').split('/')[-1]
  216. except:
  217. exe = '-'
  218. else:
  219. pid = '-'
  220. exe = '-'
  221. nline = '%-7s %-32s %-32s %-11s %-8s %-6s %-s' % ('TCP6', _compress_v6(_convert_ipv6(line_array[1]))+':'+'\033[1m'+l_port+'\033[0m', _compress_v6(_convert_ipv6(line_array[2]))+':'+'\033[1m'+r_port+'\033[0m', state, uid, pid, exe)
  222. tcpresult.append(nline)
  223. return tcpresult
  224. def netstat_tcp4in6():
  225. '''
  226. Returns a list of tcp ipv4 in ipv6 listen sockets.
  227. '''
  228. #print xx()
  229. tcpcontent = _tcp6load()
  230. tcpresult = []
  231. for line in tcpcontent:
  232. line_array = _remove_empty(line.split(' '))
  233. #if TCP_STATE[line_array[3]] != 'LISTEN':
  234. # continue
  235. l_host,l_port = _convert_ipv6_port(line_array[1])
  236. r_host,r_port = _convert_ipv6_port(line_array[2])
  237. if grep_b(v4ports,l_port):
  238. continue
  239. tcp_id = line_array[0]
  240. state = TCP_STATE[line_array[3]]
  241. if state != 'LISTEN' and o_listen == True:
  242. continue
  243. if ( state == 'LISTEN' or state == 'SYN_SENT' or state == 'SYN_RECV' ) and o_estab == True:
  244. continue
  245. try:
  246. uid = pwd.getpwuid(int(line_array[7]))[0] # Get user from UID.
  247. except:
  248. uid = line_array[7]
  249. inode = line_array[9]
  250. if int(inode) > 0:
  251. pid = _get_pid_of_inode(inode)
  252. try: # try read the process name.
  253. exe = os.readlink('/proc/'+pid+'/exe').split('/')[-1]
  254. except:
  255. exe = '-'
  256. else:
  257. pid = '-'
  258. exe = '-'
  259. if l_host == '00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:01':
  260. if _check_v4inv6_port("127.0.0.1",l_port):
  261. nline = '%-7s %-32s %-32s %-11s %-8s %-6s %-s' % ('TCP4in6', '127.0.0.1:'+'\033[1m'+l_port+'\033[0m', _compress_v6(_convert_ipv6(line_array[2]))+':'+'\033[1m'+r_port+'\033[0m', state, uid, pid, exe)
  262. tcpresult.append(nline)
  263. if l_host == "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00":
  264. if _check_v4inv6_port("0.0.0.0",l_port):
  265. nline = '%-7s %-32s %-32s %-11s %-8s %-6s %-s' % ('TCP4in6', '0.0.0.0:'+'\033[1m'+l_port+'\033[0m', _compress_v6(_convert_ipv6(line_array[2]))+':'+'\033[1m'+r_port+'\033[0m', state, uid, pid, exe)
  266. tcpresult.append(nline)
  267. #else:
  268. # for a in MYIFS.split():
  269. # _check_v4inv6_port(get_ip_address(a),l_port)
  270. return tcpresult
  271. def netstat_udp4():
  272. '''
  273. Function to return a list with status of udp connections.
  274. '''
  275. udpcontent =_udp4load()
  276. udpresult = []
  277. for line in udpcontent:
  278. line_array = _remove_empty(line.split(' '))
  279. l_host,l_port = _convert_ipv4_port(line_array[1])
  280. r_host,r_port = _convert_ipv4_port(line_array[2])
  281. udp_id = line_array[0]
  282. udp_state = TCP_STATE[line_array[3]]
  283. if ( udp_state != 'ESTABLISHED' and o_estab == True ) or o_estab == False:
  284. continue
  285. if udp_state != 'ESTABLISHED':
  286. udp_state =' ' #UDP is stateless
  287. try:
  288. uid = pwd.getpwuid(int(line_array[7]))[0] # Get user from UID.
  289. except:
  290. uid = line_array[7]
  291. inode = line_array[9]
  292. if int(inode) > 0:
  293. pid = _get_pid_of_inode(inode)
  294. try: # try read the process name.
  295. exe = os.readlink('/proc/'+pid+'/exe').split('/')[-1]
  296. except:
  297. exe = '-'
  298. else:
  299. pid = '-'
  300. exe = '-'
  301. nline = '%-7s %-32s %-32s %-11s %-8s %-6s %-s' % ('UDP4', l_host+':'+'\033[1m'+l_port+'\033[0m', r_host+':'+'\033[1m'+r_port+'\033[0m', udp_state, uid, pid, exe)
  302. udpresult.append(nline)
  303. return udpresult
  304. def netstat_udp6():
  305. '''
  306. Function to return a list of udp connection utilizing ipv6
  307. '''
  308. udpcontent =_udp6load()
  309. udpresult = []
  310. for line in udpcontent:
  311. line_array = _remove_empty(line.split(' '))
  312. l_host,l_port = _convert_ipv6_port(line_array[1])
  313. r_host,r_port = _convert_ipv6_port(line_array[2])
  314. udp_id = line_array[0]
  315. udp_state = TCP_STATE[line_array[3]]
  316. if ( udp_state != 'ESTABLISHED' and o_estab == True ) or o_estab == False:
  317. continue
  318. if udp_state != 'ESTABLISHED':
  319. udp_state =' ' #UDP is stateless
  320. try:
  321. uid = pwd.getpwuid(int(line_array[7]))[0] # Get user from UID.
  322. except:
  323. uid = line_array[7]
  324. inode = line_array[9]
  325. if int(inode) > 0:
  326. pid = _get_pid_of_inode(inode)
  327. try: # try read the process name.
  328. exe = os.readlink('/proc/'+pid+'/exe').split('/')[-1]
  329. except:
  330. exe = '-'
  331. else:
  332. pid = '-'
  333. exe = '-'
  334. nline = '%-7s %-32s %-32s %-11s %-8s %-6s %-s' % ('UDP6', _compress_v6(_convert_ipv6(line_array[1]))+':'+'\033[1m'+l_port+'\033[0m', _compress_v6(_convert_ipv6(line_array[2]))+':'+'\033[1m'+r_port+'\033[0m', udp_state, uid, pid, exe)
  335. udpresult.append(nline)
  336. return udpresult
  337. def _get_pid_of_inode(inode):
  338. '''
  339. To retrieve the process pid, check every running process and look for one using
  340. the given inode.
  341. '''
  342. for item in glob.glob('/proc/[0-9]*/fd/[0-9]*'):
  343. try:
  344. if re.search(inode,os.readlink(item)):
  345. return item.split('/')[2]
  346. except:
  347. pass
  348. return None
  349. def check_root():
  350. if os.getuid() == 0:
  351. return True
  352. else:
  353. return False
  354. def _check_v4inv6_port(addr,portnr):
  355. '''
  356. check if a v4 port is listening over ip6. Strange, we do a SYN connect for every port not listening v4.
  357. thats because I think there is no image in the /proc filesystem
  358. '''
  359. #print 'aaacc:', addr, portnr
  360. is_onnected = False
  361. try:
  362. try:
  363. t_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  364. except:
  365. print("Error: Can't open socket!\n")
  366. return False
  367. t_socket.connect((addr, int(portnr)))
  368. is_onnected = True
  369. except:
  370. is_onnected = False
  371. finally:
  372. if(is_onnected and portnr != t_socket.getsockname()[1]):
  373. #print("{}:{} Open \n".format(addr, portnr))
  374. t_socket.close()
  375. return True
  376. t_socket.close()
  377. return False
  378. if __name__ == '__main__':
  379. if not check_root():
  380. print("This program needs root privileges !\nThats because of reading the /proc filesystem and using functions like getpwuid().\nSo I give up\n")
  381. sys.exit()
  382. #print
  383. # commandline params
  384. o_numeric = True
  385. o_listen = False
  386. o_estab = None
  387. o_udp = True
  388. o_tcp = True
  389. o_v6 = True
  390. o_v4 = True
  391. parser = argparse.ArgumentParser(description='netstat utility V'+VERSION+"\n2017 by sigi <https://wiki.zweiernet.ch/wiki/Informatik>",
  392. formatter_class=argparse.RawDescriptionHelpFormatter )
  393. parser.add_argument('-l', help="Only listening sockets", action="store_true")
  394. parser.add_argument('-e', help="Only established sockets", action="store_true")
  395. parser.add_argument('-r', help="Resolve IP-Addresses", action="store_true")
  396. parser.add_argument('-t', help="Only TCP", action="store_true")
  397. parser.add_argument('-u', help="Only UDP", action="store_true")
  398. parser.add_argument('-4', dest='v4', help="Only IPv4", action="store_true")
  399. parser.add_argument('-6', dest='v6', help="Only IPv6", action="store_true")
  400. args = parser.parse_args()
  401. if args.r:
  402. o_numeric = False
  403. if args.l:
  404. o_listen = True
  405. if args.e:
  406. o_estab = True
  407. o_listen = False
  408. if args.t:
  409. o_udp = False
  410. if args.u:
  411. o_tcp = False
  412. if args.v4:
  413. o_v6 = False
  414. if args.v6:
  415. o_v4 = False
  416. # Output
  417. print '%-7s %-24s %-24s %-11s %-8s %-6s %-s' % ('Proto', 'Local Address', 'Remote Address', 'State', 'UID', 'PID', 'Program')
  418. print '%-7s %-24s %-24s %-11s %-8s %-6s %-s' % ('-----', '-------------', '--------------', '-----', '---', '---', '-------')
  419. #print "\nTCP (v4) Results:\n"
  420. if o_v4 == True and o_tcp == True:
  421. for conn_tcp in netstat_tcp4():
  422. print conn_tcp
  423. #print "\nTCP (v4inv6) Results:\n"
  424. if o_v4 == True and o_tcp == True:
  425. for conn_tcp46 in netstat_tcp4in6():
  426. print conn_tcp46
  427. #print "\nTCP (v6) Results:\n"
  428. if o_v6 == True and o_tcp == True:
  429. for conn_tcp6 in netstat_tcp6():
  430. print conn_tcp6
  431. #print "\nUDP (v4) Results:\n"
  432. if o_v4 == True and o_udp == True and not args.l:
  433. for conn_udp in netstat_udp4():
  434. print conn_udp
  435. #print "\nUDP (v6) Results:\n"
  436. if o_v6 == True and o_udp == True and not args.l:
  437. for conn_udp6 in netstat_udp6():
  438. print conn_udp6