Compare commits

..

10 Commits

Author SHA1 Message Date
f18ca40214 changed rolling-file to git2 2025-10-10 10:54:21 +08:00
b7aafc68d6 added mac_to_string 2024-10-06 15:44:33 +08:00
ea5d463216 changed the is_multi_broadcast 2024-10-06 15:35:51 +08:00
dd5610addc changed git from https to ssh 2024-07-30 10:52:08 +08:00
f320526f2d added encode error 2024-06-20 23:02:44 +08:00
f9a5cc3986 added protocol.md 2024-04-07 11:44:22 +08:00
71a10514f8 fix some api 2024-04-07 11:17:49 +08:00
bc63f284ff fix warning 2024-04-06 11:31:00 +08:00
107a7f8161 api set user's id from u64 to u32 2024-04-06 11:23:40 +08:00
1467b8ba68 fix some warning 2024-04-02 11:02:39 +08:00
10 changed files with 284 additions and 30 deletions

4
Cargo.toml Normal file → Executable file
View File

@ -13,8 +13,10 @@ dashmap = "5.5.3"
futures = "0.3.30"
# lazy_static = "1.4.0"
once_cell = "1.19.0"
prost = "0.12.6"
rand = "0.8.5"
rolling-file = { git = "https://git.asxalex.pw/rust/rolling-file" }
# rolling-file = { git = "https://git.asxalex.pw/rust/rolling-file" }
rolling-file = { git = "ssh://git@git2.asxalex.pw/rust/rolling-file.git" }
rsa = "0.9.6"
serde = { version = "1.0.196", features = ["derive"] }
serde_json = "1.0.113"

225
docs/http_api.md Normal file
View File

@ -0,0 +1,225 @@
# sdlan中心节点提供的api
sdlan中心节点需要被告知的内容主要有
* `register_token`——节点上线需要填写的token是一个`big int`64位的unsigned int
* 与`register_token`关联的信息,包括(但不一定需要)
* 过期时间
* 默认加入的网络
* 与`register_token`关联的用户信息,主要包括
* 用户的uuid为一个`big int`(64位的unsigned int)
* 用户是否启用
* 与用户uuid相互关联的信息比如该用户限制的连接的终端的个数等如果有的化
## 1.0. token创建通知
```
POST /token/created
{
"value": $unsigned_int,
// 过期时候的unix timestamp
"expire": $unsigned_int,
// 默认加入的网络的uuid
"defalut_network_id": "",
"user_info": {
// 关联的用户的id的数字
"user_id": $unsigned_int,
// 限制的节点的个数
"peer_limit": $int,
// 表明用户是否启用
"enabled": $bool,
}
}
```
返回值:
```
{
// 0表示成功其他数字代表失败
"code": $int,
// 如果成功message返回信息这里只返回一个ok
"message": "ok",
// 如果code不为0则error表示出错信息
"error": $string,
}
```
## 1.1. token删除
通过调用该接口告知supernode删除一个token。
```
POST /token/deleted
{
"value": $unsigned_int,
}
```
返回值:
```
{
// 0表示成功其他数字代表失败
"code": $int,
// 如果成功message返回信息这里只返回一个ok
"message": "ok",
// 如果code不为0则error表示出错信息
"error": $string,
}
```
## 1.2. 用户信息修改
用户信息修改,主要涉及到用户可以创建节点的个数,接口如下:
```
POST /user/updated
{
"user_id": $unsigned_int,
"peer_limit": $int,
"enabled": $bool
}
```
返回值:
```
{
// 0表示成功其他数字代表失败
"code": $int,
// 如果成功message返回信息这里只返回一个ok
"message": "ok",
// 如果code不为0则error表示出错信息
"error": $string,
}
```
## 1.3. 创建网络
创建网络是supernode端进行主要因为需要分配ip地址supernode端提供创建网络的api。
```
POST /network/create
{
// 网络的名称
"name": $string,
// 用户选择的ip地址段cidr格式的ip地址如10.20.1.0/24
// 如果不指定,则该字段不传递
"ip": $string,
// 创建的用户的id最后会用于表明该网络属于哪个用户
"user_id": $unsigned_int,
}
```
返回值:
```
{
// 0表示成功其他数字代表失败
"code": $int,
// 如果成功message返回信息这里只返回一个ok
"message": {
// 该网络的唯一标识
"uuid": $string
// 最后分配的cidr格式的ip地址如10.20.1.0/24
"ip": $string
},
// 如果code不为0则error表示出错信息
"error": $string,
}
```
## 1.4. 移动节点
移动节点用于将某个处于网络A中的节点移动到同一个用户的另一个网络B中
```
POST /peer/move
{
// 需要移动的节点的uuid
"peer_id": $string,
// 节点所处的以前的网络
"from_id": $string,
// 节点移动到的新网络的uuid
"to_id": $string,
// 属于哪个用户
"user_id": $unsigned_int,
}
```
返回值:
```json
{
// 0表示成功其他数字代表失败
"code": $int,
// 如果成功message返回信息这里只返回一个ok
"message": "ok",
// 如果code不为0则error表示出错信息
"error": $string
}
```
## 1.5. 授权节点
移动节点用于将某个处于未授权的节点,移动到同一个用户的某个网络中:
```
POST /peer/authorize
{
// 需要移动的节点的uuid
"peer_id": $string,
// 节点移动到的新网络的uuid
"to_id": $string,
// 属于哪个用户
"user_id": $unsigned_int,
}
```
返回值:
```json
{
// 0表示成功其他数字代表失败
"code": $int,
// 如果成功message返回信息这里只返回一个ok
"message": "ok",
// 如果code不为0则error表示出错信息
"error": $string
}
```
## 1.6. 取消节点授权
移动节点用于将某个处于某个网络的节点,取消授权,返回到为授权状态:
```
POST /peer/unauthorize
{
// 需要移动的节点的uuid
"peer_id": $string,
// 节点所处的以前的网络
"from_id": $string,
// 属于哪个用户
"user_id": $unsigned_int,
}
```
返回值:
```json
{
// 0表示成功其他数字代表失败
"code": $int,
// 如果成功message返回信息这里只返回一个ok
"message": "ok",
// 如果code不为0则error表示出错信息
"error": $string
}
```
# 2.0 sdlan中心节点使用的数据库结构
在中心节点收到客户端上线请求之后,如果这个节点是第一次上线,则会将相关信息插入到

