|
|
@ -36,12 +36,18 @@ def __checkPortAvailable(port): # 检测端口可用性 |
|
|
|
return True # IPv4 TCP / IPv4 UDP / IPv6 TCP / IPv6 UDP 均无占用 |
|
|
|
except: |
|
|
|
return False |
|
|
|
finally: |
|
|
|
try: # 关闭socket |
|
|
|
if ipv4_tcp: ipv4_tcp.close() |
|
|
|
if ipv4_udp: ipv4_udp.close() |
|
|
|
if ipv6_tcp: ipv6_tcp.close() |
|
|
|
if ipv6_udp: ipv6_udp.close() |
|
|
|
finally: # 关闭socket |
|
|
|
try: |
|
|
|
ipv4_tcp.close() |
|
|
|
except: pass |
|
|
|
try: |
|
|
|
ipv4_udp.close() |
|
|
|
except: pass |
|
|
|
try: |
|
|
|
ipv6_tcp.close() |
|
|
|
except: pass |
|
|
|
try: |
|
|
|
ipv6_udp.close() |
|
|
|
except: pass |
|
|
|
|
|
|
|
def __genTaskFlag(length = 16): # 生成任务代号 |
|
|
@ -63,11 +69,28 @@ def __getAvailablePort(rangeStart, rangeEnd): # 获取一个空闲端口 |
|
|
|
return port |
|
|
|
time.sleep(0.1) # 100ms |
|
|
|
|
|
|
|
def build(proxyInfo, configDir): # 构建代理节点连接 |
|
|
|
taskFlag = __genTaskFlag() |
|
|
|
socksPort = __getAvailablePort(1024, 65535) # Socks5测试端口 |
|
|
|
if not 'type' in proxyInfo: |
|
|
|
return None |
|
|
|
def build(proxyInfo, configDir, portRangeStart = 1024, portRangeEnd = 65535): |
|
|
|
''' |
|
|
|
创建代理节点客户端 |
|
|
|
|
|
|
|
程序内部错误: |
|
|
|
return None, {reason} |
|
|
|
|
|
|
|
代理节点无效: |
|
|
|
return False, {reason} |
|
|
|
|
|
|
|
代理工作正常: |
|
|
|
return True, { |
|
|
|
'flag': taskFlag, |
|
|
|
'port': socksPort, |
|
|
|
'file': configFile, |
|
|
|
'process': process |
|
|
|
} |
|
|
|
''' |
|
|
|
taskFlag = __genTaskFlag() # 生成测试标志 |
|
|
|
socksPort = __getAvailablePort(portRangeStart, portRangeEnd) # 获取Socks5测试端口 |
|
|
|
if not 'type' in proxyInfo: # 未指定节点类型 |
|
|
|
return False, 'Proxy type not specified' |
|
|
|
proxyType = proxyInfo['type'] # 节点类型 |
|
|
|
proxyInfo.pop('type') |
|
|
|
|
|
|
@ -77,19 +100,16 @@ def build(proxyInfo, configDir): # 构建代理节点连接 |
|
|
|
elif proxyType == 'ssr': # ShadowsocksR节点 |
|
|
|
startCommand, fileContent = ShadowsocksR.load(proxyInfo, socksPort, configFile) |
|
|
|
else: # 未知类型 |
|
|
|
print("Unknown proxy type") |
|
|
|
return None |
|
|
|
return False, 'Unknown proxy type' |
|
|
|
if startCommand == None: # 格式出错 |
|
|
|
print("Format error with " + proxyType) |
|
|
|
return None |
|
|
|
return False, 'Format error with ' + str(proxyType) |
|
|
|
try: |
|
|
|
with open(configFile, 'w') as fileObject: |
|
|
|
fileObject.write(fileContent) # 保存配置文件 |
|
|
|
except: |
|
|
|
print("Unable write to file " + configFile) |
|
|
|
return None # 配置文件写入失败 |
|
|
|
except: # 配置文件写入失败 |
|
|
|
return None, "Unable write to file " + str(configFile) |
|
|
|
|
|
|
|
try: |
|
|
|
try: # 子进程形式启动 |
|
|
|
for libcPath in libcPaths: |
|
|
|
if os.path.exists(libcPath): # 定位libc.so文件 |
|
|
|
break |
|
|
@ -99,37 +119,68 @@ def build(proxyInfo, configDir): # 构建代理节点连接 |
|
|
|
stderr = subprocess.DEVNULL, |
|
|
|
preexec_fn = exitWithMe) # 子进程跟随退出 |
|
|
|
except: |
|
|
|
print("WARNING: Subprocess may become a Orphan Process") |
|
|
|
try: |
|
|
|
process = subprocess.Popen(startCommand, |
|
|
|
stdout = subprocess.DEVNULL, |
|
|
|
stderr = subprocess.DEVNULL) # prctl失败 回退正常启动 |
|
|
|
except: pass |
|
|
|
if not 'process' in vars(): # 启动失败 |
|
|
|
return None, 'Subprocess start failed by `' + ' '.join(startCommand) + '`' |
|
|
|
|
|
|
|
return { # 返回连接参数 |
|
|
|
return True, { # 返回连接参数 |
|
|
|
'flag': taskFlag, |
|
|
|
'port': socksPort, |
|
|
|
'file': configFile, |
|
|
|
'process': process, |
|
|
|
'pid': process.pid, |
|
|
|
'process': process |
|
|
|
} |
|
|
|
|
|
|
|
def check(taskInfo): # 检查客户端是否正常 |
|
|
|
if taskInfo == None: |
|
|
|
return False |
|
|
|
process = taskInfo['process'] |
|
|
|
if process.poll() != None: |
|
|
|
def check(client): |
|
|
|
''' |
|
|
|
检查客户端是否正常运行 |
|
|
|
|
|
|
|
检测出错: return None |
|
|
|
|
|
|
|
工作异常: return False |
|
|
|
|
|
|
|
工作正常: return True |
|
|
|
''' |
|
|
|
if client == None: |
|
|
|
return None |
|
|
|
try: |
|
|
|
if client['process'].poll() != None: |
|
|
|
return False # 死亡 |
|
|
|
else: |
|
|
|
return True # 正常 |
|
|
|
except: |
|
|
|
return None # 异常 |
|
|
|
|
|
|
|
def destroy(client): |
|
|
|
''' |
|
|
|
结束客户端并清理 |
|
|
|
|
|
|
|
销毁异常: return None |
|
|
|
|
|
|
|
def destroy(taskInfo): # 结束客户端并清理 |
|
|
|
if taskInfo == None: |
|
|
|
return |
|
|
|
process = taskInfo['process'] |
|
|
|
客户端退出: return True |
|
|
|
''' |
|
|
|
if client == None: |
|
|
|
return None |
|
|
|
try: |
|
|
|
process = client['process'] |
|
|
|
if process.poll() == None: # 未死亡 |
|
|
|
process.terminate() # SIGTERM |
|
|
|
while process.poll() == None: # 等待退出 |
|
|
|
time.sleep(1) |
|
|
|
process.terminate() |
|
|
|
except: |
|
|
|
return None |
|
|
|
|
|
|
|
try: |
|
|
|
os.remove(taskInfo['file']) # 删除配置文件 |
|
|
|
except: pass |
|
|
|
file = client['file'] |
|
|
|
if os.path.exists(file) and os.path.isfile(file): |
|
|
|
os.remove(file) # 删除配置文件 |
|
|
|
else: |
|
|
|
return None |
|
|
|
except: |
|
|
|
return None |
|
|
|
|
|
|
|
return True # 销毁完成 |
|
|
|