فهرست منبع

change code struct

Joyit 1 ماه پیش
والد
کامیت
e72294e09a
4فایلهای تغییر یافته به همراه205 افزوده شده و 0 حذف شده
  1. 7 0
      PROTO.md
  2. 147 0
      conn/host.go
  3. 16 0
      conn/host_test.go
  4. 35 0
      dail.go

+ 7 - 0
PROTO.md

@@ -0,0 +1,7 @@
+# 协议流程
+
+1. 连接后双方的第一个数据包是发送 IV,然后跟 KEY 异或作为混淆串
+2. 客户端先发送验证包,服务器验证通过后再发送服务端的验证包
+3. 建立连接后,如果需要建立代理,客户端发送建立代理的数据包
+4. 代理的验证需要客户端处理
+5. 代理建立后,服务端就只单纯转发代理数据

+ 147 - 0
conn/host.go

@@ -0,0 +1,147 @@
+package conn
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"regexp"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// 连接服务结构
+type HostInfo struct {
+	Proto    string    `json:"proto" yaml:"proto"`                 // 协议
+	Version  uint8     `json:"version" yaml:"version"`             // 版本
+	Host     string    `json:"host" yaml:"host"`                   // 连接的IP地址或者域名
+	Bind     string    `json:"bind,omitempty" yaml:"bind"`         // 绑定的地址
+	Port     uint16    `json:"port,omitempty" yaml:"port"`         // 连接的端口
+	Path     string    `json:"path,omitempty" yaml:"path"`         // 连接的路径
+	Hash     string    `json:"hash,omitempty" yaml:"hash"`         // 连接验证使用,格式 method:key
+	Proxy    bool      `json:"proxy,omitempty" yaml:"proxy"`       // 是否代理
+	Nat      bool      `json:"nat,omitempty" yaml:"nat"`           // 是否是前端nat的方式处理
+	Priority int16     `json:"priority,omitempty" yaml:"priority"` // 优先级,-1 表示不可用,0 表示最高优先级(为了兼容没有优先级的节点),1-100 表示优先级别,数值越高优先级越高,不过这是由应用端决定的规则
+	Errors   uint16    `json:"errors,omitempty" yaml:"errors"`     // 连接失败计数,如果成功了则重置为0
+	Updated  time.Time `json:"updated" yaml:"updated"`             // 节点信息刷新时间
+}
+
+// 从 url 中解析信息
+// url 格式:ws2://xor:s^7mv7L!Mrn8Y!vn@127.0.0.1:14541/wsv2?proxy=1
+// 仅支持客户端连接使用
+func ParseUrl(url string) (hostInfo *HostInfo, err error) {
+	mx := regexp.MustCompile(`^([a-z]+)([0-9]*)://([^#/\?]+)(/[\w\-/]+)?`).FindStringSubmatch(url)
+	if mx == nil {
+		return nil, errors.New("invalid url")
+	}
+	protocol := mx[1]
+	version, _ := strconv.Atoi(mx[2])
+	host := mx[3]
+	index := strings.LastIndex(host, "@")
+	hash := ""
+	if index >= 0 {
+		hash = host[0:index]
+		host = host[index+1:]
+	}
+	// 检查是否ipv6,解析出ip和端口
+	index = strings.Index(host, "]:")
+	port := 0
+	if index > 0 {
+		// ipv6 地址和端口
+		port, err = strconv.Atoi(host[index+2:])
+		if err != nil {
+			return nil, err
+		}
+		host = host[1:index]
+	} else {
+		hs := strings.Split(host, ":")
+		if len(hs) == 2 {
+			host = hs[0]
+			port, err = strconv.Atoi(hs[1])
+			if err != nil {
+				return nil, err
+			}
+		}
+	}
+	path := ""
+	if len(mx) > 4 {
+		path = mx[4]
+	}
+	hostInfo = &HostInfo{
+		Proto:   protocol,
+		Version: uint8(version),
+		Host:    host,
+		Port:    uint16(port),
+		Hash:    hash,
+		Path:    path,
+	}
+	// 查找是否代理
+	url = url[len(mx[0]):]
+	if regexp.MustCompile(`[\?&]proxy=1`).MatchString(url) {
+		hostInfo.Proxy = true
+	}
+	if regexp.MustCompile(`[\?&]nat=1`).MatchString(url) {
+		hostInfo.Nat = true
+	}
+	priorityM := regexp.MustCompile(`[\?&]priority=(-?\d+)`).FindStringSubmatch(url)
+	if priorityM != nil {
+		priority, err := strconv.Atoi(priorityM[1])
+		if err != nil {
+			return nil, err
+		}
+		hostInfo.Priority = int16(priority)
+	}
+	return hostInfo, nil
+}
+
+// 只输出客户端要连接的信息
+func (h *HostInfo) Url() string {
+	if h == nil {
+		// 避免空出错
+		return ""
+	}
+	var b bytes.Buffer
+	fmt.Fprintf(&b, "%s%d://", h.Proto, h.Version)
+	if h.Hash != "" {
+		b.WriteString(h.Hash)
+		b.WriteString("@")
+	}
+	if strings.Contains(h.Host, ":") {
+		// ipv6
+		b.WriteString("[")
+		b.WriteString(h.Host)
+		b.WriteString("]")
+	} else {
+		b.WriteString(h.Host)
+	}
+	if h.Port > 0 {
+		fmt.Fprintf(&b, ":%d", h.Port)
+	}
+	if h.Path != "" {
+		b.WriteString(h.Path)
+	}
+	param := make([]string, 0)
+	if h.Proxy {
+		param = append(param, "proxy=1")
+	}
+	if h.Nat {
+		param = append(param, "nat=1")
+	}
+	if h.Priority != 0 {
+		param = append(param, fmt.Sprintf("priority=%d", h.Priority))
+	}
+	if len(param) > 0 {
+		b.WriteString("?")
+		b.WriteString(strings.Join(param, "&"))
+	}
+	return b.String()
+}
+
+// 输出代表一个节点的关键信息
+func (h *HostInfo) Key() string {
+	if h == nil {
+		// 避免空出错
+		return ""
+	}
+	return fmt.Sprintf("%s%d://%s:%d%s", h.Proto, h.Version, h.Host, h.Port, h.Path)
+}

