【问题标题】:I need to find which ports are being used on a server's IP using python我需要使用python查找服务器IP上正在使用哪些端口
【发布时间】:2024-01-15 01:13:01
【问题描述】:

我有一个服务器在各种端口上托管应用程序,这些端口由服务器随机分配。我希望能够扫描服务器的 IP,并找到可以用来连接这些应用程序的端口。我必须扫描超过 50K(可以是从端口 10000 到 60000 的任何地方)端口,所以我一直在寻找一种有效的方法来做到这一点。我尝试了以下方法:

import string
import time
import socket
import threading

from telnetlib import Telnet
from datetime import datetime

import nmap

def main():
    """ Entry point. """
    #known used ports - ['43828','38238','56272']

    # Using nmap - this seems to be the only working code. 
    # But, with a timeout of 0.5 secs, this would take somewhere
    # near 25K secs. Way too long.
    # (Vast majority of ports will be closed, and timeout at a half second. 
    # There will only be a few dozen open ones.)

    t0 = time.time()
    print([testConn('10.159.122.232', x) for x in range(10000, 60000)])
    t1 = time.time()
    print (t1-t0)

    # I found this chunk of code somewhere, using the socket lib, 
    # and attempting multithreading. It never completes (after 30+ min.). 
    r = 10000
    for x in range(1,100):
        t = threading.Thread(target=portscan, kwargs={'host':'10.159.122.232', 'port':r})

        r += 1
        t.start()

    # I ripped this off of the python-nmap website, and it outputs a key error.

    nm = nmap.PortScanner()
    nm.scan('10.159.122.232', '38000-39000')
    hosts_list = [(x, nm[x]['status']['state']) for x in nm.all_hosts()]
    for host, status in hosts_list:
        print(host + ' ' + status)
    for port in nm['10.159.122.232']['tcp']:
        thisDict = nm['10.159.122.232']['tcp'][port]
        print ('Port ' + str(port) + ': ' + thisDict['product'] + ', v' + thisDict['version'])

    return 0


def testConn(host, port):
        """ Establish a Telnet connection and perform a login """
        theSession = Telnet()
        try:
            theSession.open(host, port,.1)
            return True
        except:
            return False

def portscan(host, port):

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(0.2)

    try:
        con = s.connect((host,port))

        print('Port :',port,"is open.")

        con.close()
    except: 
        pass

if __name__ == '__main__':
    sys.exit(main())

有什么帮助吗??我发现的大多数线程都试图从主机扫描主机上的开放端口。我想从客户端扫描正在使用其端口的主机。这有意义吗?

谢谢!!!

【问题讨论】:

    标签: python sockets networking port nmap


    【解决方案1】:

    您需要并行执行此操作才能在合理的时间内完成。这意味着,对于每个端口,创建一个套接字,将其置于非阻塞模式,然后调用connect。当您在非阻塞模式下调用connect 时,您将立即收到BlockingIOError 异常,但连接尝试正在进行中。

    一旦你启动了所有的connects,你就开始轮询文件描述符,等待连接是否成功。这里的难点实际上只是跟踪所有套接字的状态信息。

    一旦你找到一个开放的端口,你就可以继续test_conn尝试telnet登录。

    您需要处理的事情(这些是 linux/Unix 特有的,但在其他平台上可能存在类似问题):

    • 任何时候打开的文件描述符的数量都有限制。通常可以使用resource.setrlimit(RLIMIT_NOFILE, (soft_limit, hard_limit)) 提高该限制。

    • 默认情况下,由于默认的 SYN 重试次数(至少在 linux 上),每个连接请求将需要大约 2 分钟才能超时。这可能不是问题,因为所有连接请求都是并行发生的。但可以使用mysock.setsockopt(socket.IPPROTO_TCP, socket.TCP_SYNCNT, my_syn_retry_cnt) 进一步减少。

    • 您需要确定每次连接尝试何时完成。并行执行此操作的最佳方法是使用select 模块的epollpoll 对象。 (还有更多使用select.select 的示例,但select 对象[至少在Linux 上] 只能轮询最多1024 个文件描述符,这对您的场景来说是个问题,因为您将拥有大约50000 个。)

    • 每次连接尝试完成后,有些会成功,有些则不会。您可以使用mysock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) 来判断连接是否成功。但如果你使用poll/epoll,则会返回一个标志,告诉你是否有错误。

    在写这篇文章的时候,我开始玩并想出了这个最有效的快速技巧:

    #!/usr/bin/env python3
    import sys
    from select import epoll, EPOLLERR
    from resource import setrlimit, RLIMIT_NOFILE
    from socket import socket, IPPROTO_TCP, TCP_SYNCNT, gaierror
    
    def scan_ip(ip_addr):
        pollobj = epoll()
        fd_socks = [None] * 65540
        open_ports = [None] * 65536
        for n in range(65536):
            sock = socket()
            sock.setblocking(0)
            sock.setsockopt(IPPROTO_TCP, TCP_SYNCNT, 3)
            try:
                sock.connect((ip_addr, n))
                print("??? Port {} connected (immediately)".format(n))
                open_ports[n] = True
                sock.close()
            except BlockingIOError:
                # Store port and socket corresponding to the file descriptor
                fd_socks[sock.fileno()] = (n, sock)
                pollobj.register(sock.fileno())
            except gaierror:
                print("Bad host name")
                return None
            except Exception as exc:
                print("Unexpected failure - {}".format(exc))
                return None
    
        while any(fd_socks):
            ready_fds = pollobj.poll()
            for fd, event in ready_fds:
                port, sock = fd_socks[fd]
                open_ports[port] = not (event & EPOLLERR)
                pollobj.unregister(fd)
                sock.close()
                fd_socks[fd] = None
    
        return [n for n, is_open in enumerate(open_ports) if is_open]
    
    def main():
        if len(sys.argv) < 2:
            print("Usage: {} IP-address".format(sys.argv[0]), file=sys.stderr)
            sys.exit(1)
        # Raise number-of-open-files-limit
        setrlimit(RLIMIT_NOFILE, (65540, 65540))
        ip_addr = sys.argv[1]
        ports = scan_ip(ip_addr)
        if ports is not None:
            print("{}: {}".format(ip_addr, ", ".join(str(port) for port in ports)))
    
    if __name__ == '__main__':
        main()
    

    【讨论】: