modified: README.md

modified:   client/main.py
	modified:   database/initdb.py
	new file:   node/main.py
	modified:   server/main.py
	modified:   server/xiaomiandns.py
This commit is contained in:
Smart-SangGe 2023-04-04 11:07:59 +08:00
parent 5acfa132e4
commit bae75d53c1
6 changed files with 188 additions and 62 deletions

View File

@ -18,6 +18,10 @@ sqlite3
## 安装步骤 ## 安装步骤
## 使用说明 ## 使用说明
本项目中包含三种角色client, node和server。每种角色运行所需要的代码在相应的项目文件夹下面。
client 即客户端。可以通过用户端连接小面网络、创建小面网站、访问别人创建的小面网站。
node 即代理节点。运行此程序可以将计算机加入小面网络中,代理连接流量
server 即DNS服务器和小面网站目录服务器。运行此程序可以作为server接受请求。
## 许可证 ## 许可证

View File

@ -1,14 +1,19 @@
from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes from cryptography.hazmat.primitives import serialization, hashes
import base64
import random import random
# 生产随机域名 # 生产随机域名
def generate_domain():
def generate_domain() -> str:
domain = random.getrandbits(64) domain = random.getrandbits(64)
domain = hex(domain)[2:] domain = hex(domain)[2:]
return domain + ".xiaomian" return domain + ".xiaomian"
def generate_key():
# Generate a new RSA key pair # Generate a new RSA key pair
private_key = rsa.generate_private_key( private_key = rsa.generate_private_key(
public_exponent=65537, public_exponent=65537,
@ -16,36 +21,54 @@ private_key = rsa.generate_private_key(
) )
public_key = private_key.public_key() public_key = private_key.public_key()
# Convert the public key to bytes # Convert keys to bytes
private_key_bytes = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
public_key_bytes = public_key.public_bytes( public_key_bytes = public_key.public_bytes(
encoding=serialization.Encoding.PEM, encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo format=serialization.PublicFormat.SubjectPublicKeyInfo
) )
# Encrypt a message using the public key # Encode bytes as base64
message = b"Hello World" private_key_base64 = base64.b64encode(private_key_bytes).decode('utf-8')
encrypted_message = public_key.encrypt( public_key_base64 = base64.b64encode(public_key_bytes).decode('utf-8')
message,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# Decrypt the message using the private key # private_key
decrypted_message = private_key.decrypt( print(private_key_bytes)
encrypted_message, print(public_key_bytes)
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
print(decrypted_message)
# # 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__': if __name__ == '__main__':
print("Welcome to my xiaomiao tor network") print("Welcome to my xiaomiao tor network")
domain = generate_domain() 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()

View File

@ -5,7 +5,9 @@ if __name__ == '__main__':
conn = sqlite3.connect(db_file) conn = sqlite3.connect(db_file)
cursor = conn.cursor() cursor = conn.cursor()
try: 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: except sqlite3.OperationalError:
print("table xiaomiandns already exists") print("table xiaomiandns already exists")
conn.commit() conn.commit()

0
node/main.py Normal file
View File

View File

@ -1,5 +1,5 @@
import xiaomiandns import xiaomiandns
import asyncio
if __name__ == '__main__': if __name__ == '__main__':
db_file = '../database/dns.db' db_file = '../database/dns.db'

View File

@ -10,6 +10,7 @@ import dns.rrset
import time import time
import sqlite3 import sqlite3
import re import re
import base64
class DNSServer: class DNSServer:
@ -68,7 +69,7 @@ class DNSServer:
name = question.question[0].name name = question.question[0].name
# search domain in database # search domain in database
dbcursor.execute( 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() result = dbcursor.fetchone()
# Create a new RRset for the answer # Create a new RRset for the answer
@ -86,8 +87,12 @@ class DNSServer:
class DNSAPI: class DNSAPI:
# usage: /add?domian=xxxx&ip=xx.xx.xx.xx&pubkey=xxxxx # usage: use POST method
# /delete?domian=xxxx&ip=xx.xx.xx.xx&prikey=xxxxx # /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): def __init__(self, hostname, port, db_file):
self.hostname = hostname self.hostname = hostname
@ -117,24 +122,53 @@ class DNSAPI:
def handle_http_request(self, request): def handle_http_request(self, request):
request_line, headers = request.split('\r\n\r\n', 2) request_line, headers = request.split('\r\n\r\n', 2)
method, url, version = request_line.split(' ', 2) method, url, version = request_line.split(' ', 2)
print(method, url)
if method == 'GET': if method == 'GET':
response = self.handle_get_request(url) response = self.handle_get_request(url)
elif method == 'POST':
data = request.split('\r\n')[-1]
response = self.handle_post_request(url, data)
else: else:
response = self.handle_error_request() response = self.handle_error_request()
return response return response
def handle_get_request(self, url): 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 # check url start with /add
if re.match(r'^/add\?', url): if re.match(r'^/add\?', url):
status_code = self.add_data(url[5:]) status_code = self.add_data(data)
if status_code == 200: if status_code == 200:
reason_phrase = 'Add data successful' reason_phrase = 'Add data successful'
else: else:
reason_phrase = 'Add data unsuccessful' reason_phrase = 'Add data unsuccessful'
# check url start with /delete # check url start with /delete
elif re.match(r'^/delete\?', url): elif re.match(r'^/delete\?', url):
status_code = self.delete_data(url[9:]) status_code = self.delete_data(data)
if status_code == 200: if status_code == 200:
reason_phrase = 'Delete data successful' reason_phrase = 'Delete data successful'
else: else:
@ -143,10 +177,6 @@ class DNSAPI:
status_code = 400 status_code = 400
reason_phrase = 'unsupport api' reason_phrase = 'unsupport api'
headers = {
'Content-Type': 'text/html',
'Connection': 'close',
}
response = 'HTTP/1.1 {} {}\r\n'.format(status_code, reason_phrase) response = 'HTTP/1.1 {} {}\r\n'.format(status_code, reason_phrase)
return response.encode("utf-8") return response.encode("utf-8")
@ -161,41 +191,108 @@ class DNSAPI:
return response.encode("utf-8") return response.encode("utf-8")
def add_data(self, url): def add_data(self, url):
domain = re.search(r'domain=([^&]+)', url)
ip = re.search(r'ip=([^&]+)', url) # parse and check validation
pubkey = re.search(r'pubkey=([^&]+)', url) domain, ip, pubkey, nodetype = parse_data(url)
if domain and ip and key:
domain = domain.group(1) if not check_data(url):
ip = ip.group(1)
pubkey = pubkey.group(1)
else:
return 400 return 400
# connect db
conn = sqlite3.connect(self.db_file) conn = sqlite3.connect(self.db_file)
c = conn.cursor() c = conn.cursor()
# Check if the data already exists # Check if the data already exists
c.execute( 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() existing_data = c.fetchall()
cursor.close()
conn.close()
if existing_data: if existing_data:
return 400 return 400
else: else:
# Insert the new data # Insert the new data
c.execute( 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 return 200
def delete_data(self, url): def delete_data(self, url):
m = re.search(r'domain=([^&]+)', url)
if m: # parse and check validation
domain = m.group(1) domain, ip, privkey, nodetype = parse_data(url)
print(domain)
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: else:
print('not matched') # Insert the new data
c.execute(
"INSERT INTO xiaomiandns (domain,ip,pubkey,nodetype,timestamp) VALUES (?,?,?,?,DATETIME('now'))", (domain, ip, pubkey, nodetype))
return 200 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__': if __name__ == '__main__':
# some config # some config
db_file = '../database/dns.db' db_file = '../database/dns.db'
DNS_port = 53 DNS_port = 53