+ 16 - 0
conn/host_test.go

@@ -0,0 +1,16 @@
+package conn
+
+import "testing"
+
+func TestParseUrl(t *testing.T) {
+	url := "ws2://xor:s^7mv7L!Mrn8Y!vn@127.0.0.1:141/wsv2?proxy=1&priority=90"
+	hostInfo, err := ParseUrl(url)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	result := hostInfo.Url()
+	if result != url {
+		t.Errorf("want: %s, get: %s", url, result)
+	}
+}

+ 35 - 0
dail.go

@@ -0,0 +1,35 @@
+package tinymq
+
+import (
+	"fmt"
+	"log"
+	"net"
+	"strconv"
+
+	"git.me9.top/git/tinymq/config"
+	"git.me9.top/git/tinymq/conn"
+	"git.me9.top/git/tinymq/conn/tcp2"
+	"git.me9.top/git/tinymq/conn/ws2"
+)
+
+// 建立连接,输出实际代码实现的协议
+// 会一直阻塞直到连接结束
+func Dail(host *conn.HostInfo, cf *config.Config) (rawProto string, conn conn.Connect, err error) {
+	addr := net.JoinHostPort(host.Host, strconv.Itoa(int(host.Port)))
+	if host.Version == ws2.VERSION && (host.Proto == ws2.PROTO || host.Proto == ws2.PROTO_STL) {
+		rawProto = ws2.PROTO
+		if cf.PrintMsg {
+			log.Println("[ConnectToServer] connecting:", host.Proto, addr, host.Path, host.Hash)
+		}
+		conn, err = ws2.Dial(cf, host.Proto, addr, host.Path, host.Hash)
+	} else if host.Version == tcp2.VERSION && host.Proto == tcp2.PROTO {
+		rawProto = tcp2.PROTO
+		if cf.PrintMsg {
+			log.Println("[ConnectToServer] connecting:", host.Proto, addr, host.Hash)
+		}
+		conn, err = tcp2.Dial(cf, addr, host.Hash)
+	} else {
+		err = fmt.Errorf("not correct protocol and version found in: %+v", host)
+	}
+	return
+}