View File

@ -19,13 +19,14 @@ sdlan协议的总体格式如下
消息头二进制格式如下:
```
+---------+----+-----+------+-----+
| version | id | ttl | flag | pc |
+---------+----+-----+------+-----+
+---------+----+-------+-----+------+-----+
| version | id | token | ttl | flag | pc |
+---------+----+-------+-----+------+-----+
```
其中version占用一个字节用于标识协议版本。id用于唯一标识某个客户端为长度为32字节的uuid之后的ttl占用一个字节当ttl为0则直接丢弃该数据包。后面的flag占用2字节用于标识数据包属性目前拥有的标识如下
其中version占用一个字节用于标识协议版本。id用于唯一标识某个客户端为长度为32字节的uuidtoken是用户生成的邀请码占用8个字节(64比特的大端序整型数字)。之后的ttl占用一个字节当ttl为0则直接丢弃该数据包。后面的flag占用2字节用于标识数据包属性目前拥有的标识如下
* `federation`: `0x0010`,表示这个数据包是从其他的supernode主动发送过来的比如其他supernode主动发送过来的RegisterSuper其他supernode收到情短请求之后主动将请求以Command的形式广播给其他supernode等。
* `from_sn`: `0x0020`,表示这个数据包是从服务端发送过来的。
* `socket`: `0x0040`表示这个数据包里面包含了有用的socket信息通常在服务端转发的时候有用
* `v6_info`: `0x0080`,表示这个数据包里面包含了ipv6信息专门用在packet数据包里面目前只有这个数据包使用了该标识

View File

@ -6,6 +6,10 @@ use serde_repr::{Deserialize_repr, Serialize_repr};
#[repr(u8)]
pub enum CommandType {
MovePeer = 1,
TokenCreated = 2,
TokenDeleted = 3,
UserChanged = 4,
UserDeleted = 5,
}
#[derive(Serialize, Deserialize)]
@ -21,7 +25,7 @@ pub struct MovePeerCommandReq {
pub peer_id: String,
pub new_ip: u32,
pub net_bit_len: u8,
pub user_id: u64,
pub user_id: u32,
}
#[derive(Serialize, Deserialize)]

View File

