diff --git a/Basis/Api.py b/Basis/Api.py index 66040b3..8761b17 100644 --- a/Basis/Api.py +++ b/Basis/Api.py @@ -3,6 +3,7 @@ import json from gevent import pywsgi +from Basis.Manage import Manage from Basis.Logger import logging from Basis.Constant import Version from flask import Flask, Response, request @@ -37,35 +38,80 @@ def tokenCheck() -> bool: def getTaskList() -> Response: if not tokenCheck(): # token check return tokenError() - logging.critical('get task list') - return jsonResponse({}) + try: + taskList = Manage.listTask() + logging.debug('api get task list -> %s' % taskList) + return jsonResponse({ + 'success': True, + 'task': taskList, + }) + except: + return jsonResponse({ + 'success': False, + 'message': 'Unknown error' + }) @webApi.route('/task', methods = ['POST']) def createTask() -> Response: if not tokenCheck(): # token check return tokenError() - logging.critical('create task') - return jsonResponse({}) + checkList = request.json.get('check') + proxyList = request.json.get('proxy') + if checkList is None or type(checkList) != list: + return jsonResponse({ + 'success': False, + 'message': 'invalid check list', + }) + if proxyList is None or type(proxyList) != list: + return jsonResponse({ + 'success': False, + 'message': 'invalid proxy list', + }) + logging.debug('api create task -> check: %s | proxy: %s' % (checkList, proxyList)) + + # TODO: format check and proxy list + + tasks = [] + for proxy in proxyList: + tasks.append({**proxy, 'check': checkList}) + checkId = Manage.addTask(tasks) + logging.debug('api return check id %s' % checkId) + + return jsonResponse({ + 'success': True, + 'id': checkId, + 'check': checkList, + 'proxy': proxyList, + }) @webApi.route('/task/', methods = ['GET']) -def getTaskInfo() -> Response: +def getTaskInfo(taskId: str) -> Response: if not tokenCheck(): # token check return tokenError() - logging.critical('get task info') - return jsonResponse({}) + logging.critical('API get task %s info' % taskId) + if not Manage.isTask(taskId): + return jsonResponse({ + 'success': False, + 'message': 'task id not found', + }) + return jsonResponse({ + 'success': True, + **Manage.getTask(taskId) + }) @webApi.route('/version', methods = ['GET']) def getVersion() -> Response: logging.debug('get version -> %s' + Version) return jsonResponse({ - 'version': Version + 'success': True, + 'version': Version, }) -def startServer(apiPort: int = 7839, apiToken: str = '') -> None: +def startServer(apiToken: str = '', apiPort: int = 7839) -> None: global token token = apiToken logging.warning('API server at http://:%i/' % apiPort) diff --git a/Basis/Check.py b/Basis/Check.py new file mode 100644 index 0000000..4fc3ec2 --- /dev/null +++ b/Basis/Check.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import time +from Basis.Logger import logging +from Builder import Builder, clientEntry + +from ProxyChecker import httpCheck # TODO: refactor in the future + + +def Check(proxyType: str, proxyInfo: dict, checkInfo: dict) -> dict: + # TODO: checkInfo -> [...] (only check http for now) + if proxyType not in clientEntry: + logging.error('Unknown proxy type %s' % proxyType) + raise RuntimeError('Unknown proxy type') + try: + client = Builder(proxyType, proxyInfo) + except Exception as reason: + logging.error('Client build error -> %s' % reason) + raise RuntimeError('Client build error') + + # TODO: debug combine output + logging.debug(client.id) + logging.debug(client.proxyType) + logging.debug(client.proxyInfo) + logging.debug(client.socksAddr) + logging.debug(client.socksPort) + + # TODO: wait port occupied + time.sleep(1) + if not client.status(): # client unexpected exit + client.destroy() # remove file and kill sub process + logging.error('Client unexpected exit\n%s', client.output) + raise RuntimeError('Client unexpected exit') + + # TODO: check process + status, _ = httpCheck(client.socksPort) # TODO: add socks5 addr + + logging.critical('http check status -> %s' % status) + + client.destroy() # clean up the client + + return { + 'http': { + 'status': status, + # TODO: more http check info + }, + # TODO: more check items (from checkInfo list) + } + diff --git a/Basis/Manage.py b/Basis/Manage.py index 7f5ee2b..ea59be4 100644 --- a/Basis/Manage.py +++ b/Basis/Manage.py @@ -50,9 +50,8 @@ class Task(object): if completed < len(subList): # some sub tasks are not completed logging.debug('[%s] task still running' % taskId) return { - 'done': False, - 'total': len(subList), - 'finish': completed, + 'finish': False, + 'percent': '%i%%' % (completed / len(subList)) } logging.debug('[%s] task work complete' % taskId) # all sub tasks completed result = [] @@ -62,7 +61,7 @@ class Task(object): result.append(subTask['data']) logging.debug('release sub tasks -> %s' % result) return { - 'done': True, + 'finish': True, 'result': result } diff --git a/demo.py b/demo.py index 8696076..a3dae6c 100755 --- a/demo.py +++ b/demo.py @@ -132,17 +132,20 @@ proxyHysteria = { # client = Builder('trojan', proxyTrojan) # client = Builder('trojan-go', proxyTrojanGo) # client = Builder('brook', proxyBrook) -client = Builder('hysteria', proxyHysteria) +# client = Builder('hysteria', proxyHysteria) +# +# logging.critical(client.id) +# logging.critical(client.proxyType) +# logging.critical(client.proxyInfo) +# logging.critical(client.socksAddr) +# logging.critical(client.socksPort) +# +# time.sleep(15) +# logging.critical(client.status()) +# +# client.destroy() +# logging.critical(client.status()) +# logging.critical('Client output:\n' + str(client.output)) -logging.critical(client.id) -logging.critical(client.proxyType) -logging.critical(client.proxyInfo) -logging.critical(client.socksAddr) -logging.critical(client.socksPort) - -time.sleep(15) -logging.critical(client.status()) - -client.destroy() -logging.critical(client.status()) -logging.critical('Client output:\n' + str(client.output)) +from Basis.Check import Check +Check('ss', proxySS, {}) diff --git a/main.py b/main.py index ae87141..76d78e2 100755 --- a/main.py +++ b/main.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- - +import os import time import _thread import subprocess @@ -9,21 +9,28 @@ from Basis.Api import startServer from Basis.Constant import Version from Basis.Compile import startCompile +dnsServers = None +# dnsServers = ['223.5.5.5', '119.28.28.28'] + def startDnsproxy(command: list) -> subprocess.Popen: logging.debug('start dnsproxy -> %s' % command) return subprocess.Popen(command, stdout = subprocess.PIPE, stderr = subprocess.STDOUT) -def daemonDnsproxy(dnsServers: list, localPort: int = 53, cacheSize: int = 4194304) -> None: # default cache -> 4MiB - logging.info('start dnsproxy at port %i -> %s' % (localPort, dnsServers)) +def daemonDnsproxy(servers: list or None, port: int = 53, cache: int = 4194304) -> None: # default cache size -> 4MiB + if servers is None or len(servers) == 0: + logging.info('skip dnsproxy process') + return + logging.info('start dnsproxy at port %i -> %s' % (port, servers)) + os.system('echo "nameserver 127.0.0.1" > /etc/resolv.conf') # system dns settings dnsCommand = [ 'dnsproxy', '--all-servers', - '--port', str(localPort), - '--cache', '--cache-size', str(cacheSize) + '--port', str(port), + '--cache', '--cache-size', str(cache) ] - for dnsServer in dnsServers: - dnsCommand += ['--upstream', dnsServer] + for server in servers: + dnsCommand += ['--upstream', server] dnsproxy = startDnsproxy(dnsCommand) while True: time.sleep(2) # daemon time gap @@ -33,10 +40,24 @@ def daemonDnsproxy(dnsServers: list, localPort: int = 53, cacheSize: int = 41943 dnsproxy = startDnsproxy(dnsCommand) -logging.warning('ProxyC starts running (%s)' % Version) +from Basis.Check import Check +from Basis.Manage import Manage -_thread.start_new_thread(startCompile, ('/usr', )) +def loopCheck() -> None: + while True: + time.sleep(2) # TODO: thread pool working + subTaskId, subTask = Manage.popSubTask() + if subTaskId is None: continue + logging.info('new sub task -> %s', subTask) + ret = Check(subTask['type'], subTask['info'], {}) + logging.info('check result -> %s' % ret) + subTask.pop('check') + subTask['result'] = ret + Manage.updateSubTask(subTaskId, subTask) -_thread.start_new_thread(daemonDnsproxy, (['223.5.5.5', '119.28.28.28'], 53)) -startServer(apiToken = 'dnomd343') +logging.warning('ProxyC starts running (%s)' % Version) +_thread.start_new_thread(startCompile, ('/usr', )) # python compile (generate .pyc file) +_thread.start_new_thread(daemonDnsproxy, (dnsServers, 53)) # set system's dns server +_thread.start_new_thread(loopCheck, ()) # start loop check +startServer(apiToken = '') # start api server