我需要使用python查找服务器IP上正在使用哪些端口

时间:2018-08-17 18:49:13

标签: python sockets networking port nmap

我有一台服务器,它在各个端口上托管应用程序,这些端口由服务器随机分配。我希望能够扫描服务器的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())

有帮助吗??我发现的大多数线程都试图从主机扫描主机上的打开端口。我想从客户端扫描正在使用端口的主机。有道理吗?

谢谢!

1 个答案:

答案 0 :(得分:0)

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

启动所有connect之后,便开始轮询文件描述符,以等待连接是否成功。这里最困难的部分实际上只是跟踪所有套接字的状态信息。

找到打开的端口后,您可以进入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()