Identifying listening ports using Python
In translating some scripts from bash, I am encountering many uses of netstat -an to find if one of our services is listening. While I know I can just use subprocess.call or other even popen I would rather use a pythonic solution so I am not leveraging the unix environment we are operating in.
From what I have read the socket module should have something but I haven't seen anything that checks for lis开发者_JAVA百科tening ports. It could be me not understanding a simple trick, but so far I know how to connect to a socket, and write something that lets me know when that connection failed. But not necessarily have I found something that specifically checks the port to see if its listening.
Any ideas?
How about trying to connect...
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
result = s.connect_ex(('127.0.0.1', 3306))
if result == 0:
print('socket is open')
s.close()
I know this question is old, but i write this for begginners. If you want identifying listening ports on your system, you can use the code below.
from socket import *
Port = 0 #First port.
while Port <= 65535: #Port 65535 is last port you can access.
try:
try:
Socket = socket(AF_INET, SOCK_STREAM, 0) #Create a socket.
except:
print("Error: Can't open socket!\n")
break #If can't open socket, exit the loop.
Socket.connect(("127.0.0.1", Port)) #Try connect the port. If port is not listening, throws ConnectionRefusedError.
Connected = True
except ConnectionRefusedError:
Connected = False
finally:
if(Connected and Port != Socket.getsockname()[1]): #If connected,
print("{}:{} Open \n".format("127.0.0.1", Port)) #print port.
Port = Port + 1 #Increase port.
Socket.close() #Close socket.
On Linux we can use strace to see that netstat -ln is reading and parsing various values from the /proc filesystem.
$ strace netstat -ln 2>&1| grep '/proc'
open("/proc/net/tcp", O_RDONLY) = 3
open("/proc/net/tcp6", O_RDONLY) = 3
open("/proc/net/udp", O_RDONLY) = 3
open("/proc/net/udp6", O_RDONLY) = 3
open("/proc/net/raw", O_RDONLY) = 3
open("/proc/net/raw6", O_RDONLY) = 3
open("/proc/net/unix", O_RDONLY) = 3
open("/proc/net/ipx/socket", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/proc/net/ipx", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/proc/net/ax25", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/proc/net/x25", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/proc/net/x25", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/proc/net/nr", O_RDONLY) = -1 ENOENT (No such file or directory)
So you can just read those files from Python and extract the data you need.
$ cat /proc/net/tcp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 00000000:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 8190 1 00000000 300 0 0 2 -1
1: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 6458 1 00000000 300 0 0 2 -1
2: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 10425 1 00000000 300 0 0 2 -1
3: 8D0BA8C0:8801 689255D1:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 1680975 1 00000000 24 4 16 6 -1
4: 8D0BA8C0:D142 97E67D4A:01BB 06 00000000:00000000 03:000012E8 00000000 0 0 0 3 00000000
5: 8D0BA8C0:D1A1 96E67D4A:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 1672130 1 00000000 24 4 18 5 -1
6: 8D0BA8C0:D148 97E67D4A:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 1679875 1 00000000 24 4 20 5 -1
The listening sockets will have remote address 00000000:0000
The address:port pairs are in hex. See: * How can i match each /proc/net/tcp entry to each opened socket?
You could cross reference with /proc//fd. For example, sshd is running on my laptop.
$ cat /var/run/sshd.pid
522
$ sudo ls -l /proc/522/fd
total 0
lrwx------ 1 root root 64 2011-09-15 21:32 0 -> /dev/null
lrwx------ 1 root root 64 2011-09-15 21:32 1 -> /dev/null
lrwx------ 1 root root 64 2011-09-15 21:32 2 -> /dev/null
lrwx------ 1 root root 64 2011-09-15 21:32 3 -> socket:[6456]
lrwx------ 1 root root 64 2011-09-15 21:32 4 -> socket:[6458]
Socket 6456 corresponds to inode 6458 listed in the second row of /proc/net/tcp.
So you can get all this information from proc, but you may end up reinventing netstat -lntp
import psutil
connections = psutil.net_connections()
Reference: https://stackoverflow.com/a/6244270
You could either try connecting to the port in question, or emulate netstat
.
Doing the latter will be OS-specific. On Linux you can examine /proc/net/tcp
. It looks like this:
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 00000000:C809 00000000:0000 0A 00000000:00000000 00:00000000 00000000 117 0 8381 1 ffff8802f22a8000 300 0 0 2 -1
1: 00000000:16CC 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1026 0 14336 1 ffff8802f2249440 300 0 0 2 -1
2: 00000000:006F 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 7876 1 ffff8802f2248000 300 0 0 2 -1
3: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 8163 1 ffff8802f3578000 300 0 0 2 -1
4: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 981582 1 ffff8800d7a53600 300 0 0 2 -1
5: 00000000:0019 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 9129 1 ffff8802edc886c0 300 0 0 2 -1
6: 00000000:021A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 9016 1 ffff8802edc88000 300 0 0 2 -1
7: 00000000:2B1C 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1026 0 783709 1 ffff8803119cca40 300 0 0 2 -1
8: 00000000:977C 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 24292 1 ffff8802f224e540 300 0 0 2 -1
You're looking for lines that have 0A
in the st
("status") column. The numbers after the colon in local_address
-- C809
, 16CC
etc -- are TCP port numbers (in hex) on which there are listening processes.
I know I am several years late, but non of the existing answers are good enough.
I was Google searching for a good, elegant solution for exactly the same problem and non of the answers already posted seemed good enough, instead I have found solutions of my own and I want to post them here to help future readers who get redirected here by Google.
Most operating systems have an executable named netstat
, that can be used to capture listening ports, in this example I am using Windows 10 and Python 3.9.6 x64, but this is written Python so you can easily adapt it for your own use case.
Using plain netstat
will be very slow, because of all the name-resolving, using netstat -n
will be exponentially faster because it doesn't waste time resolving the names.
In Python 3.9.6, use subproces.run()
to run os calls, and use capture_output=True
to capture stdout, then use .stdout
property of resultant process to get output, the result is in binary form, use .decode()
to get string.
Then the output should look like this:
Active Connections
Proto Local Address Foreign Address State
TCP 10.70.0.6:1134 40.83.240.146:443 ESTABLISHED
TCP 10.70.0.6:1283 117.18.232.200:443 CLOSE_WAIT
TCP 10.70.0.6:1609 198.252.206.25:443 ESTABLISHED
TCP 10.70.0.6:1621 198.252.206.25:443 ESTABLISHED
TCP 10.70.0.6:1691 74.125.24.102:443 ESTABLISHED
TCP 10.70.0.6:1727 142.251.10.94:443 ESTABLISHED
TCP 10.70.0.6:1728 142.251.10.100:443 TIME_WAIT
TCP 10.70.0.6:1731 172.217.194.119:443 TIME_WAIT
TCP 10.70.0.6:1735 74.125.24.113:443 ESTABLISHED
TCP 10.70.0.6:1787 104.244.42.130:443 ESTABLISHED
TCP 10.70.0.6:1796 151.101.1.69:443 ESTABLISHED
TCP 10.70.0.6:1797 151.101.196.193:443 ESTABLISHED
TCP 10.70.0.6:1799 74.125.130.132:443 ESTABLISHED
TCP 10.70.0.6:1800 198.252.206.25:443 ESTABLISHED
TCP 10.70.0.6:1805 3.209.45.230:443 TIME_WAIT
TCP 10.70.0.6:1806 3.219.6.82:443 TIME_WAIT
TCP 10.70.0.6:1807 3.211.239.214:443 TIME_WAIT
TCP 10.70.0.6:1816 140.82.113.26:443 ESTABLISHED
TCP 127.0.0.1:1053 127.0.0.1:1055 ESTABLISHED
TCP 127.0.0.1:1055 127.0.0.1:1053 ESTABLISHED
TCP 127.0.0.1:1057 127.0.0.1:1058 ESTABLISHED
TCP 127.0.0.1:1058 127.0.0.1:1057 ESTABLISHED
TCP 127.0.0.1:1061 127.0.0.1:1062 ESTABLISHED
TCP 127.0.0.1:1062 127.0.0.1:1061 ESTABLISHED
TCP 127.0.0.1:1763 127.0.0.1:1764 ESTABLISHED
TCP 127.0.0.1:1764 127.0.0.1:1763 ESTABLISHED
TCP 127.0.0.1:1766 127.0.0.1:1767 ESTABLISHED
TCP 127.0.0.1:1767 127.0.0.1:1766 ESTABLISHED
TCP 127.0.0.1:1810 127.0.0.1:2015 ESTABLISHED
TCP 127.0.0.1:1811 127.0.0.1:2015 ESTABLISHED
TCP 127.0.0.1:1820 127.0.0.1:1821 ESTABLISHED
TCP 127.0.0.1:1821 127.0.0.1:1820 ESTABLISHED
TCP 127.0.0.1:1829 127.0.0.1:9614 SYN_SENT
TCP 127.0.0.1:2015 127.0.0.1:1810 ESTABLISHED
TCP 127.0.0.1:2015 127.0.0.1:1811 ESTABLISHED
TCP 127.0.0.1:14845 127.0.0.1:14846 ESTABLISHED
TCP 127.0.0.1:14846 127.0.0.1:14845 ESTABLISHED
TCP 127.0.0.1:15004 127.0.0.1:15005 ESTABLISHED
TCP 127.0.0.1:15005 127.0.0.1:15004 ESTABLISHED
TCP 127.0.0.1:15013 127.0.0.1:15014 ESTABLISHED
TCP 127.0.0.1:15014 127.0.0.1:15013 ESTABLISHED
TCP 127.0.0.1:16976 127.0.0.1:16977 ESTABLISHED
TCP 127.0.0.1:16977 127.0.0.1:16976 ESTABLISHED
TCP 127.0.0.1:19278 127.0.0.1:19279 ESTABLISHED
TCP 127.0.0.1:19279 127.0.0.1:19278 ESTABLISHED
TCP 127.0.0.1:19280 127.0.0.1:19281 ESTABLISHED
TCP 127.0.0.1:19281 127.0.0.1:19280 ESTABLISHED
TCP 127.0.0.1:20695 127.0.0.1:21385 ESTABLISHED
TCP 127.0.0.1:21385 127.0.0.1:20695 ESTABLISHED
TCP 127.0.0.1:23460 127.0.0.1:23461 ESTABLISHED
TCP 127.0.0.1:23461 127.0.0.1:23460 ESTABLISHED
TCP 127.0.0.1:23462 127.0.0.1:23463 ESTABLISHED
TCP 127.0.0.1:23463 127.0.0.1:23462 ESTABLISHED
TCP 127.0.0.1:28343 127.0.0.1:28344 ESTABLISHED
TCP 127.0.0.1:28344 127.0.0.1:28343 ESTABLISHED
TCP 127.0.0.1:30307 127.0.0.1:30308 ESTABLISHED
TCP 127.0.0.1:30308 127.0.0.1:30307 ESTABLISHED
TCP 127.0.0.1:30311 127.0.0.1:30312 ESTABLISHED
TCP 127.0.0.1:30312 127.0.0.1:30311 ESTABLISHED
TCP 127.0.0.1:30313 127.0.0.1:30314 ESTABLISHED
TCP 127.0.0.1:30314 127.0.0.1:30313 ESTABLISHED
TCP 127.0.0.1:30316 127.0.0.1:30317 ESTABLISHED
TCP 127.0.0.1:30317 127.0.0.1:30316 ESTABLISHED
TCP 127.0.0.1:30319 127.0.0.1:30320 ESTABLISHED
TCP 127.0.0.1:30320 127.0.0.1:30319 ESTABLISHED
TCP 127.0.0.1:30584 127.0.0.1:30585 ESTABLISHED
TCP 127.0.0.1:30585 127.0.0.1:30584 ESTABLISHED
TCP 127.0.0.1:30623 127.0.0.1:30624 ESTABLISHED
TCP 127.0.0.1:30624 127.0.0.1:30623 ESTABLISHED
TCP 127.0.0.1:49669 127.0.0.1:49670 ESTABLISHED
TCP 127.0.0.1:49670 127.0.0.1:49669 ESTABLISHED
TCP 127.0.0.1:49690 127.0.0.1:49692 ESTABLISHED
TCP 127.0.0.1:49692 127.0.0.1:49690 ESTABLISHED
TCP [::1]:3306 [::1]:23468 ESTABLISHED
TCP [::1]:3306 [::1]:23469 ESTABLISHED
TCP [::1]:23468 [::1]:3306 ESTABLISHED
TCP [::1]:23469 [::1]:3306 ESTABLISHED
Use splitlines()
to get separate lines, then use list slicing to get the contents of the actual table, here we use index 4, then using regex splitting to split on whitespace characters, then use index to get the local address value, then finally use string split on colons and indexing to get the ports.
The code:
import psutil
import re
import subprocess
def get_active_ports():
process = subprocess.run(['netstat', '-n'], capture_output=True)
report = process.stdout.decode().splitlines()
ports = set()
for i in report[4:]:
ports.add(re.split(':(?!.*:)', re.split('\s+', i)[2])[1])
return ports
Or in one-liner:
set([re.split(':(?!.*:)', re.split('\s+', i)[2])[1] for i in subprocess.run(['netstat', '-n'], capture_output=True).stdout.decode().splitlines()[4:]])
Performance:
In [119]: %timeit set([re.split(':(?!.*:)', re.split('\s+', i)[2])[1] for i in subprocess.run(['netstat', '-n'], capture_output=True).stdout.decode().splitlines()[4:]])
11.4 ms ± 315 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Or, alternatively, psutil
has a .net_connections()
method, you can just get the ports from it.
Just use set comprehension to grab the output:
set(i.laddr.port for i in psutil.net_connections())
This approach is tremendously faster than the previous one:
In [103]: %timeit set(i.laddr.port for i in psutil.net_connections())
893 µs ± 13.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
精彩评论