一、背景介绍
当前网络处于校内网环境,登录外网需要学生账号,中间有小米路由。不同时间登录可能会出现ip地址更换。(当前路由下有两台电脑和一台nano)
要实现的目标:将个人路由下的设备用二级域名绑定,实现校内网远程使用域名访问路由下的设备。
需要的原料:域名,pycharm,联网主机。(当前使用腾讯云为案例)
二、操作流程
1)域名管理平台设置 (SecretId 和SecretKey)用来登录腾讯云账号
2)生成API密钥
3)进入API文档,进行构建代码更新DNS函数,其中要使用到,更新动态DNS记录,获取域名解析记录。
这里是先添加了一条DNS记录,您也可以使用API进行添加。这里可自行研究。
在我的域名DNS解析中添加:
4)模拟发送请求,获取RecordID
填好域名和二级域名后代码生成后直接调试,弹出控制台,等待加载完毕回车执行py脚本。得到当前DNS记录的RecordID。新建记事本保存好生成的函数。
5)获取更新DNS记录的API函数
可以使用界面方式就行调用。可以看见调用修改DNS
三、本地脚本构建
主要思想:①检测校园网登录状态,②登录校园网,③ 调用更新DNSAPI函数。
封装日志模块
# -*- coding: utf-8 -*
import sys
import os
import time
import logging
def get_loger():
curPath = os.path.abspath(os.path.dirname(__file__))
logger = logging.getLogger()
logger.setLevel(logging.NOTSET) # Log等级总开关
# 创建一个handler,用于写入日志文件
rq = time.strftime('%Y%m', time.localtime(time.time()))
log_path = os.path.join(curPath,'Logs')
os.makedirs(log_path,exist_ok=True)
log_name = os.path.join(log_path , rq + '.log')
logfile = log_name
fh = logging.FileHandler(logfile, mode='a',encoding="utf-8")
fh.setLevel(logging.INFO) # 输出到file的log等级的开关
# 定义handler的输出格式
formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
fh.setFormatter(formatter)
rf_handler = logging.StreamHandler(sys.stderr)
rf_handler.setLevel(logging.INFO)
rf_handler.setFormatter(formatter)
logger.addHandler(fh)
logger.addHandler(rf_handler)
# 使用logger.XX来记录错误,这里的"error"可以根据所需要的级别进行修改
return logger
if __name__ == '__main__':
logger=get_loger()
logger.info("logger init")
封装检测和登录模块
# Author: Kequanchen
# Date : 2022年3月8日08:58:42
# Des : 主要用来自动检测当前主机ip并绑定在自身域名上
# -*- coding:utf-8 -*
import os
import sys
sys.path.append(os.path.dirname(__file__))
import requests
import socket
import Logger
logger = Logger.get_loger()
#校园网账号
account = ""
password = ""
headers = {
'Proxy-Connection': 'keep-alive',
'Cache-Control': 'max-age=0',
'Upgrade-Insecure-Requests': '1',
'Origin': 'http://202.117.144.205:8602',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Referer': 'http://202.117.144.205:8602/snnuportal/login.jsp',
'Accept-Language': 'zh-CN,zh;q=0.9,zh-TW;q=0.8',
}
def login_snnu():
if not account or not password:
logger.error("请在auto_login.py文件指定位置补充学号和密码")
return False
data = {
'account': account,
'password': password,
'method': 'getUserInfo'
}
response = requests.post('http://202.117.144.205:8601/snnuportal/bind', headers=headers, data=data,
verify=False)
data = {
'sourceurl': 'null',
'account': account,
'password': password,
'yys': '',
'issave': ''
}
response=requests.post('http://202.117.144.205:8601/snnuportal/login', headers=headers, data=data,
verify=False)
if response.text.find("登录失败")>=0:
logger.error("账号或密码错误")
return False
elif response.text.find("当前登录账号")>=0:
logger.error("登陆成功")
return True
logger.error("网络连接错误")
return False
def islogin():
def socketTest(url):
socket.setdefaulttimeout(5)
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
try:
resp=s.connect(("www.baidu.com",80))
request_url = 'GET /s?wd=1 HTTP/1.0\r\nHost: {}\r\n\r\n'.format(url)
s.send(request_url.encode())
resp=s.recv(256)
s.close()
if resp.decode("utf8").find("10.196.0.242")>=0:#此ip为学校snnu服务器内网ip,若失效需更新
return False
return True
except:
s.close()
return False
urls=["www.baidu.com","www.sogou.com","www.weibo.com"]
for url in urls:
if socketTest(url):
return True
return False
if __name__ == '__main__':
try:
if not islogin():
login_snnu()
except:
logger.error("发生错误", exc_info=True)
# -*- coding: utf-8 -*
import os
import sys
sys.path.append(os.path.dirname(__file__))
import requests
import re
import socket
import json
import pickle
import Logger
logger = Logger.get_loger()
###########在此补充用户名和密码###############################
account = "校园网账号"
password = "校园网密码"
##########################################################
accessKeyId = "密钥ID"
accessSecret = "密钥"
#########################################################
headers = {
'Proxy-Connection': 'keep-alive',
'Cache-Control': 'max-age=0',
'Upgrade-Insecure-Requests': '1',
'Origin': 'http://202.117.144.205:8602',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Referer': 'http://202.117.144.205:8602/snnuportal/login.jsp',
'Accept-Language': 'zh-CN,zh;q=0.9,zh-TW;q=0.8',
}
def login_snnu():
if not account or not password:
logger.error("请在auto_login.py文件指定位置补充学号和密码")
return False
data = {
'account': account,
'password': password,
'method': 'getUserInfo'
}
response = requests.post('http://202.117.144.205:8601/snnuportal/bind', headers=headers, data=data,
verify=False)
data = {
'sourceurl': 'null',
'account': account,
'password': password,
'yys': '',
'issave': ''
}
response=requests.post('http://202.117.144.205:8601/snnuportal/login', headers=headers, data=data,
verify=False)
if response.text.find("登录失败")>=0:
logger.error("账号或密码错误")
return False
elif response.text.find("当前登录账号")>=0:
logger.info("登陆成功")
return True
logger.error("网络连接错误")
return False
def get_ip():
try:
response = requests.get('http://202.117.144.205:8601/snnuportal/login', headers=headers).text
Pattern=re.compile("当前IP[::](.*?)[^0123456789.]")
ip=Pattern.search(response).group(1)
return ip
except:
return None
def islogin():
def socketTest(url):
socket.setdefaulttimeout(5)
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
try:
resp=s.connect(("www.baidu.com",80))
request_url = 'GET /s?wd=1 HTTP/1.0\r\nHost: {}\r\n\r\n'.format(url)
s.send(request_url.encode())
resp=s.recv(256)
s.close()
if resp.decode("utf8").find("10.196.0.242")>=0:#此ip为学校snnu服务器内网ip,若失效需更新
return False
return True
except:
s.close()
return False
urls=["www.baidu.com","www.sogou.com","www.weibo.com"]
for url in urls:
if socketTest(url):
return True
return False
#腾讯云获取RecordID
import json
from tencentcloud.common import credential
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.dnspod.v20210323 import dnspod_client, models
def getRecordID(accessKeyId,accessSecret ):
recordId = ''
try:
cred = credential.Credential(accessKeyId, accessSecret)
httpProfile = HttpProfile()
httpProfile.endpoint = "dnspod.tencentcloudapi.com"
clientProfile = ClientProfile()
clientProfile.httpProfile = httpProfile
client = dnspod_client.DnspodClient(cred, "", clientProfile)
req = models.DescribeRecordListRequest()
params = {
"Domain": "chenkequan.cn",
"Subdomain": "nano"
}
req.from_json_string(json.dumps(params))
resp = client.DescribeRecordList(req)
recordId = resp.RecordList[0].RecordId
print(resp.to_json_string())
except TencentCloudSDKException as err:
print(err)
return recordId
def updateDNS(accessKeyId,accessSecret,IP ):
try:
cred = credential.Credential(accessKeyId,accessSecret )
httpProfile = HttpProfile()
httpProfile.endpoint = "dnspod.tencentcloudapi.com"
clientProfile = ClientProfile()
clientProfile.httpProfile = httpProfile
client = dnspod_client.DnspodClient(cred, "", clientProfile)
req = models.ModifyDynamicDNSRequest()
recordId = getRecordID(accessKeyId,accessSecret)
params = {
"Domain": "chenkequan.cn",
"SubDomain": "nano",
"RecordId": recordId,
"RecordLine": "默认",
"Value": IP
}
req.from_json_string(json.dumps(params))
resp = client.ModifyDynamicDNS(req)
print(resp.to_json_string())
except TencentCloudSDKException as err:
print(err)
def get_lastIP():
if not os.path.exists("./last_ip.pt"):
return "0.0.0.0"
with open("./last_ip.pt","rb") as f:
last_ip=pickle.load(f)
return last_ip
def update_lastIp(current_ip):
with open("./last_ip.pt","wb") as f:
pickle.dump(current_ip,f)
logger.info("更新LastIP:"+current_ip)
if __name__ == '__main__':
try:
logger.info("任务启动")
login_flag=False
if not islogin():
login_flag=login_snnu()
current_ip=get_ip()
last_ip=get_lastIP()
#测试
# current_ip= '6.6.6.6'
# last_ip = '6.6.6.5'
# login_flag = True
if current_ip!=last_ip and login_flag:
updateDNS(accessKeyId,accessSecret,current_ip)
logger.info("update chenkequan.cn:"+current_ip)
except:
logger.error("发生错误", exc_info=True)
使用测试数据,执行完毕后登录校园网查看DNS 是否更改为6.6.6.6
剩下工作就是运行在nano上
7) Jetson Nano (aarch64)搭建miniconda (查看博客另一篇文章)
创建一个执行脚本的python环境
conda create -n pureScript python=3.7 -y
激活环境,安装自动修改DNS的依赖
pip install requests
pip install tencentcloud_sdk_python
执行脚本
基本上大功告成,最后一步,进行定时执行或者循环执行。
这里采用定时任务,详细知识查看
*/1 * * * * /home/chan/miniforge3/envs/pureScript/bin/python /home/chan/Project/scriptPro/autoSnnuForTencent/auto_snnu.py >>/home/chan/Project/scriptPro/autoSnnuForTencent/1.log 2>&1
表示每分钟执行一次,并将日志输出到指定文件1.log中
这里直接使用环境中的python,也可以切换用户,激活环境的方式。不过编程不就是为了减少工作量吗,同样效果,哪种方便用哪种。这里还可以脚本中套脚本实现更详细的日志和效果。
总结:这篇文章主要介绍使用DNS API接口绑订内网IP,在局域网中实现远程连接或ssh。校内局域网中同样有效果。还差一步,将设备端口在路由上做下端口映射即可。
当前使用的是小米路由:
连接成功,而且校园内使用远程桌面再也不用担心向日葵限速啦
喜欢这篇文章记得鼓励下作者~~~ 继续给大家更有价值的教程