@ -46,16 +46,16 @@ impl<'a> Common<'a> {
}
pub fn from_slice(value: &'a [u8]) -> Result<(Common, &'a [u8])> {
const id_len: usize = 32;
const token_len: usize = 8;
const ID_LEN: usize = 32;
const TOKEN_LEN: usize = 8;
if value.len() < 7 + id_len + token_len {
if value.len() < 7 + ID_LEN + TOKEN_LEN {
return Err("common header length error".into());
}
let packet_id = u16::from_be_bytes(value[0..2].try_into().unwrap());
let version = value[2];
let v1 = &value[3..3 + id_len];
let v1 = &value[3..3 + ID_LEN];
let mut id = match std::str::from_utf8(v1) {
Ok(s) => s,
Err(e) => return Err(SDLanError::ConvertError(e.to_string())),
@ -64,14 +64,14 @@ impl<'a> Common<'a> {
id = id.trim_end_matches('\0');
let token = u64::from_be_bytes(
value[3 + id_len..3 + id_len + token_len]
value[3 + ID_LEN..3 + ID_LEN + TOKEN_LEN]
.try_into()
.unwrap(),
);
let ttl = value[3 + id_len + token_len];
let flags = BigEndian::read_u16(&value[4 + id_len + token_len..6 + id_len + token_len]);
let pc = value[6 + id_len + token_len];
let ttl = value[3 + ID_LEN + TOKEN_LEN];
let flags = BigEndian::read_u16(&value[4 + ID_LEN + TOKEN_LEN..6 + ID_LEN + TOKEN_LEN]);
let pc = value[6 + ID_LEN + TOKEN_LEN];
let common = Self {
packet_id,
@ -82,7 +82,7 @@ impl<'a> Common<'a> {
pc: pc.into(),
flags: flags,
};
Ok((common, &value[7 + id_len + token_len..]))
Ok((common, &value[7 + ID_LEN + TOKEN_LEN..]))
}
pub fn from_old_common(cmn: &'a Common) -> Self {

View File

@ -1,7 +1,5 @@
use std::{convert::From, net::AddrParseError, string::ParseError};
use aes::cipher::typenum::IsLessOrEqual;
pub type Result<T> = std::result::Result<T, SDLanError>;
#[derive(Debug)]
@ -12,6 +10,7 @@ pub enum SDLanError {
SerializeError(String),
EncryptError(String),
DBError(String),
PBError(String),
}
impl SDLanError {
@ -23,6 +22,7 @@ impl SDLanError {
Self::SerializeError(ref e) => e,
Self::EncryptError(ref e) => e,
Self::DBError(ref e) => e,
Self::PBError(ref e) => e,
}
}
}
@ -33,6 +33,12 @@ impl From<std::io::Error> for SDLanError {
}
}
impl From<prost::EncodeError> for SDLanError {
fn from(value: prost::EncodeError) -> Self {
Self::PBError(value.to_string())
}
}
impl From<&'static str> for SDLanError {
fn from(value: &'static str) -> Self {
Self::NormalError(value)

View File

@ -49,7 +49,7 @@ pub fn get_current_timestamp() -> u64 {
use crate::peer::SdlanSock;
use super::{gen_uuid, Result};
use super::{gen_uuid, Mac, Result, BROADCAST_MAC, IPV6_MULTICAST_MAC, MULTICAST_MAC};
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
@ -103,21 +103,24 @@ pub fn get_sdlan_sock_from_socketaddr(addr: SocketAddr) -> Result<SdlanSock> {
}
}
pub fn is_broadcast(ip: u32) -> bool {
ip == 0xffffffff
#[inline]
pub fn is_broadcast(mac: &Mac) -> bool {
*mac == BROADCAST_MAC
}
pub fn is_multicast(ip: u32) -> bool {
let first = ((ip >> 24) & 0xff) as u8;
first >= 224 && first <= 239
#[inline]
pub fn is_multicast(mac: &Mac) -> bool {
(mac[..3] == MULTICAST_MAC[..3]) && ((mac[3] >> 7) != 0x01)
}
pub fn is_multi_broadcast(ip: u32) -> bool {
if ip == 0xffffffff {
return true;
}
let first = ((ip >> 24) & 0xff) as u8;
first >= 224 && first <= 239
#[inline]
pub fn is_ipv6_multicast(mac: &Mac) -> bool {
mac[..2] == IPV6_MULTICAST_MAC[..2]
}
#[inline]
pub fn is_multi_broadcast(mac: &Mac) -> bool {
is_broadcast(mac) || is_multicast(mac) || is_ipv6_multicast(mac)
}
pub fn ip_to_string(ip: &u32) -> String {

View File

@ -3,6 +3,7 @@ mod error;
mod helper;
mod myaes;
mod myrsa;
mod mytype;
mod myuuid;
pub use encode_decode::*;
@ -13,6 +14,7 @@ pub use myrsa::{
gen_rsa_keys, load_private_key_file, load_public_key, load_public_key_file, rsa_decrypt,
rsa_encrypt,
};
pub use mytype::*;
pub use myuuid::*;
#[cfg(test)]

View File

@ -8,7 +8,6 @@ use rsa::{Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey};
use std::fs::File;
use std::io::Read;
use std::path::Path;
pub fn gen_rsa_keys(mut dirpath: &str) {
if dirpath.len() == 0 {

12
src/utils/mytype.rs Normal file
View File

@ -0,0 +1,12 @@
pub type Mac = [u8; 6];
pub const BROADCAST_MAC: Mac = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
pub const MULTICAST_MAC: Mac = [0x01, 0x00, 0x5E, 0x00, 0x00, 0x00]; /* First 3 bytes are meaningful */
pub const IPV6_MULTICAST_MAC: Mac = [0x33, 0x33, 0x00, 0x00, 0x00, 0x00]; /* First 2 bytes are meaningful */
pub fn mac_to_string(mac: &Mac) -> String {
format!(
"[{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}]",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
)
}