si_netstat.py_SAV_08062017 19 KB

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