From bae75d53c184f9951f61ad944311c2b760408bde Mon Sep 17 00:00:00 2001 From: Smart-SangGe <2251250136@qq.com> Date: Tue, 4 Apr 2023 11:07:59 +0800 Subject: [PATCH] modified: README.md modified: client/main.py modified: database/initdb.py new file: node/main.py modified: server/main.py modified: server/xiaomiandns.py --- README.md | 4 ++ client/main.py | 87 +++++++++++++++--------- database/initdb.py | 6 +- node/main.py | 0 server/main.py | 2 +- server/xiaomiandns.py | 151 ++++++++++++++++++++++++++++++++++-------- 6 files changed, 188 insertions(+), 62 deletions(-) create mode 100644 node/main.py diff --git a/README.md b/README.md index d12fa32..e448842 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ sqlite3 ## 安装步骤 ## 使用说明 +本项目中包含三种角色,client, node和server。每种角色运行所需要的代码在相应的项目文件夹下面。 +client: 即客户端。可以通过用户端连接小面网络、创建小面网站、访问别人创建的小面网站。 +node: 即代理节点。运行此程序可以将计算机加入小面网络中,代理连接流量 +server: 即DNS服务器和小面网站目录服务器。运行此程序可以作为server接受请求。 ## 许可证 diff --git a/client/main.py b/client/main.py index 92b7721..a7153ce 100644 --- a/client/main.py +++ b/client/main.py @@ -1,51 +1,74 @@ from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives import serialization, hashes +import base64 import random # 生产随机域名 -def generate_domain(): + + +def generate_domain() -> str: domain = random.getrandbits(64) domain = hex(domain)[2:] return domain + ".xiaomian" -# Generate a new RSA key pair -private_key = rsa.generate_private_key( - public_exponent=65537, - key_size=2048 -) -public_key = private_key.public_key() +def generate_key(): -# Convert the public key to bytes -public_key_bytes = public_key.public_bytes( + # Generate a new RSA key pair + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048 + ) + public_key = private_key.public_key() + + # Convert keys to bytes + private_key_bytes = private_key.private_bytes( encoding=serialization.Encoding.PEM, - format=serialization.PublicFormat.SubjectPublicKeyInfo -) - -# Encrypt a message using the public key -message = b"Hello World" -encrypted_message = public_key.encrypt( - message, - padding.OAEP( - mgf=padding.MGF1(algorithm=hashes.SHA256()), - algorithm=hashes.SHA256(), - label=None + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption() ) -) - -# Decrypt the message using the private key -decrypted_message = private_key.decrypt( - encrypted_message, - padding.OAEP( - mgf=padding.MGF1(algorithm=hashes.SHA256()), - algorithm=hashes.SHA256(), - label=None + public_key_bytes = public_key.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo ) -) -print(decrypted_message) + + # Encode bytes as base64 + private_key_base64 = base64.b64encode(private_key_bytes).decode('utf-8') + public_key_base64 = base64.b64encode(public_key_bytes).decode('utf-8') + + # private_key + print(private_key_bytes) + print(public_key_bytes) +# # Encrypt a message using the public key +# message = b"Hello World" +# encrypted_message = public_key.encrypt( +# message, +# padding.OAEP( +# mgf=padding.MGF1(algorithm=hashes.SHA256()), +# algorithm=hashes.SHA256(), +# label=None +# ) +# ) + +# # Decrypt the message using the private key +# decrypted_message = private_key.decrypt( +# encrypted_message, +# padding.OAEP( +# mgf=padding.MGF1(algorithm=hashes.SHA256()), +# algorithm=hashes.SHA256(), +# label=None +# ) +# ) +# print(decrypted_message) if __name__ == '__main__': print("Welcome to my xiaomiao tor network") domain = generate_domain() - print(domain) + + # print(domain) + # public_key_base64 = base64.b64encode(public_key_bytes).decode('utf-8') + # print(public_key_base64) + # public_key = base64.b64decode(public_key_base64) + # print(public_key) + generate_key() diff --git a/database/initdb.py b/database/initdb.py index ad436a4..8e1f7fe 100644 --- a/database/initdb.py +++ b/database/initdb.py @@ -5,9 +5,11 @@ if __name__ == '__main__': conn = sqlite3.connect(db_file) cursor = conn.cursor() try: - cursor.execute('''CREATE TABLE xiaomiandns(domain TEXT PRIMARY KEY, ip TEXT, pubkey TEXT)''') + cursor.execute( + '''CREATE TABLE xiaomiandns(domain TEXT PRIMARY KEY, ip TEXT, pubkey TEXT, nodetype TEXT,timestamp DATETIME)''') + # node type contain 3 types: client, node, server except sqlite3.OperationalError: print("table xiaomiandns already exists") conn.commit() cursor.close() - conn.close() \ No newline at end of file + conn.close() diff --git a/node/main.py b/node/main.py new file mode 100644 index 0000000..e69de29 diff --git a/server/main.py b/server/main.py index 3a40871..bfa0985 100644 --- a/server/main.py +++ b/server/main.py @@ -1,5 +1,5 @@ import xiaomiandns -import asyncio + if __name__ == '__main__': db_file = '../database/dns.db' diff --git a/server/xiaomiandns.py b/server/xiaomiandns.py index 6047172..442b3b9 100644 --- a/server/xiaomiandns.py +++ b/server/xiaomiandns.py @@ -10,6 +10,7 @@ import dns.rrset import time import sqlite3 import re +import base64 class DNSServer: @@ -68,7 +69,7 @@ class DNSServer: name = question.question[0].name # search domain in database dbcursor.execute( - "SELECT ip FROM xiaomiandns WHERE domain = ?", (str(name)[:-1],)) + "SELECT ip FROM xiaomiandns WHERE domain = ? AND nodetype = client", (str(name)[:-1],)) result = dbcursor.fetchone() # Create a new RRset for the answer @@ -86,8 +87,12 @@ class DNSServer: class DNSAPI: - # usage: /add?domian=xxxx&ip=xx.xx.xx.xx&pubkey=xxxxx - # /delete?domian=xxxx&ip=xx.xx.xx.xx&prikey=xxxxx + # usage: use POST method + # /add + # data: domian=xxxx&ip=xx.xx.xx.xx&pubkey=xxxxx&nodetype=xxxx + # /delete + # data: domian=xxxx&ip=xx.xx.xx.xx&prikey=xxxxx&nodetype=xxxx + def __init__(self, hostname, port, db_file): self.hostname = hostname @@ -117,24 +122,53 @@ class DNSAPI: def handle_http_request(self, request): request_line, headers = request.split('\r\n\r\n', 2) method, url, version = request_line.split(' ', 2) - print(method, url) + if method == 'GET': response = self.handle_get_request(url) + elif method == 'POST': + data = request.split('\r\n')[-1] + response = self.handle_post_request(url, data) else: response = self.handle_error_request() + return response def handle_get_request(self, url): + + # check url start with /add + # if re.match(r'^/add\?', url): + # status_code = self.add_data(url[5:]) + # if status_code == 200: + # reason_phrase = 'Add data successful' + # else: + # reason_phrase = 'Add data unsuccessful' + # # check url start with /delete + # elif re.match(r'^/delete\?', url): + # status_code = self.delete_data(url[9:]) + # if status_code == 200: + # reason_phrase = 'Delete data successful' + # else: + # reason_phrase = 'Delete data unsuccessful' + # else: + # status_code = 400 + # reason_phrase = 'unsupport api' + + response = 'HTTP/1.1 {} {}\r\n'.format(status_code, reason_phrase) + return response.encode("utf-8") + + def handle_post_request(self, url, data): + # 处理 POST 请求,data 是 POST 方法提交的数据 + # check url start with /add if re.match(r'^/add\?', url): - status_code = self.add_data(url[5:]) + status_code = self.add_data(data) if status_code == 200: reason_phrase = 'Add data successful' else: reason_phrase = 'Add data unsuccessful' # check url start with /delete elif re.match(r'^/delete\?', url): - status_code = self.delete_data(url[9:]) + status_code = self.delete_data(data) if status_code == 200: reason_phrase = 'Delete data successful' else: @@ -142,11 +176,7 @@ class DNSAPI: else: status_code = 400 reason_phrase = 'unsupport api' - - headers = { - 'Content-Type': 'text/html', - 'Connection': 'close', - } + response = 'HTTP/1.1 {} {}\r\n'.format(status_code, reason_phrase) return response.encode("utf-8") @@ -161,41 +191,108 @@ class DNSAPI: return response.encode("utf-8") def add_data(self, url): - domain = re.search(r'domain=([^&]+)', url) - ip = re.search(r'ip=([^&]+)', url) - pubkey = re.search(r'pubkey=([^&]+)', url) - if domain and ip and key: - domain = domain.group(1) - ip = ip.group(1) - pubkey = pubkey.group(1) - else: + + # parse and check validation + domain, ip, pubkey, nodetype = parse_data(url) + + if not check_data(url): return 400 + + # connect db conn = sqlite3.connect(self.db_file) c = conn.cursor() # Check if the data already exists c.execute( - "SELECT * FROM xiaomiandns WHERE domain = ? OR ip = ? OR pubkey = ?", (domain, ip, pubkey)) + "SELECT * FROM xiaomiandns WHERE domain = ? OR ip = ? OR pubkey = ? OR nodetype = ?", (domain, ip, pubkey, nodetype)) existing_data = c.fetchall() + + cursor.close() + conn.close() + if existing_data: return 400 else: # Insert the new data c.execute( - "INSERT INTO xiaomiandns (domain, ip, pubkey) VALUES (?, ?, ?)", (domain, ip, pubkey)) + "INSERT INTO xiaomiandns (domain,ip,pubkey,nodetype,timestamp) VALUES (?,?,?,?,DATETIME('now'))", (domain, ip, pubkey, nodetype)) return 200 def delete_data(self, url): - m = re.search(r'domain=([^&]+)', url) - if m: - domain = m.group(1) - print(domain) + + # parse and check validation + domain, ip, privkey, nodetype = parse_data(url) + + if not check_data(url): + return 400 + + # connect db + conn = sqlite3.connect(self.db_file) + c = conn.cursor() + + c.execute( + "SELECT pubkey FROM xiaomiandns WHERE domain = ? AND ip = ? AND nodetype = ?", (domain, ip, pubkey, nodetype)) + pubkey = c.fetchone()[0] + pubkey = pubkey + cursor.close() + conn.close() + + + + if existing_data: + return 400 else: - print('not matched') - return 200 + # Insert the new data + c.execute( + "INSERT INTO xiaomiandns (domain,ip,pubkey,nodetype,timestamp) VALUES (?,?,?,?,DATETIME('now'))", (domain, ip, pubkey, nodetype)) + return 200 + + def parse_data(self, url): + + domain = re.search(r'domain=([^&]+)', url) + ip = re.search(r'ip=([^&]+)', url) + pubkey = re.search(r'pubkey=([^&]+)', url) + privkey = re.search(r'privkey=([^&]+)', url) + nodetype = re.search(r'nodetype=([^]+)', url) + + if domain and ip and nodetype: + domain = domain.group(1) + ip = ip.group(1) + nodetype = nodetype.group(1) + if bool(pubkey) != bool(privkey): + if pubkey: + key = pubkey.group(1) + else: + key = privkey.group(1) + return domain, ip, key, nodetype + + def check_data(self, domain, ip, nodetype): + + # check domain + pattern = r'^[a-z0-9]{16}\.xiaomian$' + + if re.match(pattern, domain): + return True + else: + return False + + # check ip + pattern = r'^(\d{1,3}\.){3}\d{1,3}$' + if re.match(pattern, ip): + octets = ip.split('.') + if all(int(octet) < 256 for octet in octets): + return True + return False + + # check nodetype + if nodetype in {"server", "client", "node"}: + return True + else: + return False if __name__ == '__main__': + # some config db_file = '../database/dns.db' DNS_port = 53