├── heartbeat.py ├── sshr_readme.md ├── README.md └── srun.py /heartbeat.py: -------------------------------------------------------------------------------- 1 | from srun import SrunClient 2 | import socket 3 | import logging 4 | 5 | logging.basicConfig( 6 | format='%(asctime)s.%(msecs)03d [%(filename)s:%(lineno)d] %(message)s', 7 | datefmt='## %Y-%m-%d %H:%M:%S' 8 | ) 9 | logging.getLogger("heartbeat").setLevel(logging.INFO) 10 | logger = logging.getLogger("heartbeat") 11 | 12 | USERNAME = '' 13 | PASSWD = '' 14 | 15 | CHECK_SERVER = 'www.baidu.com' 16 | 17 | 18 | def check_connect(): 19 | with socket.socket() as s: 20 | s.settimeout(3) 21 | try: 22 | status = s.connect_ex((CHECK_SERVER, 443)) 23 | return status == 0 24 | except Exception as e: 25 | print(e) 26 | return False 27 | 28 | 29 | def check_online(): 30 | if check_connect(): return 31 | srun_client = SrunClient(print_log=False) 32 | # Use this method frequently to check online is not suggested! 33 | # if srun_client.check_online(): return 34 | logger.info('NOT ONLINE, TRY TO LOGIN!') 35 | srun_client.username = USERNAME 36 | srun_client.passwd = PASSWD 37 | srun_client.login() 38 | 39 | 40 | if __name__ == "__main__": 41 | check_online() 42 | 43 | # import time 44 | # while 1: 45 | # check_online() 46 | # time.sleep(10) 47 | -------------------------------------------------------------------------------- /sshr_readme.md: -------------------------------------------------------------------------------- 1 | # SSH 反向代理 内网穿透 2 | 3 | - ### 基本原理 4 | 内网主机没有公网ip,在外网中无法直接通过公网ip来连接内网主机。 5 | 让内网主机向有公网ip的外网主机发起ssh请求,并建立起隧道连接。 6 | 此时外网主机和内网主机有一条通信隧道,便可以通过这条隧道进行ssh连接,而不用知道内网主机的ip。 7 | 8 | - ### *内网主机A* : A_user@A_host 9 | - ### *外网主机B* : B_user@B_host 10 | 11 | 12 | ## 内网主机A 操作步骤(大概步骤) 13 | 14 | 1. 确保内网主机一直在线(optional) 15 | 设置定时任务执行heartbeat.py 16 | 或修改heartbeat.py,循环检测掉线,使用`nohup`执行 17 | `nohup python heartbeat.py &` 18 | 不断检测网络在线情况,防止掉线。 (heartbeat.py中填入登录账户和密码) 19 | 20 | 2. ssh设置免密登录 21 | `ssh-keygen` 22 | `ssh-copy-id B_user@B_host` 23 | 24 | 3. 使用autossh反向代理 25 | `autossh -M 4010 -fCNR 1024:localhost:22 B_user@B_host` 26 | autossh的作用类似于不挂断的进行ssh连接 27 | ``` 28 | -M 4010 置autossh的监听端口为4010 29 | -fCNR 30 | -f 让ssh在后台执行命令 31 | -C 允许压缩数据 32 | -N 只进行端口转发,不执行远程指令 33 | -R 远程主机的端口转发到本地端口 (反向代理) 34 | -L 本地端口转发到远程主机端口 (正向代理) 35 | 1024:localhost:22 36 | 1024 远程主机端口 37 | 22 本地端口 (ssh默认端口) 38 | ``` 39 | 40 | 41 | 4. 设置开机自启 42 | 将网络在线检测以及autossh反向代理命令添加到开机自启 43 | `sudo vi /etc/rc.d/rc.local` 44 | 将下列命令填入开机自启文件 /etc/rc.d/rc.local 45 | ``` 46 | nohup python /path/to/heartbeat.py & 47 | autossh -M 4010 -fCNR 1024:localhost:22 B_user@B_host 48 | ``` 49 | 50 | ## 外网主机B 操作流程 51 | 52 | - 在外网主机B上,通过 53 | `ssh A_user@localhost -p 1024` 54 | 可以登录到内网主机上。 55 | 56 | - 或者 57 | `ssh -fCNL *:2048:localhost:1024 localhost` 58 | 将本地的1024端口转发到本地的2048端口 59 | 如此可实现在任意终端上,通过外网主机B的代理,连接上内网主机A 60 | `ssh A_user@B_host -p 2048` 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SrunClient 2 | 简易版深澜命令行客户端,包含登录登出和查询在线信息功能。支持 windows/Linux/?MacOS,python2/3 。 包含校园内网服务器反向代理教程。 3 | 4 | # how to use 5 | 1. 使用命令行客户端登录登出 6 | 更换`srun_ip`为自己学校对应的深澜网关地址 7 | ``` python3 8 | class SrunClient: 9 | 10 | name = 'CUGB' 11 | srun_ip = '202.204.105.195' # or srun_ip = 'gw.cugb.edu.cn' 12 | 13 | login_url = 'http://{}/cgi-bin/srun_portal'.format(srun_ip) 14 | online_url = 'http://{}/cgi-bin/rad_user_info'.format(srun_ip) 15 | headers = {'User-Agent': 'SrunClient {}'.format(name)} 16 | 17 | ``` 18 | 然后运行srun.py 19 | ``` 20 | [user@host SrunClient]$ python srun.py 21 | ############### Wellcome to Srun Client ############### 22 | [1]: show online information 23 | [2]: set username and passwd 24 | [3]: login 25 | [4]: logout 26 | [h]: show this messages 27 | [q]: quit 28 | ####################################################### 29 | [SrunClient CUGB] ###*** NOT ONLINE! ***### 30 | > 31 | ``` 32 | 33 | 2. 掉线自动重连 34 | - 🍬**推荐**🍬 定时任务,定时执行heartbeat.py。 35 | 使用crontba添加定时任务 36 | ```shell 37 | [user@host SrunClient]$ crontab -e 38 | ``` 39 | 将下列命令添加,按`esc`,输入`:wq`退出保存(每分钟执行一次命令)。 40 | ``` 41 | * * * * * python /path/to/heartbeat.py 42 | ``` 43 | - 或配合`nohup`使用,每隔10秒钟检测一次在线情况,不在线则重新登录。在`heartbeat.py`中设置好登录账号和密码之后,运行: 44 | ```shell 45 | nohup python heartbeat.py & 46 | ``` 47 | `nohup` 进程有被杀死的风险 48 | 49 | # addition 50 | 51 | - [SSH反向代理 内网穿透](./sshr_readme.md) 52 | 53 | # to do 54 | 55 | - [ ] 定时下线 56 | - [ ] 更换ip 57 | -------------------------------------------------------------------------------- /srun.py: -------------------------------------------------------------------------------- 1 | import time 2 | import getpass 3 | 4 | if bytes is str: input = raw_input 5 | 6 | try: 7 | import requests 8 | 9 | def get_func(url, *args, **kwargs): 10 | resp = requests.get(url, *args, **kwargs) 11 | return resp.text 12 | 13 | def post_func(url, data, *args, **kwargs): 14 | resp = requests.post(url, data=data, *args, **kwargs) 15 | return resp.text 16 | 17 | except ImportError: 18 | import urllib.request 19 | 20 | def get_func(url, *args, **kwargs): 21 | req = urllib.request.Request(url, *args, **kwargs) 22 | resp = urllib.request.urlopen(req) 23 | return resp.read().decode("utf-8") 24 | 25 | def post_func(url, data, *args, **kwargs): 26 | data_bytes = bytes(urllib.parse.urlencode(data), encoding='utf-8') 27 | req = urllib.request.Request(url, data=data_bytes, *args, **kwargs) 28 | resp = urllib.request.urlopen(req) 29 | return resp.read().decode("utf-8") 30 | 31 | 32 | def time2date(timestamp): 33 | time_arry = time.localtime(int(timestamp)) 34 | return time.strftime('%Y-%m-%d %H:%M:%S', time_arry) 35 | 36 | 37 | def humanable_bytes(num_byte): 38 | num_byte = float(num_byte) 39 | num_GB, num_MB, num_KB = 0, 0, 0 40 | if num_byte >= 1024**3: 41 | num_GB = num_byte // (1024**3) 42 | num_byte -= num_GB * (1024**3) 43 | if num_byte >= 1024**2: 44 | num_MB = num_byte // (1024**2) 45 | num_byte -= num_MB * (1024**2) 46 | if num_byte >= 1024: 47 | num_KB = num_byte // 1024 48 | num_byte -= num_KB * 1024 49 | return '{} GB {} MB {} KB {} B'.format(num_GB, num_MB, num_KB, num_byte) 50 | 51 | 52 | def humanable_bytes2(num_byte): 53 | num_byte = float(num_byte) 54 | if num_byte >= 1024**3: 55 | return '{:.2f} GB'.format(num_byte/(1024**3)) 56 | elif num_byte >= 1024**2: 57 | return '{:.2f} MB'.format(num_byte/(1024**2)) 58 | elif num_byte >= 1024**1: 59 | return '{:.2f} KB'.format(num_byte/(1024**1)) 60 | 61 | 62 | class SrunClient: 63 | 64 | name = 'CUGB' 65 | srun_ip = '202.204.105.195' 66 | 67 | login_url = 'http://{}/cgi-bin/srun_portal'.format(srun_ip) 68 | online_url = 'http://{}/cgi-bin/rad_user_info'.format(srun_ip) 69 | # headers = {'User-Agent': 'SrunClient {}'.format(name)} 70 | headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36'} 71 | 72 | def __init__(self, username=None, passwd=None, print_log=True): 73 | self.username = username 74 | self.passwd = passwd 75 | self.print_log = print_log 76 | self.online_info = dict() 77 | self.check_online() 78 | 79 | def _encrypt(self, passwd): 80 | column_key = [0,0,'d','c','j','i','h','g'] 81 | row_key = [ 82 | ['6','7','8','9',':',';','<','=','>','?','@','A','B','C','D','E'], 83 | ['?','>','A','@','C','B','E','D','7','6','9','8',';',':','=','<'], 84 | ['>','?','@','A','B','C','D','E','6','7','8','9',':',';','<','='], 85 | ['=','<',';',':','9','8','7','6','E','D','C','B','A','@','?','>'], 86 | ['<','=',':',';','8','9','6','7','D','E','B','C','@','A','>','?'], 87 | [';',':','=','<','7','6','9','8','C','B','E','D','?','>','A','@'], 88 | [':',';','<','=','6','7','8','9','B','C','D','E','>','?','@','A'], 89 | ['9','8','7','6','=','<',';',':','A','@','?','>','E','D','B','C'], 90 | ['8','9','6','7','<','=',':',';','@','A','>','?','D','E','B','C'], 91 | ['7','6','8','9',';',':','=','<','?','>','A','@','C','B','D','E'], 92 | ] 93 | encrypt_passwd = '' 94 | for idx, c in enumerate(passwd): 95 | char_c = column_key[ord(c) >> 4] 96 | char_r = row_key[idx%10][ord(c) & 0xf] 97 | if idx%2: 98 | encrypt_passwd += char_c + char_r 99 | else: 100 | encrypt_passwd += char_r + char_c 101 | return encrypt_passwd 102 | 103 | def _log(self, msg): 104 | if self.print_log: 105 | print('[SrunClient {}] {}'.format(self.name, msg)) 106 | 107 | def check_online(self): 108 | resp_text = get_func(self.online_url, headers=self.headers) 109 | if 'not_online' in resp_text: 110 | self._log('###*** NOT ONLINE! ***###') 111 | return False 112 | try: 113 | items = resp_text.split(',') 114 | self.online_info = { 115 | 'online':True, 'username':items[0], 116 | 'login_time':items[1], 'now_time':items[2], 117 | 'used_bytes':items[6], 'used_second':items[7], 118 | 'ip':items[8], 'balance':items[11], 119 | 'auth_server_version':items[21] 120 | } 121 | return True 122 | except Exception as e: 123 | print(resp_text) 124 | print('Catch `Status Internal Server Error`? The request is frequent!') 125 | print(e) 126 | 127 | def show_online(self): 128 | if not self.check_online(): return 129 | self._log('###*** ONLINE INFORMATION! ***###') 130 | header = '================== ONLIN INFORMATION ==================' 131 | print(header) 132 | print('Username: {}'.format(self.online_info['username'])) 133 | print('Login time: {}'.format(time2date(self.online_info['login_time']))) 134 | print('Now time: {}'.format(time2date(self.online_info['now_time']))) 135 | print('Used data: {}'.format(humanable_bytes(self.online_info['used_bytes']))) 136 | print('Ip: {}'.format(self.online_info['ip'])) 137 | print('Balance: {}'.format(self.online_info['balance'])) 138 | print('=' * len(header)) 139 | 140 | def login(self): 141 | if self.check_online(): 142 | self._log('###*** ALREADY ONLINE! ***###') 143 | return True 144 | if not self.username or not self.passwd: 145 | self._log('###*** LOGIN FAILED! (username or passwd is None) ***###') 146 | self._log('username and passwd are required! (check username and passwd)') 147 | return False 148 | encrypt_passwd = self._encrypt(self.passwd) 149 | payload = { 150 | 'action': 'login', 151 | 'username': self.username, 152 | 'password': encrypt_passwd, 153 | 'type': 2, 'n': 117, 154 | 'drop': 0, 'pop': 0, 155 | 'mbytes': 0, 'minutes': 0, 156 | 'ac_id': 1 157 | } 158 | resp_text = post_func(self.login_url, data=payload, headers=self.headers) 159 | if 'login_ok' in resp_text: 160 | self._log('###*** LOGIN SUCCESS! ***###') 161 | self._log(resp_text) 162 | self.show_online() 163 | return True 164 | elif 'login_error' in resp_text: 165 | self._log('###*** LOGIN FAILED! (login error)***###') 166 | self._log(resp_text) 167 | return False 168 | else: 169 | self._log('###*** LOGIN FAILED! (unknown error) ***###') 170 | self._log(resp_text) 171 | return False 172 | 173 | def logout(self): 174 | if not self.check_online(): return True 175 | payload = { 176 | 'action': 'logout', 177 | 'ac_id': 1, 178 | 'username': self.online_info['username'], 179 | 'type': 2 180 | } 181 | resp_text = post_func(self.login_url, data=payload, headers=self.headers) 182 | if 'logout_ok' in resp_text: 183 | self._log('###*** LOGOUT SUCCESS! ***###') 184 | return True 185 | elif 'login_error' in resp_text: 186 | self._log('###*** LOGOUT FAILED! (login error) ***###') 187 | self._log(resp_text) 188 | return False 189 | else: 190 | self._log('###*** LOGOUT FAILED! (unknown error) ***###') 191 | self._log(resp_text) 192 | return False 193 | 194 | 195 | def show_commands(): 196 | wellcome = '############### Wellcome to Srun Client ###############' 197 | print(wellcome) 198 | print('[1]: show online information') 199 | print('[2]: set username and passwd') 200 | print('[3]: login') 201 | print('[4]: logout') 202 | print('[h]: show this messages') 203 | print('[q]: quit') 204 | print('#' * len(wellcome)) 205 | 206 | 207 | if __name__ == "__main__": 208 | srun_client = SrunClient() 209 | show_commands() 210 | srun_client.show_online() 211 | command = '_' 212 | while command != 'q': 213 | command = input('>') 214 | if command == '1': 215 | srun_client.show_online() 216 | elif command == '2': 217 | srun_client.username = input('username: ') 218 | srun_client.passwd = getpass.getpass('passwd: ') 219 | elif command == '3': 220 | srun_client.login() 221 | elif command == '4': 222 | srun_client.logout() 223 | elif command == 'h': 224 | show_commands() 225 | elif command == 'q': 226 | print('bye!') 227 | else: 228 | print('unknown command!') 229 | --------------------------------------------------------------------------------