我有一台服务器,它在各个端口上托管应用程序,这些端口由服务器随机分配。我希望能够扫描服务器的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())
有帮助吗??我发现的大多数线程都试图从主机扫描主机上的打开端口。我想从客户端扫描正在使用端口的主机。有道理吗?
谢谢!
答案 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
模块的epoll
或poll
对象。 (还有更多使用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()