├── scripts ├── build_libs │ ├── build.sh │ ├── builddll.sh │ ├── builddll.bat │ ├── builddylib.sh │ └── buildso.sh ├── entrypoint.sh ├── install_remove │ ├── postremove.sh │ ├── postinstall.sh │ ├── preremove.sh │ └── preinstall.sh └── build.sh ├── .idea ├── .gitignore ├── misc.xml ├── vcs.xml ├── encodings.xml ├── modules.xml └── gateway-go.iml ├── tasks ├── tasks.go └── ipv6ClientServer.go ├── info └── info.go ├── gateway-go.yaml ├── .github ├── docs │ └── doc.md └── workflows │ ├── dockerhub-description.yml │ ├── release.yml │ └── test.yml ├── cmd └── main.go ├── chans └── chans.go ├── models ├── chansData.go └── models.go ├── config ├── global.go ├── config.go └── config_file.go ├── systemd └── gateway-go.service ├── .gitignore ├── Dockerfile ├── hash.sh ├── netservice ├── services │ ├── connect │ │ ├── tapTun │ │ │ ├── tapTun_freebsd.go │ │ │ ├── tapTun_darwin.go │ │ │ ├── tapTun_linux.go │ │ │ └── tapTun_windows.go │ │ ├── conn │ │ │ ├── udp.go │ │ │ ├── serialPort.go │ │ │ ├── ws.go │ │ │ ├── tcp.go │ │ │ └── ssh.go │ │ └── service │ │ │ ├── check.go │ │ │ ├── systemStatus_ios.go │ │ │ ├── serviceHdl.go │ │ │ ├── listenMulticastUDP.go │ │ │ ├── getIPv6Addr.go │ │ │ ├── scanPort.go │ │ │ ├── mDns_test.go │ │ │ ├── mdns │ │ │ ├── enter.go │ │ │ └── mDns.go │ │ │ └── systemStatus_others.go │ └── login │ │ └── login.go └── handle │ └── handle.go ├── utils ├── docker │ ├── docker_test.go │ └── docker.go ├── str │ ├── str.go │ └── logs.go ├── qr │ └── qrService.go └── login │ └── login.go ├── LICENSE ├── register └── register_service.go ├── client ├── build.bat └── lib.go ├── gateway-go.rb ├── gateway.pom ├── services ├── serverSession.go └── gatewayManager.go ├── go.mod ├── main.go ├── README.md ├── .goreleaser.yml └── go.sum /scripts/build_libs/build.sh: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml -------------------------------------------------------------------------------- /scripts/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | gateway-go $@ 3 | -------------------------------------------------------------------------------- /scripts/install_remove/postremove.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo postremove.sh 4 | #rm -rf /etc/gateway-go -------------------------------------------------------------------------------- /tasks/tasks.go: -------------------------------------------------------------------------------- 1 | package tasks 2 | 3 | func RunTasks() { 4 | go ipv6ServerTask() 5 | go ipv6ClientTask() 6 | } 7 | -------------------------------------------------------------------------------- /info/info.go: -------------------------------------------------------------------------------- 1 | package info 2 | 3 | var ( 4 | Version = "" 5 | Commit = "" 6 | Date = "" 7 | BuiltBy = "" 8 | ) 9 | -------------------------------------------------------------------------------- /gateway-go.yaml: -------------------------------------------------------------------------------- 1 | gatewayuuid: 2 | logconfig: 3 | enablestdout: true 4 | logfilepath: "" 5 | loginwithtokenmap: {} 6 | -------------------------------------------------------------------------------- /scripts/install_remove/postinstall.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo postinstall.sh 4 | systemctl enable gateway-go 5 | systemctl start gateway-go -------------------------------------------------------------------------------- /scripts/install_remove/preremove.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo preremove.sh: 4 | systemctl stop gateway-go 5 | systemctl disable gateway-go -------------------------------------------------------------------------------- /scripts/install_remove/preinstall.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo preinstall.sh: 4 | #systemctl stop gateway-go 5 | #systemctl disable gateway-go -------------------------------------------------------------------------------- /.github/docs/doc.md: -------------------------------------------------------------------------------- 1 | https://www.cnblogs.com/yjp372928571/p/12758614.html 2 | 3 | gpg -o my-gpg-prvi-key -a --export-secret-keys 4 | 5 | gpg --import my-gpg-prvi-key -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/OpenIoTHub/gateway-go/v2/client" 7 | ) 8 | 9 | func main() { 10 | client.Run() 11 | time.Sleep(500 * time.Second) 12 | } 13 | -------------------------------------------------------------------------------- /chans/chans.go: -------------------------------------------------------------------------------- 1 | package chans 2 | 3 | import "github.com/OpenIoTHub/gateway-go/v2/models" 4 | 5 | // ClientTaskChan 传入访问者的ipv6监听ip+端口,任务从本chan接受配置创建客户端 6 | var ClientTaskChan = make(chan models.Ipv6ClientHandleTask) 7 | -------------------------------------------------------------------------------- /models/chansData.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Ipv6ClientHandleTask struct { 4 | RunId string `json:"RunId"` 5 | Ipv6AddrIp string `json:"Ipv6AddrIp"` 6 | Ipv6AddrPort int `json:"Ipv6AddrPort"` 7 | } 8 | -------------------------------------------------------------------------------- /config/global.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | // Ipv6ListenTcpHandlePort ipv6监听访问端直接的连接, 4 | // 处理测试链接,测试链接显示可以连通后面有请求直接使用,当连接不通则使用其他连接 5 | // 这个端口在启动时开启,开启后将实际端口号存到这里,当接到访问者请求之后将该端口和ipv6地址发送到访问者 6 | var Ipv6ListenTcpHandlePort int = 0 7 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /systemd/gateway-go.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=OpenIoTHub Gateway 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | User=nobody 8 | Restart=on-failure 9 | RestartSec=5s 10 | ExecStart=/usr/local/bin/gateway-go -c /etc/gateway-go/gateway-go.yaml 11 | 12 | [Install] 13 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.aar 2 | *.jar 3 | *.exe 4 | /vendor/github.com/miekg/dns/gomobile.exe 5 | /dist 6 | /gateway-go 7 | /*.log 8 | /cn-hk.yaml 9 | /build/ 10 | *.zip 11 | /Client.xcframework/ 12 | /*.xcframework/ 13 | *.asc 14 | /target/ 15 | gateway-* 16 | client-* 17 | /vendor 18 | /gateway 19 | /gateway-go 20 | /main 21 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | LABEL name=gateway-go 3 | LABEL url=https://github.com/OpenIoTHub/OpenIoTHub 4 | RUN apk add --no-cache bash 5 | 6 | WORKDIR /app 7 | COPY gateway-go /app/ 8 | ENV TZ=Asia/Shanghai 9 | #mdns端口 10 | EXPOSE 5353/udp 11 | EXPOSE 34323 12 | ENTRYPOINT ["/app/gateway-go"] 13 | CMD ["-c", "/root/config.yaml"] -------------------------------------------------------------------------------- /hash.sh: -------------------------------------------------------------------------------- 1 | #提交openwrt版获取hash 2 | PKG_VERSION=2.0.10 3 | URL=https://codeload.github.com/OpenIoTHub/gateway-go/tar.gz/v${PKG_VERSION} 4 | wget ${URL} 5 | openssl sha256 v${PKG_VERSION} 6 | rm v${PKG_VERSION} 7 | #SHA2-256(v2.0.10)= 95de453e76a22ae69a1bc9c32d6930278980c98decbe7a511fd9870bcdf90d2d 8 | 9 | git commit --amend -m "gateway-go: update to 2.0.10" -s -------------------------------------------------------------------------------- /netservice/services/connect/tapTun/tapTun_freebsd.go: -------------------------------------------------------------------------------- 1 | package tapTun 2 | 3 | import ( 4 | "github.com/OpenIoTHub/utils/v2/models" 5 | "net" 6 | ) 7 | 8 | func NewTun(stream net.Conn, service *models.NewService) error { 9 | 10 | return NewTap(stream, service) 11 | } 12 | 13 | func NewTap(stream net.Conn, service *models.NewService) error { 14 | return nil 15 | } 16 | -------------------------------------------------------------------------------- /scripts/build_libs/builddll.sh: -------------------------------------------------------------------------------- 1 | #for build windows dll 2 | echo "building windows dll" 3 | #brew install mingw-w64 4 | #sudo apt-get install binutils-mingw-w64 5 | # shellcheck disable=SC2034 6 | export CGO_ENABLED=1 7 | export CC=x86_64-w64-mingw32-gcc 8 | export CXX=x86_64-w64-mingw32-g++ 9 | export GOOS=windows GOARCH=amd64 10 | go build -tags windows -ldflags=-w -trimpath -o ./build/windows/gateway_amd64.dll -buildmode=c-shared main.go -------------------------------------------------------------------------------- /models/models.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // 网关的配置文件 4 | type GatewayConfig struct { 5 | GatewayUUID string `json:"uuid"` 6 | LogConfig *LogConfig `json:"log"` 7 | HttpServicePort int `json:"http_service_port" yaml:"http_service_port"` 8 | LoginWithTokenMap map[string]string `json:"tokens"` 9 | } 10 | 11 | type LogConfig struct { 12 | EnableStdout bool 13 | LogFilePath string 14 | } 15 | -------------------------------------------------------------------------------- /netservice/services/connect/conn/udp.go: -------------------------------------------------------------------------------- 1 | package conn 2 | 3 | import ( 4 | "github.com/OpenIoTHub/utils/v2/io" 5 | "net" 6 | ) 7 | 8 | func JoinUDP(stream net.Conn, ip string, port int) error { 9 | addr := &net.UDPAddr{ 10 | IP: net.ParseIP(ip), 11 | Port: port, 12 | } 13 | //udp还是udp4 14 | c, err := net.DialUDP("udp4", nil, addr) 15 | if err != nil { 16 | return err 17 | } 18 | go io.Join(stream, c) 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /.idea/gateway-go.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /scripts/build_libs/builddll.bat: -------------------------------------------------------------------------------- 1 | ::for build windows dll 2 | echo "building windows dll" 3 | ::brew install mingw-w64 4 | ::sudo apt-get install binutils-mingw-w64 5 | ::https://winlibs.com/#download-release 6 | SET PATH=C:\mingw64\bin;%PATH% 7 | SET CGO_ENABLED=1 8 | SET CC=x86_64-w64-mingw32-gcc 9 | SET CXX=x86_64-w64-mingw32-g++ 10 | SET GOOS=windows 11 | SET GOARCH=amd64 12 | go build -tags windows -ldflags=-w -trimpath -o ./build/windows/gateway_amd64.dll -buildmode=c-shared main.go -------------------------------------------------------------------------------- /netservice/services/connect/conn/serialPort.go: -------------------------------------------------------------------------------- 1 | package conn 2 | 3 | import ( 4 | "github.com/OpenIoTHub/utils/v2/io" 5 | "github.com/OpenIoTHub/utils/v2/models" 6 | "github.com/jacobsa/go-serial/serial" 7 | "log" 8 | "net" 9 | ) 10 | 11 | func JoinSerialPort(stream net.Conn, m *models.ConnectSerialPort) error { 12 | options := serial.OpenOptions(*m) 13 | conn, err := serial.Open(options) 14 | if err != nil { 15 | log.Println(err.Error()) 16 | return err 17 | } 18 | go io.Join(stream, conn) 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /utils/docker/docker_test.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestGetContainersInfo(t *testing.T) { 9 | got, err := GetContainersInfo() 10 | if err != nil { 11 | t.Fatal(err) 12 | } 13 | for _, info := range got.Items { 14 | t.Log(info) 15 | fmt.Printf("%+v", info) 16 | //t.Log(info.Ports) 17 | //t.Log(info.Status) 18 | //t.Log(info.State) 19 | } 20 | } 21 | 22 | func TestGetContainersServices(t *testing.T) { 23 | clr := GetContainersServices() 24 | for _, info := range clr { 25 | t.Log(info) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /netservice/services/connect/conn/ws.go: -------------------------------------------------------------------------------- 1 | package conn 2 | 3 | import ( 4 | "github.com/OpenIoTHub/utils/v2/io" 5 | "golang.org/x/net/websocket" 6 | "net" 7 | ) 8 | 9 | func JoinWs(stream net.Conn, url, prot, orig string) error { 10 | ws, err := websocket.Dial(url, prot, orig) 11 | if err != nil { 12 | return err 13 | } 14 | go io.Join(stream, ws) 15 | return nil 16 | } 17 | 18 | func JoinWss(stream net.Conn, url, prot, orig string) error { 19 | ws, err := websocket.Dial(url, prot, orig) 20 | if err != nil { 21 | return err 22 | } 23 | go io.Join(stream, ws) 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /netservice/services/connect/conn/tcp.go: -------------------------------------------------------------------------------- 1 | package conn 2 | 3 | import ( 4 | "github.com/OpenIoTHub/utils/v2/io" 5 | "net" 6 | "strconv" 7 | "time" 8 | //"github.com/xtaci/smux" 9 | "crypto/tls" 10 | ) 11 | 12 | func JoinTCP(stream net.Conn, ip string, port int) error { 13 | c, err := net.DialTimeout("tcp", net.JoinHostPort(ip, strconv.Itoa(port)), time.Second*30) 14 | if err != nil { 15 | return err 16 | } 17 | go io.Join(stream, c) 18 | return nil 19 | } 20 | 21 | func JoinSTCP(stream net.Conn, ip string, port int) error { 22 | c, err := tls.Dial("tcp", net.JoinHostPort(ip, strconv.Itoa(port)), nil) 23 | if err != nil { 24 | return err 25 | } 26 | go io.Join(stream, c) 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /netservice/services/connect/service/check.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "time" 7 | //"github.com/xtaci/smux" 8 | "crypto/tls" 9 | ) 10 | 11 | // CheckTcpUdpTls 检查端口状态,如果端口可连接则状态为0,如果不可连接则状态为其他 12 | func CheckTcpUdpTls(connType, addr string) (int, string) { 13 | var c net.Conn = nil 14 | var err error 15 | defer func() { 16 | if c != nil { 17 | err = c.Close() 18 | if err != nil { 19 | log.Println(err.Error()) 20 | } 21 | } 22 | }() 23 | switch connType { 24 | case "tcp", "udp": 25 | c, err = net.DialTimeout(connType, addr, time.Second) 26 | case "tls": 27 | c, err = tls.Dial("tcp", addr, nil) 28 | default: 29 | return 1, "type not tcp,udp or tls" 30 | } 31 | if err != nil { 32 | return 1, err.Error() 33 | } 34 | return 0, "" 35 | } 36 | -------------------------------------------------------------------------------- /netservice/services/connect/service/systemStatus_ios.go: -------------------------------------------------------------------------------- 1 | //go:build ios 2 | 3 | package service 4 | 5 | import ( 6 | "encoding/json" 7 | "log" 8 | "net" 9 | 10 | "github.com/OpenIoTHub/utils/v2/models" 11 | "github.com/OpenIoTHub/utils/v2/msg" 12 | ) 13 | 14 | func GetSystemStatus(stream net.Conn, service *models.NewService) error { 15 | statMap := make(map[string]interface{}) 16 | statMap["code"] = 1 17 | statMap["message"] = "failed" 18 | rstByte, err := json.Marshal(statMap) 19 | if err != nil { 20 | log.Println("json.Marshal(statMap):") 21 | log.Println(err.Error()) 22 | } 23 | err = msg.WriteMsg(stream, &models.JsonResponse{Code: 1, Msg: "Success", Result: string(rstByte)}) 24 | if err != nil { 25 | log.Println("写消息错误:") 26 | log.Println(err.Error()) 27 | } 28 | return err 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/dockerhub-description.yml: -------------------------------------------------------------------------------- 1 | name: Update Docker Hub Description 2 | on: 3 | push: 4 | branches: 5 | - master 6 | paths: 7 | - README.md 8 | - .github/workflows/dockerhub-description.yml 9 | 10 | jobs: 11 | dockerHubDescription: 12 | runs-on: ubuntu-latest 13 | if: github.repository == 'OpenIoTHub/gateway-go' 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | 18 | - name: Docker Hub Description 19 | uses: peter-evans/dockerhub-description@v4 20 | with: 21 | username: ${{ secrets.DOCKER_USERNAME }} 22 | password: ${{ secrets.DOCKERHUB_TOKEN }} 23 | repository: ${{ secrets.DOCKER_USERNAME }}/gateway-go 24 | short-description: ${{ github.event.repository.description }} 25 | -------------------------------------------------------------------------------- /utils/str/str.go: -------------------------------------------------------------------------------- 1 | package str 2 | 3 | import ( 4 | "fmt" 5 | version2 "github.com/OpenIoTHub/gateway-go/v2/info" 6 | ) 7 | 8 | func BuildVersion(version, commit, date, builtBy string) string { 9 | var result = "" 10 | //TODO .. 11 | if version != "" { 12 | result = version 13 | } else { 14 | result = version2.Version 15 | } 16 | if commit != "" { 17 | result = fmt.Sprintf("%s\ncommit: %s", result, commit) 18 | } else { 19 | result = fmt.Sprintf("%s\ncommit: %s", result, version2.Commit) 20 | } 21 | if date != "" { 22 | result = fmt.Sprintf("%s\nbuilt at: %s", result, date) 23 | } else { 24 | result = fmt.Sprintf("%s\nbuilt at: %s", result, version2.Date) 25 | } 26 | if builtBy != "" { 27 | result = fmt.Sprintf("%s\nbuilt by: %s", result, builtBy) 28 | } else { 29 | result = fmt.Sprintf("%s\nbuilt by: %s", result, version2.BuiltBy) 30 | } 31 | return result 32 | } 33 | -------------------------------------------------------------------------------- /netservice/services/connect/tapTun/tapTun_darwin.go: -------------------------------------------------------------------------------- 1 | package tapTun 2 | 3 | import ( 4 | "github.com/OpenIoTHub/utils/v2/io" 5 | "github.com/OpenIoTHub/utils/v2/models" 6 | "github.com/OpenIoTHub/water" 7 | "log" 8 | "net" 9 | "os/exec" 10 | ) 11 | 12 | func NewTun(stream net.Conn, service *models.NewService) error { 13 | 14 | return NewTap(stream, service) 15 | } 16 | 17 | func NewTap(stream net.Conn, service *models.NewService) error { 18 | config := water.Config{ 19 | DeviceType: water.TAP, 20 | } 21 | ifce, err := water.New(config) 22 | if err != nil { 23 | return err 24 | } 25 | ifaceName := ifce.Name() 26 | log.Println("ifaceName", ifaceName) 27 | cmd := exec.Command("ifconfig", ifaceName, "192.168.69.1", "netmask", "255.255.255.0", "broadcast", "192.168.69.255", "up") 28 | err = cmd.Run() 29 | if err != nil { 30 | return err 31 | } 32 | go io.Join(stream, ifce) 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "github.com/OpenIoTHub/gateway-go/v2/models" 6 | uuid "github.com/satori/go.uuid" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | const ConfigFileName = "gateway-go.yaml" 12 | 13 | var ConfigFilePath = fmt.Sprintf("%s%s", "./", ConfigFileName) 14 | 15 | var GatewayLoginToken = "" 16 | 17 | const GRpcAddr = "" 18 | const GrpcPort = 0 19 | 20 | var ConfigMode = &models.GatewayConfig{ 21 | GatewayUUID: uuid.Must(uuid.NewV4()).String(), 22 | LogConfig: &models.LogConfig{ 23 | EnableStdout: true, 24 | LogFilePath: "", 25 | }, 26 | HttpServicePort: 34323, 27 | LoginWithTokenMap: make(map[string]string), 28 | } 29 | 30 | func init() { 31 | //是否是snapcraft应用,如果是则从snapcraft指定的工作目录保存配置文件 32 | appDataPath, havaAppDataPath := os.LookupEnv("SNAP_USER_DATA") 33 | if havaAppDataPath { 34 | ConfigFilePath = filepath.Join(appDataPath, ConfigFileName) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /netservice/services/connect/tapTun/tapTun_linux.go: -------------------------------------------------------------------------------- 1 | package tapTun 2 | 3 | import ( 4 | "fmt" 5 | "github.com/OpenIoTHub/utils/v2/io" 6 | "github.com/OpenIoTHub/utils/v2/models" 7 | "github.com/OpenIoTHub/water" 8 | "net" 9 | "os/exec" 10 | "time" 11 | ) 12 | 13 | func NewTun(stream net.Conn, service *models.NewService) error { 14 | 15 | return NewTap(stream, service) 16 | } 17 | 18 | func NewTap(stream net.Conn, service *models.NewService) error { 19 | config := water.Config{ 20 | DeviceType: water.TAP, 21 | } 22 | config.Name = "OpenIoTHub" + fmt.Sprintf("-%d", time.Now().UTC().Unix()) 23 | ifce, err := water.New(config) 24 | if err != nil { 25 | return err 26 | } 27 | cmd1 := exec.Command("ip", "addr", "add", "192.168.69.1/24", "dev", ifce.Name()) 28 | err = cmd1.Run() 29 | if err != nil { 30 | return err 31 | } 32 | cmd2 := exec.Command("ip", "link", "set", "dev", ifce.Name(), "up") 33 | err = cmd2.Run() 34 | if err != nil { 35 | return err 36 | } 37 | go io.Join(stream, ifce) 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /netservice/services/connect/tapTun/tapTun_windows.go: -------------------------------------------------------------------------------- 1 | package tapTun 2 | 3 | import ( 4 | "errors" 5 | "github.com/OpenIoTHub/utils/v2/io" 6 | "github.com/OpenIoTHub/utils/v2/models" 7 | "github.com/OpenIoTHub/water" 8 | "log" 9 | "net" 10 | "os/exec" 11 | ) 12 | 13 | func NewTun(stream net.Conn, service *models.NewService) error { 14 | 15 | return NewTap(stream, service) 16 | } 17 | 18 | func NewTap(stream net.Conn, service *models.NewService) error { 19 | config := water.Config{ 20 | DeviceType: water.TAP, 21 | } 22 | ifce, err := water.New(config) 23 | if err != nil { 24 | return err 25 | } 26 | ifaceName := ifce.Name() 27 | log.Println("虚拟网卡名称:", ifaceName) 28 | cmd := exec.Command("netsh", "interface", "ip", "set", "address", 29 | "name", "=", ifaceName, "source", "=", "static", "addr", "=", "192.168.69.1", "mask", "=", "255.255.255.0", "gateway", "=", "none") 30 | err = cmd.Run() 31 | if err != nil { 32 | return errors.New("请以管理员身份运行本软件!" + err.Error()) 33 | } 34 | go io.Join(stream, ifce) 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Open IoT Hub 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /netservice/services/connect/service/serviceHdl.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/OpenIoTHub/gateway-go/v2/netservice/services/connect/service/mdns" 5 | "github.com/OpenIoTHub/gateway-go/v2/netservice/services/connect/tapTun" 6 | "github.com/OpenIoTHub/utils/v2/models" 7 | "github.com/OpenIoTHub/utils/v2/msg" 8 | "net" 9 | ) 10 | 11 | func ServiceHdl(stream net.Conn, service *models.NewService) error { 12 | switch service.Type { 13 | case "tap": 14 | return tapTun.NewTap(stream, service) 15 | case "tun": 16 | return tapTun.NewTun(stream, service) 17 | case "mDNSFind": 18 | return mdns.MdnsManager.FindAllmDNS(stream, service) 19 | case "scanPort": 20 | return ScanPort(stream, service) 21 | case "ListenMulticastUDP": 22 | return ListenMulticastUDP(stream, service) 23 | case "GetSystemStatus": 24 | return GetSystemStatus(stream, service) 25 | case "GetIPv6Addr": 26 | return GetIPv6Addr(stream, service) 27 | default: 28 | err := msg.WriteMsg(stream, &models.JsonResponse{Code: 1, Msg: "Failed", Result: "Unknown service type"}) 29 | if err != nil { 30 | return err 31 | } 32 | return stream.Close() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /config/config_file.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "github.com/OpenIoTHub/gateway-go/v2/models" 6 | "gopkg.in/yaml.v3" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | ) 11 | 12 | // 将配置写入指定的路径的文件 13 | func WriteConfigFile(ConfigMode *models.GatewayConfig, path string) (err error) { 14 | configByte, err := yaml.Marshal(ConfigMode) 15 | if err != nil { 16 | log.Println(err.Error()) 17 | return 18 | } 19 | if os.WriteFile(path, configByte, 0644) == nil { 20 | return 21 | } 22 | return 23 | } 24 | 25 | func InitConfigFile() { 26 | // 生成配置文件模板 27 | err := os.MkdirAll(filepath.Dir(ConfigFilePath), 0644) 28 | if err != nil { 29 | return 30 | } 31 | err = WriteConfigFile(ConfigMode, ConfigFilePath) 32 | if err == nil { 33 | fmt.Println("config created") 34 | return 35 | } 36 | log.Println("写入配置文件模板出错,请检查本程序是否具有写入权限!或者手动创建配置文件。") 37 | log.Println(err.Error()) 38 | } 39 | 40 | //func UseGateWayToken() { 41 | // //使用服务器签发的Token登录 42 | // err := GatewayManager.AddServer(GatewayLoginToken) 43 | // if err != nil { 44 | // log.Printf(err.Error()) 45 | // log.Printf("登陆失败!请重新登陆。") 46 | // return 47 | // } 48 | // log.Printf("登陆成功!\n") 49 | //} 50 | -------------------------------------------------------------------------------- /register/register_service.go: -------------------------------------------------------------------------------- 1 | package register 2 | 3 | import ( 4 | "net" 5 | "sync" 6 | 7 | "github.com/OpenIoTHub/gateway-go/v2/utils/docker" 8 | "github.com/OpenIoTHub/utils/v2/models" 9 | ) 10 | 11 | var registeredServices = make([]models.MDNSResult, 0) 12 | var registeredServicesLock sync.RWMutex 13 | 14 | // RegisterService 注册mdns服务,TODO扫描端口并注册 15 | func RegisterService(instance, service, domain, hostname string, port int, text []string, TTL uint32, AddrIPv4, AddrIPv6 []net.IP) (err error) { 16 | registeredServicesLock.Lock() 17 | defer registeredServicesLock.Unlock() 18 | registeredServices = append(registeredServices, models.MDNSResult{ 19 | Instance: instance, 20 | Service: service, 21 | Domain: domain, 22 | HostName: hostname, 23 | Port: port, 24 | Text: text, 25 | TTL: TTL, 26 | AddrIPv4: AddrIPv4, 27 | AddrIPv6: AddrIPv6, 28 | }) 29 | return 30 | } 31 | 32 | func GetRegisteredServices() (services []models.MDNSResult) { 33 | registeredServicesLock.RLock() 34 | defer registeredServicesLock.RUnlock() 35 | //添加docker服务 36 | tmp := make([]models.MDNSResult, len(registeredServices)) 37 | copy(tmp, registeredServices) 38 | tmp = append(tmp, docker.GetContainersServices()...) 39 | return tmp 40 | } 41 | -------------------------------------------------------------------------------- /client/build.bat: -------------------------------------------------------------------------------- 1 | gomobile bind -target=android -o gateway.aar 2 | :: gpg --clearsign gateway-0.0.1.aar 3 | :: mvn gpg:sign-and-deploy-file -Durl=https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=ossrh -Dpackaging=aar -DpomFile=gateway-0.0.2.pom -Dfile=gateway-0.0.2.aar -Dsources=gateway-0.0.2.jar -Djavadoc=gateway-0.0.2.jar 4 | :: mvn deploy:deploy-file -Dfile=client.aar -DgroupId=cloud.iothub -DartifactId=gateway -Dversion=0.0.1 -Dpackaging=aar -DrepositoryId=github -Durl=https://maven.pkg.github.com/OpenIoTHub/gateway-go/v2 5 | gomobile bind -ldflags '-w -s -extldflags "-lresolve"' --target=ios,macos,iossimulator 6 | ::gomobile bind -ldflags '-w -s -extldflags "-lresolve"' --target=ios,macos,iossimulator -o OpenIoTHubGateway.xcframework ./client 7 | ::https://gitee.com/OpenIoThub/mobile-lib-podspec 8 | ::git tag -a 0.0.1 -m '0.0.1' 9 | ::git pus --tags 10 | ::pod trunk push ./OpenIoTHubGateway.podspec --skip-import-validation --allow-warnings 11 | 12 | mvn gpg:sign-and-deploy-file -DrepositoryId=ossrh -Dfile=gateway.aar -DpomFile=gateway.pom -Durl=https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 13 | mvn deploy:deploy-file -Dfile=client.aar -DgroupId=cloud.iothub -DartifactId=gateway -Dversion=0.0.1 -Dpackaging=aar -DrepositoryId=github -Durl=https://maven.pkg.github.com/OpenIoTHub/gateway-go/v2 14 | -------------------------------------------------------------------------------- /utils/str/logs.go: -------------------------------------------------------------------------------- 1 | package str 2 | 3 | import "fmt" 4 | 5 | //echo " "; 6 | //echo " ,-----. ,--. ,--------.,--. ,--. ,--. "; 7 | //echo "' .-. ' ,---. ,---. ,--,--, | | ,---.'--. .--'| '--' |,--.,--.| |-. "; 8 | //echo "| | | || .-. || .-. :| \| || .-. | | | | .--. || || || .-. ' "; 9 | //echo "' '-' '| '-' '\ --.| || || |' '-' ' | | | | | |' '' '| \`-' | "; 10 | //echo " \`-----' | |-' \`----'\`--''--'\`--' \`---' \`--' \`--' \`--' \`----' \`---' "; 11 | //echo " \`--' "; 12 | 13 | func PrintOpenIoTHubLogo() { 14 | fmt.Println(" ") 15 | fmt.Println(" ,-----. ,--. ,--------.,--. ,--. ,--. ") 16 | fmt.Println("' .-. ' ,---. ,---. ,--,--, | | ,---.'--. .--'| '--' |,--.,--.| |-. ") 17 | fmt.Println("| | | || .-. || .-. :| \\| || .-. | | | | .--. || || || .-. ' ") 18 | fmt.Println("' '-' '| '-' '\\ --.| || || |' '-' ' | | | | | |' '' '| `-' | ") 19 | fmt.Println(" `-----' | |-' `----'`--''--'`--' `---' `--' `--' `--' `----' `---' ") 20 | fmt.Println(" `--' form https://github.com/OpenIoTHub/gateway-go/v2 ") 21 | } 22 | -------------------------------------------------------------------------------- /netservice/services/connect/service/listenMulticastUDP.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/OpenIoTHub/utils/v2/io" 5 | "github.com/OpenIoTHub/utils/v2/models" 6 | "log" 7 | "net" 8 | "strconv" 9 | ) 10 | 11 | func ListenMulticastUDP(stream net.Conn, service *models.NewService) error { 12 | host, port, err := net.SplitHostPort(service.Config) 13 | if err != nil { 14 | return err 15 | } 16 | portInt, err := strconv.Atoi(port) 17 | if err != nil { 18 | log.Println(err.Error()) 19 | return err 20 | } 21 | l, err := net.ListenMulticastUDP("udp4", nil, &net.UDPAddr{ 22 | IP: net.ParseIP(host), 23 | Port: portInt, 24 | }) 25 | if err != nil { 26 | log.Println(err.Error()) 27 | return err 28 | } 29 | 30 | go io.Join(stream, l) 31 | 32 | //var message = make(chan []byte, 100) 33 | //go func() { 34 | // buf := make([]byte, 2048) 35 | // for { 36 | // size, _, err := l.ReadFromUDP(buf) 37 | // if err != nil { 38 | // log.Println(err) 39 | // return 40 | // } 41 | // log.Println(string(buf)) 42 | // go func() { 43 | // if size > 0 { 44 | // msg := make([]byte, size) 45 | // copy(msg, buf[0:size]) 46 | // message <- msg 47 | // } 48 | // }() 49 | // } 50 | //}() 51 | //go func() { 52 | // for { 53 | // msgin := <-message 54 | // _, err = stream.Write(msgin) 55 | // if err != nil { 56 | // return 57 | // } 58 | // time.Sleep(time.Second) 59 | // } 60 | //}() 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /netservice/services/connect/conn/ssh.go: -------------------------------------------------------------------------------- 1 | package conn 2 | 3 | import ( 4 | "golang.org/x/crypto/ssh" 5 | "net" 6 | "strconv" 7 | ) 8 | 9 | //func JoinSSHold(stream *mux.Stream,targetIP string,targetPort int,userName,passWord string) error { 10 | // sshConn,err := ConnectToSSH(targetIP,targetPort,userName,passWord) 11 | // if err!=nil{ 12 | // return err 13 | // } 14 | // go io.Join(stream,sshConn) 15 | // return nil 16 | //} 17 | 18 | func JoinSSH(stream net.Conn, remoteIP string, remotePort int, userName, passWord string) (err error) { 19 | client, err := ssh.Dial("tcp", net.JoinHostPort(remoteIP, strconv.Itoa(remotePort)), &ssh.ClientConfig{ 20 | User: userName, 21 | Auth: []ssh.AuthMethod{ssh.Password(passWord)}, 22 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 23 | }) 24 | if err != nil { 25 | return err 26 | } 27 | session, err := client.NewSession() 28 | if err != nil { 29 | return err 30 | } 31 | defer session.Close() 32 | session.Stdout = stream 33 | session.Stderr = stream 34 | session.Stdin = stream 35 | //go func() { 36 | // time.Sleep(time.Second) 37 | // Join(session.Stdin,session.Stdout,stream) 38 | //}() 39 | 40 | modes := ssh.TerminalModes{ 41 | ssh.ECHO: 1, 42 | ssh.TTY_OP_ISPEED: 14400, 43 | ssh.TTY_OP_OSPEED: 14400, 44 | } 45 | err = session.RequestPty("xterm", 25, 80, modes) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | err = session.Shell() 51 | if err != nil { 52 | return err 53 | } 54 | session.Wait() 55 | return 56 | } 57 | -------------------------------------------------------------------------------- /utils/docker/docker.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "strings" 8 | "time" 9 | 10 | "github.com/OpenIoTHub/utils/v2/models" 11 | "github.com/moby/moby/client" 12 | ) 13 | 14 | // GetContainersInfo 获取当前主机上所有Docker容器的信息 15 | func GetContainersInfo() (client.ContainerListResult, error) { 16 | // 创建Docker客户端 17 | cli, err := client.New(client.FromEnv) 18 | if err != nil { 19 | return client.ContainerListResult{}, fmt.Errorf("failed to create docker client: %v", err) 20 | } 21 | defer cli.Close() 22 | 23 | // 设置超时上下文 24 | ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 25 | defer cancel() 26 | 27 | // 获取容器列表(包括停止的容器) 28 | return cli.ContainerList(ctx, client.ContainerListOptions{ 29 | All: true, 30 | }) 31 | } 32 | 33 | func GetContainersServices() (rst []models.MDNSResult) { 34 | clr, err := GetContainersInfo() 35 | if err != nil { 36 | return []models.MDNSResult{} 37 | } 38 | for _, items := range clr.Items { 39 | port := 0 40 | for _, portInfo := range items.Ports { 41 | if portInfo.Type == "tcp" { 42 | port = int(portInfo.PublicPort) 43 | } 44 | } 45 | name := "Docker Service" 46 | for _, n := range items.Names { 47 | name = strings.ReplaceAll(n, "/", "") 48 | break 49 | } 50 | rst = append(rst, models.MDNSResult{ 51 | Instance: items.ID, 52 | Service: "_http._tcp", 53 | Domain: "local", 54 | HostName: "localhost", 55 | Port: port, 56 | Text: []string{fmt.Sprintf("name=%s", name), fmt.Sprintf("id=%s", items.ID)}, 57 | TTL: 0, 58 | AddrIPv4: []net.IP{net.ParseIP("127.0.0.1")}, 59 | AddrIPv6: []net.IP{}, 60 | }) 61 | } 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /netservice/services/connect/service/getIPv6Addr.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/OpenIoTHub/gateway-go/v2/chans" 6 | "github.com/OpenIoTHub/gateway-go/v2/config" 7 | models2 "github.com/OpenIoTHub/gateway-go/v2/models" 8 | "github.com/OpenIoTHub/getip/v2/iputils" 9 | "github.com/OpenIoTHub/utils/v2/models" 10 | "github.com/OpenIoTHub/utils/v2/msg" 11 | "log" 12 | "net" 13 | ) 14 | 15 | // GetIPv6Addr 传过来一个ipv6Addr+port返回一个ipv6Addr+port,传过来的ipv6Addr+port用于这边通过channel通知连接, 16 | // 访问端获取的ipv6Addr+port用于先校验连通性再有连接需求的时候使用连接 17 | func GetIPv6Addr(stream net.Conn, service *models.NewService) error { 18 | //从service读取访问者监听ipv6端口号,这里将ipv6地址+port通过chan发送到任务创建连接并处理请求 19 | var remoteIpv6ServerConfig models2.Ipv6ClientHandleTask 20 | err := json.Unmarshal([]byte(service.Config), &remoteIpv6ServerConfig) 21 | if err != nil { 22 | log.Println("json.Unmarshal([]byte(service.Config), &config):" + err.Error()) 23 | return err 24 | } 25 | chans.ClientTaskChan <- remoteIpv6ServerConfig 26 | // 获取ipv6公网地址,可能为空字符串代表没有或者没获取到 27 | var ipv6Addr = iputils.GetMyPublicIpv6() 28 | ipv6Info := models2.Ipv6ClientHandleTask{} 29 | //访问者只要保存提供服务的ipv6地址+端口,有连接请求时创建连接 30 | ipv6Info.Ipv6AddrIp = ipv6Addr 31 | ipv6Info.Ipv6AddrPort = config.Ipv6ListenTcpHandlePort 32 | rstByte, err := json.Marshal(ipv6Info) 33 | if err != nil { 34 | log.Println("json.Marshal(ipv6Map):") 35 | log.Println(err.Error()) 36 | return err 37 | } 38 | //log.Println(string(rstByte)) 39 | err = msg.WriteMsg(stream, &models.JsonResponse{Code: 0, Msg: "Success", Result: string(rstByte)}) 40 | if err != nil { 41 | stream.Close() 42 | log.Println("写消息错误:") 43 | log.Println(err.Error()) 44 | } 45 | return err 46 | } 47 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | export CGO_ENABLED=0 2 | export GO111MODULE=on 3 | export GOARCH=386 4 | export GOOS=windows 5 | go build -ldflags -w ../ 6 | mv gateway-go.exe windows386.exe 7 | upx windows386.exe 8 | export GOOS=linux 9 | go build -ldflags -w ../ 10 | mv gateway-go linux386 11 | upx linux386 12 | export GOOS=freebsd 13 | go build -ldflags -w ../ 14 | mv gateway-go freebsd386 15 | upx freebsd386 16 | export GOOS=darwin 17 | go build -ldflags -w ../ 18 | mv gateway-go darwin386 19 | upx darwin386 20 | 21 | export GOARCH=amd64 22 | export GOOS=windows 23 | go build -ldflags -w ../ 24 | mv gateway-go.exe windowsAmd64.exe 25 | upx windowsAmd64.exe 26 | export GOOS=linux 27 | go build -ldflags -w ../ 28 | mv gateway-go linuxAMD64 29 | upx linuxAMD64 30 | export GOOS=freebsd 31 | go build -ldflags -w ../ 32 | mv gateway-go freebsdAMD64 33 | upx freebsdAMD64 34 | export GOOS=darwin 35 | go build -ldflags -w ../ 36 | mv gateway-go darwinAMD64 37 | upx darwinAMD64 38 | 39 | export GOARCH=arm 40 | export GOOS=linux 41 | go build -ldflags -w ../ 42 | mv gateway-go LinuxArm 43 | upx LinuxArm 44 | 45 | export GOARCH=mips64le 46 | export GOOS=linux 47 | go build -ldflags -w ../ 48 | mv gateway-go LinuxMips64le 49 | upx LinuxMips64le 50 | 51 | export GOARCH=mips64 52 | export GOOS=linux 53 | go build -ldflags -w ../ 54 | mv gateway-go LinuxMips64 55 | upx LinuxMips64 56 | 57 | export GOARCH=mipsle 58 | export GOOS=linux 59 | export CGO_ENABLED=0 60 | export GOMIPS=softfloat 61 | go build -ldflags -w ../ 62 | mv gateway-go LinuxMipsle 63 | upx LinuxMipsle 64 | 65 | export GOARCH=mips 66 | export GOOS=linux 67 | export CGO_ENABLED=0 68 | export GOMIPS=softfloat 69 | go build -ldflags -w ../ 70 | mv gateway-go LinuxMips 71 | upx LinuxMips 72 | 73 | export GOARCH=amd64 74 | export GOOS=windows -------------------------------------------------------------------------------- /utils/qr/qrService.go: -------------------------------------------------------------------------------- 1 | package qr 2 | 3 | import ( 4 | "fmt" 5 | qrcode "github.com/skip2/go-qrcode" 6 | "log" 7 | ) 8 | 9 | const ( 10 | GatewayQRByIdForAddTemplate = "https://iothub.cloud/a/g?id=%s" 11 | GatewayQRByIdAndHostForAddTemplate = "https://iothub.cloud/a/g?id=%s&host=%s" 12 | STDHost = "guonei.servers.iothub.cloud" 13 | ) 14 | 15 | // 通过jwt展示二维码 16 | func DisplayQRCodeById(id, host string) (err error) { 17 | qrContent := fmt.Sprintf(GatewayQRByIdAndHostForAddTemplate, id, host) 18 | if host == "" || host == STDHost { 19 | qrContent = fmt.Sprintf(GatewayQRByIdForAddTemplate, id) 20 | } else { 21 | qrContent = fmt.Sprintf(GatewayQRByIdAndHostForAddTemplate, id, host) 22 | } 23 | qrCode, err := qrcode.New(qrContent, qrcode.Low) 24 | if err != nil { 25 | log.Println(err) 26 | return 27 | } 28 | ascii := qrCode.ToSmallString(false) 29 | fmt.Println(fmt.Sprintf("Use OpenIoTHub to scan the following QR code and add a gateway(%s)", id)) 30 | fmt.Println(ascii) 31 | fmt.Println("If the above QR code cannot be scanned, please open the following link and scan the QR code in page:") 32 | if host == "" || host == STDHost { 33 | fmt.Println(fmt.Sprintf("https://api.iot-manager.iothub.cloud/v1/displayGatewayQRCodeById?id=%s", id)) 34 | } else { 35 | fmt.Println(fmt.Sprintf("https://api.iot-manager.iothub.cloud/v1/displayGatewayQRCodeById?id=%s&host=%s", id, host)) 36 | } 37 | return 38 | } 39 | 40 | func GetQrById(id string) (qr *qrcode.QRCode, err error) { 41 | qrStr := fmt.Sprintf(GatewayQRByIdForAddTemplate, id) 42 | return qrcode.New(qrStr, qrcode.Low) 43 | } 44 | 45 | func GetQrByIdAndHost(id, host string) (qr *qrcode.QRCode, err error) { 46 | qrStr := fmt.Sprintf(GatewayQRByIdAndHostForAddTemplate, id, host) 47 | return qrcode.New(qrStr, qrcode.Low) 48 | } 49 | -------------------------------------------------------------------------------- /utils/login/login.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | "context" 5 | "crypto/tls" 6 | "errors" 7 | "log" 8 | "net/url" 9 | 10 | "github.com/OpenIoTHub/gateway-go/v2/config" 11 | "github.com/OpenIoTHub/gateway-go/v2/services" 12 | "github.com/OpenIoTHub/gateway-go/v2/utils/qr" 13 | pb "github.com/OpenIoTHub/openiothub_grpc_api/pb-go/proto/manager" 14 | "google.golang.org/grpc" 15 | "google.golang.org/grpc/credentials" 16 | "google.golang.org/grpc/metadata" 17 | "google.golang.org/protobuf/types/known/emptypb" 18 | ) 19 | 20 | const ( 21 | IoTManagerAddr = "api.iot-manager.iothub.cloud:50051" 22 | ) 23 | 24 | // 自动创建jwt并登陆,并展示二维码 25 | func AutoLoginAndDisplayQRCode() (err error) { 26 | tlsConfig := grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})) 27 | conn, err := grpc.NewClient(IoTManagerAddr, tlsConfig) 28 | if err != nil { 29 | log.Println("grpc.NewClient:", err) 30 | return 31 | } 32 | defer conn.Close() 33 | c := pb.NewPublicApiClient(conn) 34 | md := metadata.Pairs() 35 | ctx := metadata.NewOutgoingContext(context.Background(), md) 36 | rst, err := c.GenerateJwtQRCodePair(ctx, &emptypb.Empty{}) 37 | if err != nil { 38 | log.Println(err) 39 | return 40 | } 41 | err = services.GatewayManager.AddServer(rst.GatewayJwt) 42 | if err != nil { 43 | log.Println(err) 44 | return 45 | } 46 | qrs, err := url.ParseRequestURI(rst.QRCodeForMobileAdd) 47 | if err != nil { 48 | log.Println(err) 49 | return 50 | } 51 | host := qrs.Query().Get("host") 52 | runId := qrs.Query().Get("id") 53 | if runId == "" { 54 | err = errors.New("url id is empty in QRCodeForMobileAdd") 55 | return 56 | } 57 | config.ConfigMode.LoginWithTokenMap[runId] = rst.GatewayJwt 58 | err = config.WriteConfigFile(config.ConfigMode, config.ConfigFilePath) 59 | if err != nil { 60 | log.Println(err) 61 | } 62 | return qr.DisplayQRCodeById(runId, host) 63 | } 64 | -------------------------------------------------------------------------------- /netservice/services/connect/service/scanPort.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/OpenIoTHub/utils/v2/models" 6 | "github.com/OpenIoTHub/utils/v2/msg" 7 | "log" 8 | "net" 9 | ) 10 | 11 | func loop(startport, endport int, inport chan int) { 12 | for i := startport; i <= endport; i++ { 13 | inport <- i 14 | } 15 | } 16 | 17 | func scanner(inport, outport, out chan int, ip net.IP, endport int) { 18 | for { 19 | in := <-inport 20 | tcpaddr := &net.TCPAddr{IP: ip, Port: in} 21 | conn, err := net.DialTCP("tcp", nil, tcpaddr) 22 | if err != nil { 23 | outport <- 0 24 | } else { 25 | outport <- in 26 | conn.Close() 27 | } 28 | if in == endport { 29 | out <- in 30 | } 31 | } 32 | } 33 | 34 | func ScanPort(stream net.Conn, service *models.NewService) error { 35 | //decode json 36 | var config *models.ScanPort 37 | //var rst *models.ScanPortResult 38 | err := json.Unmarshal([]byte(service.Config), &config) 39 | if err != nil { 40 | return err 41 | } 42 | inport := make(chan int) 43 | outport := make(chan int) 44 | out := make(chan int) 45 | collect := []int{} 46 | go loop(config.StartPort, config.EndPort, inport) 47 | for { 48 | needBreak := false 49 | select { 50 | case <-out: 51 | //log.Println(collect) 52 | needBreak = true 53 | default: 54 | ip := net.ParseIP(config.Host) 55 | go scanner(inport, outport, out, ip, config.EndPort) 56 | port := <-outport 57 | if port != 0 { 58 | collect = append(collect, port) 59 | } 60 | } 61 | if needBreak { 62 | break 63 | } 64 | } 65 | rstByte, err := json.Marshal(&collect) 66 | if err != nil { 67 | log.Println(err.Error()) 68 | return err 69 | } 70 | //log.Println(string(rstByte)) 71 | err = msg.WriteMsg(stream, &models.JsonResponse{Code: 0, Msg: "Success", Result: string(rstByte)}) 72 | if err != nil { 73 | log.Println("写消息错误:") 74 | log.Println(err.Error()) 75 | } 76 | return err 77 | } 78 | -------------------------------------------------------------------------------- /scripts/build_libs/builddylib.sh: -------------------------------------------------------------------------------- 1 | #for build MacOS/iOS dylib 2 | echo "building MacOS/iOS dylib" 3 | #iOS 4 | # shellcheck disable=SC2155 5 | export CFLAGS="-arch arm64 -miphoneos-version-min=9.0 -isysroot "$(xcrun -sdk iphoneos --show-sdk-path) 6 | # shellcheck disable=SC2155 7 | export CGO_LDFLAGS="-arch arm64 -miphoneos-version-min=9.0 -isysroot "$(xcrun -sdk iphoneos --show-sdk-path) 8 | CGO_ENABLED=1 GOARCH=arm64 GOOS=ios CC="clang $CFLAGS $CGO_LDFLAGS" go build -tags ios -ldflags=-w -trimpath -o "./build/ios/gateway_arm64.a" -buildmode c-archive main.go 9 | 10 | # shellcheck disable=SC2155 11 | export CFLAGS="-arch x86_64 -miphoneos-version-min=9.0 -isysroot "$(xcrun -sdk iphonesimulator --show-sdk-path) 12 | # shellcheck disable=SC2155 13 | export CGO_LDFLAGS="-arch x86_64 -miphoneos-version-min=9.0 -isysroot "$(xcrun -sdk iphonesimulator --show-sdk-path) 14 | CGO_ENABLED=1 GOARCH=amd64 GOOS=ios CC="clang $CFLAGS $CGO_LDFLAGS" go build -tags ios -ldflags=-w -trimpath -o "./build/ios/gateway_simulator_amd64.a" -buildmode c-archive main.go 15 | 16 | lipo -create ./build/ios/gateway_arm64.a ./build/ios/gateway_simulator_amd64.a -output ./build/ios/libgateway_amd64_arm64.a 17 | 18 | #Mac 19 | # shellcheck disable=SC2155 20 | export CFLAGS="-mmacosx-version-min=10.9 -isysroot "$(xcrun -sdk macosx --show-sdk-path) 21 | # shellcheck disable=SC2155 22 | export CGO_LDFLAGS="-mmacosx-version-min=10.9 -isysroot "$(xcrun -sdk macosx --show-sdk-path) 23 | #CGO_ENABLED=1 GOARCH=amd64 GOOS=darwin CC="clang $CFLAGS $CGO_LDFLAGS" go build -tags macosx -ldflags=-w -trimpath -o "test.a" -buildmode c-archive 24 | CGO_ENABLED=1 GOARCH=amd64 GOOS=darwin CC="clang $CFLAGS $CGO_LDFLAGS" go build -tags macosx -ldflags=-w -trimpath -o "./build/macos/gateway_amd64.a" -buildmode c-archive main.go 25 | CGO_ENABLED=1 GOARCH=arm64 GOOS=darwin CC="clang $CFLAGS $CGO_LDFLAGS" go build -tags macosx -ldflags=-w -trimpath -o "./build/macos/gateway_arm64.a" -buildmode c-archive main.go 26 | 27 | lipo -create ./build/macos/gateway_amd64.a ./build/macos/gateway_arm64.a -output ./build/macos/libgateway_amd64_arm64.a 28 | -------------------------------------------------------------------------------- /scripts/build_libs/buildso.sh: -------------------------------------------------------------------------------- 1 | #for build linux/android so file 2 | echo "building linux/android so file" 3 | #linux和Android共用动态链接库 4 | export CGO_ENABLED=1 5 | export GOARCH=amd64 6 | export GOOS=linux 7 | go build -tags linux -ldflags=-w -trimpath -o build/linux/libgateway_amd64.so -buildmode c-shared main.go 8 | # shellcheck disable=SC2034 9 | export CGO_ENABLED=1 10 | export GOARCH=arm64 11 | export GOOS=linux 12 | #sudo apt install gcc-aarch64-linux-gnu 13 | export CC=aarch64-linux-gnu-gcc 14 | ##sudo apt install g++-aarch64-linux-gnu 15 | #export CXX=aarch64-linux-gnu-g++ 16 | ##sudo apt-get install binutils-aarch64-linux-gnu 17 | #export AR=aarch64-linux-gnu-ar 18 | go build -tags linux -ldflags=-w -trimpath -o build/linux/libgateway_arm64.so -buildmode c-shared main.go 19 | 20 | #export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:~/Android/Sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH 21 | ##android/app/src/main/jniLibs/armeabi-v7a 22 | ## shellcheck disable=SC2034 23 | #export CGO_ENABLED=1 24 | #export GOARCH=arm 25 | #export GOOS=android 26 | #export CC=armv7a-linux-androideabi33-clang 27 | #go build -tags android -ldflags=-w -trimpath -o build/android/armeabi-v7a/libgateway.so -buildmode c-shared main.go 28 | ##android/app/src/main/jniLibs/arm64-v8a 29 | ## shellcheck disable=SC2034 30 | #export CGO_ENABLED=1 31 | #export GOARCH=arm64 32 | #export GOOS=android 33 | #export CC=aarch64-linux-android33-clang 34 | #go build -tags android -ldflags=-w -trimpath -o build/android/arm64-v8a/libgateway.so -buildmode c-shared main.go 35 | ##android/app/src/main/jniLibs/x86 36 | ## shellcheck disable=SC2034 37 | #export CGO_ENABLED=1 38 | #export GOARCH=386 39 | #export GOOS=android 40 | #export CC=i686-linux-android33-clang 41 | #go build -tags android -ldflags=-w -trimpath -o build/android/x86/libgateway.so -buildmode c-shared main.go 42 | ##android/app/src/main/jniLibs/x86_64 43 | ## shellcheck disable=SC2034 44 | #export CGO_ENABLED=1 45 | #export GOARCH=amd64 46 | #export GOOS=android 47 | #export CC=x86_64-linux-android33-clang 48 | #go build -tags android -ldflags=-w -trimpath -o build/android/x86_64/libgateway.so -buildmode c-shared main.go -------------------------------------------------------------------------------- /gateway-go.rb: -------------------------------------------------------------------------------- 1 | class GatewayGo < Formula 2 | desc "GateWay Client for OpenIoTHub" 3 | homepage "https://github.com/OpenIoTHub" 4 | url "https://github.com/OpenIoTHub/gateway-go.git", 5 | tag: "v0.1.91", 6 | revision: "8df96b8ae676344c14277c61ee8ac8bb206d8ef9" 7 | license "MIT" 8 | 9 | bottle do 10 | cellar :any_skip_relocation 11 | sha256 "5263c644db3dec3a9163ffcc71b1f99c75730ba1275a8a9bc2affcb6ef479f59" => :catalina 12 | sha256 "74351f8be7f640eba1a1742938539db1d8be37b9f2e63dae58a55e7d387a7e9b" => :mojave 13 | sha256 "6038aee8b37e90dc5582c000d8fa1845ccab252190c22cbf6af8a472fb55a14d" => :high_sierra 14 | end 15 | 16 | depends_on "go" => :build 17 | 18 | def install 19 | (etc/"gateway-go").mkpath 20 | system "go", "build", "-mod=vendor", "-ldflags", 21 | "-s -w -X main.version=#{version} -X main.commit=#{stable.specs[:revision]} -X main.builtBy=homebrew", 22 | *std_go_args 23 | etc.install "gateway-go.yaml" => "gateway-go/gateway-go.yaml" 24 | end 25 | 26 | plist_options manual: "gateway-go -c #{HOMEBREW_PREFIX}/etc/gateway-go/gateway-go.yaml" 27 | 28 | def plist 29 | <<~EOS 30 | 31 | 32 | 33 | 34 | Label 35 | #{plist_name} 36 | KeepAlive 37 | 38 | ProgramArguments 39 | 40 | #{opt_bin}/gateway-go 41 | -c 42 | #{etc}/gateway-go/gateway-go.yaml 43 | 44 | StandardErrorPath 45 | #{var}/log/gateway-go.log 46 | StandardOutPath 47 | #{var}/log/gateway-go.log 48 | 49 | 50 | EOS 51 | end 52 | 53 | test do 54 | assert_match version.to_s, shell_output("#{bin}/gateway-go -v 2>&1") 55 | assert_match "config created", shell_output("#{bin}/gateway-go init --config=gateway.yml 2>&1") 56 | assert_predicate testpath/"gateway.yml", :exist? 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /netservice/services/connect/service/mDns_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/OpenIoTHub/utils/v2/models" 8 | "github.com/grandcat/zeroconf" 9 | mdns "github.com/hashicorp/mdns" 10 | "log" 11 | "testing" 12 | "time" 13 | ) 14 | 15 | func TestFindAllmDNS(t *testing.T) { 16 | var rst = make([]*models.MDNSResult, 0) 17 | resolver, err := zeroconf.NewResolver(nil) 18 | if err != nil { 19 | panic(err) 20 | } 21 | 22 | entries := make(chan *zeroconf.ServiceEntry) 23 | go func(results <-chan *zeroconf.ServiceEntry) { 24 | for entry := range results { 25 | log.Printf("entry:%+v", entry) 26 | //TODO 去掉记录中ip不是本网段的ip 27 | rst = append(rst, &models.MDNSResult{ 28 | Instance: entry.Instance, 29 | Service: entry.Service, 30 | Domain: entry.Domain, 31 | HostName: entry.HostName, 32 | Port: entry.Port, 33 | Text: entry.Text, 34 | TTL: entry.TTL, 35 | AddrIPv4: entry.AddrIPv4, 36 | AddrIPv6: entry.AddrIPv6, 37 | }) 38 | } 39 | }(entries) 40 | timeOut := time.Second 41 | ctx, cancel := context.WithTimeout(context.Background(), timeOut) 42 | defer cancel() 43 | //err = resolver.Browse(ctx, "_services._dns-sd._udp", "local", entries) 44 | err = resolver.Browse(ctx, "_airplay._tcp", "local", entries) 45 | if err != nil { 46 | panic(err) 47 | } 48 | <-ctx.Done() 49 | //log.Println("获取完成:") 50 | //if len(rst) > 0 { 51 | // log.Println(rst[0]) 52 | //} 53 | rstByte, err := json.Marshal(&rst) 54 | if err != nil { 55 | log.Println(err.Error()) 56 | panic(err) 57 | } 58 | fmt.Println(string(rstByte)) 59 | } 60 | 61 | func TestFindAllmDNS2(t *testing.T) { 62 | // Make a channel for results and start listening 63 | entriesCh := make(chan *mdns.ServiceEntry) 64 | go func() { 65 | for entry := range entriesCh { 66 | fmt.Printf("Got new entry: %+v\n", *entry) 67 | } 68 | }() 69 | params := mdns.DefaultParams("_airplay._tcp") 70 | params.Entries = entriesCh 71 | params.Timeout = 5 * time.Second 72 | params.DisableIPv6 = true 73 | // Start the lookup 74 | //err := mdns.Lookup("_airplay._tcp", entriesCh) 75 | err := mdns.Query(params) 76 | if err != nil { 77 | log.Println(err.Error()) 78 | } 79 | time.Sleep(1 * time.Second) 80 | 81 | close(entriesCh) 82 | } 83 | -------------------------------------------------------------------------------- /gateway.pom: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | cloud.iothub 7 | 8 | gateway 9 | 10 | 0.0.4 11 | aar 12 | 13 | cloud.iothub.gateway 14 | 15 | OpenIoTHub gateway aar 16 | http://github.com/OpenIoTHub 17 | 18 | 19 | 20 | The Apache Software License, Version 2.0 21 | http://www.apache.org/licenses/LICENSE-2.0.txt 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ossrh 32 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 33 | 34 | 35 | ossrh 36 | https://s01.oss.sonatype.org/content/repositories/snapshots 37 | 38 | 39 | 40 | 41 | release 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | https://github.com/OpenIoTHub/nexus-public 52 | https://github.com/OpenIoTHub/nexus-public.git 53 | https://github.com/OpenIoTHub 54 | 55 | 56 | 57 | 58 | 59 | FangYu 60 | yu@iotserv.com 61 | https://github.com/OpenIoTHub 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /netservice/services/login/login.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | "github.com/OpenIoTHub/gateway-go/v2/info" 5 | "github.com/OpenIoTHub/utils/v2/models" 6 | "github.com/OpenIoTHub/utils/v2/msg" 7 | "github.com/libp2p/go-yamux" 8 | "log" 9 | "net" 10 | "runtime" 11 | "strconv" 12 | "time" 13 | ) 14 | 15 | func LoginServer(tokenstr string) (*yamux.Session, error) { //bool retry? false :dont retry 16 | token, err := models.DecodeUnverifiedToken(tokenstr) 17 | if err != nil { 18 | log.Println(err.Error()) 19 | return nil, err 20 | } 21 | //KCP方式 22 | //conn, err := kcp.DialWithOptions(fmt.Sprintf("%s:%d", token.Host, token.KcpPort), nil, 10, 3) 23 | //conn.SetStreamMode(true) 24 | //conn.SetWriteDelay(false) 25 | //conn.SetNoDelay(0, 40, 2, 1) 26 | //conn.SetWindowSize(1024, 1024) 27 | //conn.SetMtu(1472) 28 | //conn.SetACKNoDelay(true) 29 | //Tls 30 | //conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", token.Host, token.TlsPort), &tls.Config{InsecureSkipVerify: true}) 31 | //TCP 32 | conn, err := net.DialTimeout("tcp", net.JoinHostPort(token.Host, strconv.Itoa(token.TcpPort)), time.Second*2) 33 | if err != nil { 34 | return nil, err 35 | } 36 | login := &models.GatewayLogin{ 37 | Token: tokenstr, 38 | Os: runtime.GOOS, 39 | Arch: runtime.GOARCH, 40 | Version: info.Version, 41 | } 42 | 43 | err = msg.WriteMsg(conn, login) 44 | if err != nil { 45 | conn.Close() 46 | return nil, err 47 | } 48 | config := yamux.DefaultConfig() 49 | //config.EnableKeepAlive = false 50 | session, err := yamux.Server(conn, config) 51 | if err != nil { 52 | conn.Close() 53 | return nil, err 54 | } 55 | log.Printf("login OK!") 56 | return session, nil 57 | } 58 | 59 | func LoginWorkConn(tokenStr string) (net.Conn, error) { 60 | token, err := models.DecodeUnverifiedToken(tokenStr) 61 | if err != nil { 62 | log.Println(err.Error()) 63 | return nil, err 64 | } 65 | //KCP方式 66 | //conn, err := kcp.DialWithOptions(fmt.Sprintf("%s:%d", token.Host, token.KcpPort), nil, 10, 3) 67 | //conn.SetStreamMode(true) 68 | //conn.SetWriteDelay(false) 69 | //conn.SetNoDelay(0, 40, 2, 1) 70 | //conn.SetWindowSize(1024, 1024) 71 | //conn.SetMtu(1472) 72 | //conn.SetACKNoDelay(true) 73 | //Tls 74 | //conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", token.Host, token.TlsPort), &tls.Config{InsecureSkipVerify: true}) 75 | //TCP 76 | conn, err := net.Dial("tcp", net.JoinHostPort(token.Host, strconv.Itoa(token.TcpPort))) 77 | if err != nil { 78 | return nil, err 79 | } 80 | loginWorkConn := &models.GatewayWorkConn{ 81 | RunId: token.RunId, 82 | Secret: tokenStr, 83 | Version: info.Version, 84 | } 85 | 86 | err = msg.WriteMsg(conn, loginWorkConn) 87 | if err != nil { 88 | conn.Close() 89 | return nil, err 90 | } 91 | return conn, nil 92 | } 93 | -------------------------------------------------------------------------------- /tasks/ipv6ClientServer.go: -------------------------------------------------------------------------------- 1 | package tasks 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "time" 7 | 8 | "github.com/OpenIoTHub/gateway-go/v2/chans" 9 | "github.com/OpenIoTHub/gateway-go/v2/config" 10 | "github.com/OpenIoTHub/gateway-go/v2/netservice/handle" 11 | utilsmodels "github.com/OpenIoTHub/utils/v2/models" 12 | "github.com/OpenIoTHub/utils/v2/msg" 13 | "github.com/libp2p/go-yamux" 14 | ) 15 | 16 | // Ipv6ClientTask 接收配置创建新的Client handle 17 | func ipv6ClientTask() { 18 | // 主动连接访问者的APP 19 | for remoteIpv6Server := range chans.ClientTaskChan { 20 | log.Println("====获取到任务") 21 | ip := remoteIpv6Server.Ipv6AddrIp 22 | port := remoteIpv6Server.Ipv6AddrPort 23 | //runId := remoteIpv6Server.RunId 24 | // 使用配置创建连接,并且发送带RunId的凭证给访问者 25 | ipv6conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ 26 | IP: net.ParseIP(ip), 27 | Port: port, 28 | }) 29 | if err != nil { 30 | log.Println("ipv6 net.DialTCP" + err.Error()) 31 | continue 32 | } 33 | log.Println("ipv6 net.DialTCP connected:" + remoteIpv6Server.Ipv6AddrIp) 34 | //TODO 发送凭证 35 | runIdMsg := &utilsmodels.JsonResponse{} 36 | err = msg.WriteMsg(ipv6conn, runIdMsg) 37 | if err != nil { 38 | log.Println("ipv6 msg.WriteMsg" + err.Error()) 39 | ipv6conn.Close() 40 | return 41 | } 42 | //创建session,session handle 43 | yamuxConfig := yamux.DefaultConfig() 44 | //remoteIpv6Server.EnableKeepAlive = false 45 | session, err := yamux.Server(ipv6conn, yamuxConfig) 46 | if err != nil { 47 | ipv6conn.Close() 48 | return 49 | } 50 | log.Printf("ipv6 p2p client HandleSession") 51 | go handle.HandleSession(session, "") 52 | } 53 | } 54 | 55 | func ipv6ServerTask() { 56 | listener, err := net.ListenTCP("tcp6", &net.TCPAddr{}) 57 | if err != nil { 58 | log.Println(err) 59 | return 60 | } 61 | listenerPort := listener.Addr().(*net.TCPAddr).Port 62 | log.Println("ipv6 server listening on", listenerPort) 63 | config.Ipv6ListenTcpHandlePort = listenerPort 64 | //接受验证连通性,接受连接和服务请求 65 | for { 66 | conn, err := listener.AcceptTCP() 67 | if err != nil { 68 | time.Sleep(100 * time.Millisecond) 69 | continue 70 | } 71 | log.Println("ipv6 server handle conn", conn.RemoteAddr().String()) 72 | // 验证token,回复 73 | go ipv6ClientHandle(conn) 74 | } 75 | } 76 | 77 | func ipv6ClientHandle(conn net.Conn) { 78 | rawMsg, err := msg.ReadMsg(conn) 79 | if err != nil { 80 | log.Println(err.Error() + "从stream读取数据错误") 81 | conn.Close() 82 | return 83 | } 84 | // TODO 验证token,RunId 85 | _ = rawMsg 86 | // Token为空 87 | session, err := yamux.Server(conn, yamux.DefaultConfig()) 88 | if err != nil { 89 | conn.Close() 90 | return 91 | } 92 | log.Println("ipv6 server handle session", conn.RemoteAddr().String()) 93 | //TODO token!!!! 94 | go handle.HandleSession(session, "") 95 | } 96 | -------------------------------------------------------------------------------- /services/serverSession.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "github.com/OpenIoTHub/gateway-go/v2/netservice/handle" 5 | "github.com/OpenIoTHub/gateway-go/v2/netservice/services/login" 6 | "github.com/OpenIoTHub/utils/v2/models" 7 | "github.com/libp2p/go-yamux" 8 | "log" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | type ServerSession struct { 14 | //基础信息 15 | token string 16 | tokenModel *models.TokenClaims 17 | //内部存储 18 | session *yamux.Session 19 | heartbeat *time.Ticker 20 | quit chan struct{} 21 | 22 | checkSessionStatusLock sync.Mutex 23 | loginLock sync.Mutex 24 | loopStreamLock sync.Mutex 25 | } 26 | 27 | func (ss *ServerSession) stop() { 28 | ss.quit <- struct{}{} 29 | } 30 | 31 | func (ss *ServerSession) start() (err error) { 32 | //防止多次调用 33 | ss.checkSessionStatus() 34 | ss.heartbeat = time.NewTicker(time.Second * 20) 35 | ss.quit = make(chan struct{}) 36 | go ss.task() 37 | return 38 | } 39 | 40 | func (ss *ServerSession) loginServer() (err error) { 41 | ss.loginLock.Lock() 42 | defer ss.loginLock.Unlock() 43 | if ss.session != nil && !ss.session.IsClosed() { 44 | return 45 | } 46 | ss.session, err = login.LoginServer(ss.token) 47 | if err != nil { 48 | log.Println("登录失败:" + err.Error()) 49 | return err 50 | } 51 | return 52 | } 53 | 54 | func (ss *ServerSession) loopStream() { 55 | ss.loopStreamLock.Lock() 56 | defer ss.loopStreamLock.Unlock() 57 | //防止影响新创建的会话,不关闭会话 58 | //defer func() { 59 | // if ss.session != nil { 60 | // err := ss.session.Close() 61 | // if err != nil { 62 | // log.Println(err.Error()) 63 | // } 64 | // } 65 | //}() 66 | for { 67 | if ss.session == nil || (ss.session != nil && ss.session.IsClosed()) { 68 | log.Println("ss.session is nil") 69 | break 70 | } 71 | // Accept a stream 72 | stream, err := ss.session.AcceptStream() 73 | if err != nil { 74 | log.Println("accpStreamErr:" + err.Error()) 75 | ss.session.Close() 76 | break 77 | } 78 | log.Println("获取到一个连接需要处理") 79 | go handle.HandleStream(stream, ss.token) 80 | } 81 | } 82 | 83 | func (ss *ServerSession) checkSessionStatus() { 84 | ss.checkSessionStatusLock.Lock() 85 | defer ss.checkSessionStatusLock.Unlock() 86 | if ss.session == nil || (ss.session != nil && ss.session.IsClosed()) { 87 | log.Println("开始(重新)连接:", ss.tokenModel.RunId, "@", ss.tokenModel.Host) 88 | err := ss.loginServer() 89 | if err != nil { 90 | log.Println(err) 91 | return 92 | } 93 | go ss.loopStream() 94 | } 95 | } 96 | 97 | func (ss *ServerSession) task() { 98 | for { 99 | select { 100 | //心跳来了,检测连接的存活状态 101 | case <-ss.heartbeat.C: 102 | ss.checkSessionStatus() 103 | case <-ss.quit: 104 | ss.heartbeat.Stop() 105 | close(ss.quit) 106 | if ss.session != nil && !ss.session.IsClosed() { 107 | log.Println(ss.session.Close()) 108 | } 109 | return 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /netservice/services/connect/service/mdns/enter.go: -------------------------------------------------------------------------------- 1 | package mdns 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/OpenIoTHub/utils/v2/models" 7 | "github.com/grandcat/zeroconf" 8 | "log" 9 | "strings" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | var MdnsManager *MdnsCtrl 15 | 16 | // MdnsCtrl mdns管理器 17 | type MdnsCtrl struct { 18 | sync.Mutex 19 | serviceTypeList map[string]bool 20 | serviceTypeServiceMap map[string][]models.MDNSResult 21 | } 22 | 23 | func init() { 24 | //NewMdnsManager() 25 | } 26 | 27 | func NewMdnsManager() { 28 | if MdnsManager == nil { 29 | MdnsManager = new(MdnsCtrl) 30 | MdnsManager.serviceTypeList = make(map[string]bool) 31 | MdnsManager.serviceTypeServiceMap = make(map[string][]models.MDNSResult) 32 | MdnsManager.startService() 33 | } 34 | } 35 | 36 | func (mc *MdnsCtrl) startService() { 37 | go mc.startZeroconfFind() 38 | go mc.startAvahiFind() 39 | } 40 | 41 | func (mc *MdnsCtrl) startZeroconfFind() (err error) { 42 | var resolver *zeroconf.Resolver 43 | for { 44 | const mdnsFindTypes = "_services._dns-sd._udp" 45 | resolver, err = zeroconf.NewResolver(nil) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | entries := make(chan *zeroconf.ServiceEntry) 51 | go func(results <-chan *zeroconf.ServiceEntry) { 52 | for entry := range results { 53 | mc.serviceTypeList[strings.Replace(entry.Instance, ".local", "", 1)] = true 54 | } 55 | }(entries) 56 | 57 | //TODO 发现时间 58 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) 59 | defer cancel() 60 | err = resolver.Browse(ctx, mdnsFindTypes, "local", entries) 61 | if err != nil { 62 | return err 63 | } 64 | <-ctx.Done() 65 | //close(entries) 66 | 67 | for key, _ := range mc.serviceTypeList { 68 | entries2 := make(chan *zeroconf.ServiceEntry) 69 | go func(results <-chan *zeroconf.ServiceEntry) { 70 | for entry := range results { 71 | //去重添加 72 | mc.serviceTypeServiceMap[entry.Service] = append(mc.serviceTypeServiceMap[entry.Service], models.MDNSResult{ 73 | Instance: entry.Instance, 74 | Service: entry.Service, 75 | Domain: entry.Domain, 76 | HostName: entry.HostName, 77 | Port: entry.Port, 78 | Text: entry.Text, 79 | TTL: entry.TTL, 80 | AddrIPv4: entry.AddrIPv4, 81 | AddrIPv6: entry.AddrIPv6, 82 | }) 83 | } 84 | }(entries2) 85 | ctx, cancel = context.WithTimeout(context.Background(), time.Second*2) 86 | resolver, err = zeroconf.NewResolver(nil) 87 | if err != nil { 88 | log.Println(err) 89 | cancel() 90 | continue 91 | } 92 | err = resolver.Browse(ctx, key, "local", entries2) 93 | if err != nil { 94 | log.Println(err) 95 | cancel() 96 | continue 97 | } 98 | <-ctx.Done() 99 | cancel() 100 | time.Sleep(time.Second) 101 | } 102 | //close(entries2) 103 | fmt.Printf("mdns:%+v", mc.serviceTypeServiceMap) 104 | } 105 | } 106 | 107 | func (mc *MdnsCtrl) startAvahiFind() (err error) { 108 | return 109 | } 110 | -------------------------------------------------------------------------------- /netservice/services/connect/service/mdns/mDns.go: -------------------------------------------------------------------------------- 1 | package mdns 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "log" 7 | "net" 8 | "time" 9 | 10 | "github.com/OpenIoTHub/gateway-go/v2/register" 11 | "github.com/OpenIoTHub/utils/v2/models" 12 | "github.com/OpenIoTHub/utils/v2/msg" 13 | "github.com/grandcat/zeroconf" 14 | ) 15 | 16 | func (mc *MdnsCtrl) FindAllmDNS(stream net.Conn, service *models.NewService) error { 17 | //decode json 18 | var config *models.FindmDNS 19 | var rst = make([]*models.MDNSResult, 0) 20 | err := json.Unmarshal([]byte(service.Config), &config) 21 | if err != nil { 22 | log.Println("json.Unmarshal([]byte(service.Config), &config):" + err.Error()) 23 | return err 24 | } 25 | 26 | resolver, err := zeroconf.NewResolver(nil) 27 | if err != nil { 28 | log.Println("zeroconf.NewResolver:" + err.Error()) 29 | } else { 30 | entries := make(chan *zeroconf.ServiceEntry) 31 | go func(results <-chan *zeroconf.ServiceEntry) { 32 | for entry := range results { 33 | //log.Println("entry:", entry) 34 | //TODO 去掉记录中ip不是本网段的ip 35 | rst = append(rst, &models.MDNSResult{ 36 | Instance: entry.Instance, 37 | Service: entry.Service, 38 | Domain: entry.Domain, 39 | HostName: entry.HostName, 40 | Port: entry.Port, 41 | Text: entry.Text, 42 | TTL: entry.TTL, 43 | AddrIPv4: entry.AddrIPv4, 44 | AddrIPv6: entry.AddrIPv6, 45 | }) 46 | } 47 | }(entries) 48 | //TODO 发现时间 49 | timeOut := time.Millisecond * time.Duration(config.Second) * 250 50 | ctx, cancel := context.WithTimeout(context.Background(), timeOut) 51 | defer cancel() 52 | err = resolver.Browse(ctx, config.Service, config.Domain, entries) 53 | if err != nil { 54 | log.Println("resolver.Browse:" + err.Error()) 55 | return err 56 | } 57 | <-ctx.Done() 58 | } 59 | registeredServices := register.GetRegisteredServices() 60 | //发现_services._dns-sd._udp类型的时候添加所有手动注册的类型 61 | if config.Service == "_services._dns-sd._udp" { 62 | registeredType := make([]string, 0) 63 | Loop1: 64 | for _, registeredService := range registeredServices { 65 | for _, item := range registeredType { 66 | if item == registeredService.Service { 67 | continue Loop1 68 | } 69 | } 70 | //log.Println("ADD Registered service: ", registeredService.Service) 71 | rst = append(rst, &models.MDNSResult{ 72 | Instance: registeredService.Service + ".local", 73 | Service: "_services._dns-sd._udp", 74 | Domain: "local", 75 | }) 76 | registeredType = append(registeredType, registeredService.Service) 77 | } 78 | } else { 79 | for _, registeredService := range registeredServices { 80 | rst = append(rst, &models.MDNSResult{ 81 | Instance: registeredService.Instance, 82 | Service: registeredService.Service, 83 | Domain: registeredService.Domain, 84 | HostName: registeredService.HostName, 85 | Port: registeredService.Port, 86 | Text: registeredService.Text, 87 | TTL: registeredService.TTL, 88 | AddrIPv4: registeredService.AddrIPv4, 89 | AddrIPv6: registeredService.AddrIPv6, 90 | }) 91 | } 92 | } 93 | rstByte, err := json.Marshal(&rst) 94 | if err != nil { 95 | log.Println(err.Error()) 96 | return err 97 | } 98 | //log.Println("mdns rstByte:", string(rstByte)) 99 | err = msg.WriteMsg(stream, &models.JsonResponse{Code: 0, Msg: "Success", Result: string(rstByte)}) 100 | if err != nil { 101 | log.Println("写消息错误:") 102 | log.Println(err.Error()) 103 | } 104 | //content, _ := json.Marshal(&models.JsonResponse{Code: 0, Msg: "Success", Result: string(rstByte)}) 105 | //log.Println("content:", string(content)) 106 | //stream.Close() 107 | return err 108 | } 109 | -------------------------------------------------------------------------------- /netservice/services/connect/service/systemStatus_others.go: -------------------------------------------------------------------------------- 1 | //go:build !ios 2 | 3 | package service 4 | 5 | import ( 6 | "encoding/json" 7 | 8 | "github.com/OpenIoTHub/utils/v2/models" 9 | "github.com/OpenIoTHub/utils/v2/msg" 10 | "github.com/shirou/gopsutil/v3/cpu" 11 | "github.com/shirou/gopsutil/v3/disk" 12 | "github.com/shirou/gopsutil/v3/host" 13 | "github.com/shirou/gopsutil/v3/mem" 14 | sysnet "github.com/shirou/gopsutil/v3/net" 15 | 16 | "log" 17 | "net" 18 | ) 19 | 20 | func GetSystemStatus(stream net.Conn, service *models.NewService) error { 21 | statMap := make(map[string]interface{}) 22 | // 获取主机相关信息 23 | hostInfo, _ := host.Info() 24 | hostMap := make(map[string]interface{}) 25 | hostMap["uptime"] = hostInfo.Uptime //运行时间 26 | hostMap["bootTime"] = hostInfo.BootTime //启动时间 27 | hostMap["procs"] = hostInfo.Procs //进程数 28 | hostMap["os"] = hostInfo.OS //操作系统 29 | hostMap["platform"] = hostInfo.Platform //平台 30 | hostMap["platformVersion"] = hostInfo.PlatformVersion //平台版本 31 | hostMap["kernelArch"] = hostInfo.KernelArch //内核 32 | hostMap["kernelVersion"] = hostInfo.KernelVersion //内核版本 33 | statMap["hosts"] = hostMap 34 | 35 | // 获取内存信息 36 | memInfo, _ := mem.VirtualMemory() 37 | memMap := make(map[string]interface{}) 38 | memMap["total"] = memInfo.Total //总内存 39 | memMap["available"] = memInfo.Available //可用内存 40 | memMap["used"] = memInfo.Used //已使用内存 41 | memMap["free"] = memInfo.Free //剩余内存 42 | memMap["usedPercent"] = memInfo.UsedPercent //百分比 43 | memMap["buffers"] = memInfo.Buffers //缓存 44 | memMap["shared"] = memInfo.Shared //共享内存 45 | memMap["cached"] = memInfo.Cached //缓冲区 46 | statMap["mems"] = memMap 47 | 48 | // 获取CPU信息 49 | cpuInfo, _ := cpu.Info() 50 | var cpuMapArr []map[string]interface{} 51 | for _, c := range cpuInfo { 52 | cpuMap := make(map[string]interface{}) 53 | cpuMap["cpu"] = c.CPU + 1 //第几个CPU 从0开始的 54 | cpuMap["cores"] = c.Cores //CPU的核数 55 | cpuMap["modelName"] = c.ModelName //CPU类型 56 | cpuMapArr = append(cpuMapArr, cpuMap) 57 | } 58 | statMap["cpus"] = cpuMapArr 59 | 60 | // 获取IO信息 61 | ioInfo, _ := sysnet.IOCounters(false) 62 | var ioMapArr []map[string]interface{} 63 | for _, i := range ioInfo { 64 | ioMap := make(map[string]interface{}) 65 | ioMap["ioName"] = i.Name //网口名 66 | ioMap["bytesSent"] = i.BytesSent //发送字节数 67 | ioMap["bytesRecv"] = i.BytesRecv //接收字节数 68 | ioMap["packetsSent"] = i.PacketsSent //发送的数据包数 69 | ioMap["packetsRecv"] = i.PacketsRecv //接收的数据包数 70 | ioMapArr = append(ioMapArr, ioMap) 71 | } 72 | statMap["ios"] = ioMapArr 73 | 74 | // 获取磁盘信息 75 | partitions, _ := disk.Partitions(false) 76 | var diskMapArr []map[string]interface{} 77 | for _, partition := range partitions { 78 | diskMap := make(map[string]interface{}) 79 | usage, _ := disk.Usage(partition.Mountpoint) 80 | diskMap["disk"] = partition.Mountpoint //第几块磁盘 81 | diskMap["total"] = usage.Total //总大小 82 | diskMap["free"] = usage.Free //剩余空间 83 | diskMap["used"] = usage.Used //已使用空间 84 | diskMap["usedPercent"] = usage.UsedPercent //百分比 85 | diskMapArr = append(diskMapArr, diskMap) 86 | } 87 | statMap["disks"] = diskMapArr 88 | statMap["code"] = 0 89 | statMap["message"] = "success" 90 | rstByte, err := json.Marshal(statMap) 91 | if err != nil { 92 | log.Println("json.Marshal(statMap):") 93 | log.Println(err.Error()) 94 | } 95 | //log.Println(string(rstByte)) 96 | err = msg.WriteMsg(stream, &models.JsonResponse{Code: 0, Msg: "Success", Result: string(rstByte)}) 97 | if err != nil { 98 | log.Println("写消息错误:") 99 | log.Println(err.Error()) 100 | } 101 | return err 102 | } 103 | -------------------------------------------------------------------------------- /services/gatewayManager.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/OpenIoTHub/gateway-go/v2/utils/qr" 7 | "github.com/OpenIoTHub/utils/v2/models" 8 | "github.com/gin-gonic/gin" 9 | "github.com/skip2/go-qrcode" 10 | "log" 11 | ) 12 | 13 | var GatewayManager = &GatewayCtl{serverSession: make(map[string]*ServerSession)} 14 | 15 | type GatewayCtl struct { 16 | serverSession map[string]*ServerSession 17 | } 18 | 19 | func (gm *GatewayCtl) Loged() bool { 20 | return len(gm.serverSession) > 0 21 | } 22 | 23 | // AddServer 添加网关实例,登录一个id 24 | func (gm *GatewayCtl) AddServer(token string) (err error) { 25 | tokenModel, err := models.DecodeUnverifiedToken(token) 26 | if err != nil { 27 | log.Println(err.Error()) 28 | return 29 | } 30 | if _, ok := gm.serverSession[tokenModel.RunId]; ok { 31 | log.Println("runId already exist") 32 | return errors.New("runId already exist") 33 | } 34 | serverSession := &ServerSession{ 35 | token: token, 36 | tokenModel: tokenModel, 37 | } 38 | gm.serverSession[tokenModel.RunId] = serverSession 39 | return serverSession.start() 40 | } 41 | 42 | // DelServer 删除网关实例,删除一个id 43 | func (gm *GatewayCtl) DelServer(runid string) (err error) { 44 | if _, ok := gm.serverSession[runid]; ok { 45 | log.Println("找到了runid的serverSession") 46 | gm.serverSession[runid].stop() 47 | delete(gm.serverSession, runid) 48 | //TODO 同时删除配置文件的相关配置 49 | return 50 | } 51 | return errors.New(fmt.Sprintf("gateway uuid:%s not found", runid)) 52 | } 53 | 54 | // IndexHandler http服务首页 55 | func (gm *GatewayCtl) IndexHandler(c *gin.Context) { 56 | //显示添加的二维码 57 | htmlContent := ` 58 | 59 | 60 | 61 | 62 | OpenIoThub gateway-go - NAT tool for remote control 63 | 83 | 84 | 85 | 扫码添加二维码 86 |
使用云亿连(从应用市场搜索下载或拷贝本链接在移动端打开)扫描上述二维码添加本网关,然后添加主机,主机下面添加端口就可以访问目标端口了!视频教程🌐文档🌐开源地址🌐
87 |
Use OpenIoTHub to scan the above QR code and add a gateway,then add host,add host's port,finally, enjoy remote control.HomePage🌐
88 | 89 | 90 | ` 91 | c.Data(200, "text/html", []byte(htmlContent)) 92 | } 93 | 94 | // DisplayQrHandler 返回二维码 95 | func (gm *GatewayCtl) DisplayQrHandler(c *gin.Context) { 96 | var err error 97 | //显示添加的二维码 98 | if len(gm.serverSession) == 0 { 99 | c.Data(200, "text/plain", []byte("no gateway login")) 100 | return 101 | } 102 | gatewayUUID, serverHost, err := gm.GetLoginInfo() 103 | if err != nil { 104 | c.Data(200, "text/plain", []byte(err.Error())) 105 | return 106 | } 107 | 108 | var qrCode *qrcode.QRCode 109 | if serverHost == "" || serverHost == qr.STDHost { 110 | qrCode, err = qr.GetQrById(gatewayUUID) 111 | } else { 112 | qrCode, err = qr.GetQrByIdAndHost(gatewayUUID, serverHost) 113 | } 114 | if err != nil { 115 | c.Data(200, "text/plain", []byte(err.Error())) 116 | return 117 | } 118 | c.Header("ContentType", "image/png") 119 | qrCode.Write(300, c.Writer) 120 | } 121 | 122 | // DisplayQrHandler 返回二维码 123 | func (gm *GatewayCtl) GetLoginInfo() (gatewayUUID, serverHost string, err error) { 124 | for key, sess := range gm.serverSession { 125 | gatewayUUID = key 126 | serverHost = sess.tokenModel.Host 127 | } 128 | if gatewayUUID == "" && serverHost == "" { 129 | err = errors.New("Not Logged In") 130 | } 131 | return 132 | } 133 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/OpenIoTHub/gateway-go/v2 2 | 3 | go 1.24.2 4 | 5 | require ( 6 | github.com/OpenIoTHub/getip/v2 v2.0.1 7 | github.com/OpenIoTHub/openiothub_grpc_api v1.2.4 8 | github.com/OpenIoTHub/utils/v2 v2.0.4 9 | github.com/OpenIoTHub/water v0.0.3 10 | github.com/gin-gonic/gin v1.11.0 11 | github.com/grandcat/zeroconf v1.0.0 12 | github.com/hashicorp/mdns v1.0.6 13 | github.com/jacobsa/go-serial v0.0.0-20180131005756-15cf729a72d4 14 | github.com/libp2p/go-yamux v1.4.1 15 | github.com/moby/moby/client v0.2.1 16 | github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b 17 | github.com/shirou/gopsutil/v3 v3.24.5 18 | github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e 19 | github.com/urfave/cli/v2 v2.27.7 20 | golang.org/x/crypto v0.46.0 21 | golang.org/x/net v0.48.0 22 | google.golang.org/grpc v1.77.0 23 | google.golang.org/protobuf v1.36.11 24 | gopkg.in/yaml.v3 v3.0.1 25 | ) 26 | 27 | require ( 28 | github.com/Microsoft/go-winio v0.6.2 // indirect 29 | github.com/bytedance/gopkg v0.1.3 // indirect 30 | github.com/bytedance/sonic v1.14.2 // indirect 31 | github.com/bytedance/sonic/loader v0.4.0 // indirect 32 | github.com/cenkalti/backoff v2.2.1+incompatible // indirect 33 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 34 | github.com/cloudwego/base64x v0.1.6 // indirect 35 | github.com/containerd/errdefs v1.0.0 // indirect 36 | github.com/containerd/errdefs/pkg v0.3.0 // indirect 37 | github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect 38 | github.com/distribution/reference v0.6.0 // indirect 39 | github.com/docker/go-connections v0.6.0 // indirect 40 | github.com/docker/go-units v0.5.0 // indirect 41 | github.com/felixge/httpsnoop v1.0.4 // indirect 42 | github.com/gabriel-vasile/mimetype v1.4.11 // indirect 43 | github.com/gin-contrib/sse v1.1.0 // indirect 44 | github.com/go-logr/logr v1.4.3 // indirect 45 | github.com/go-logr/stdr v1.2.2 // indirect 46 | github.com/go-ole/go-ole v1.3.0 // indirect 47 | github.com/go-playground/locales v0.14.1 // indirect 48 | github.com/go-playground/universal-translator v0.18.1 // indirect 49 | github.com/go-playground/validator/v10 v10.28.0 // indirect 50 | github.com/goccy/go-json v0.10.5 // indirect 51 | github.com/goccy/go-yaml v1.19.0 // indirect 52 | github.com/golang-jwt/jwt/v5 v5.3.0 // indirect 53 | github.com/golang/snappy v1.0.0 // indirect 54 | github.com/json-iterator/go v1.1.12 // indirect 55 | github.com/klauspost/cpuid/v2 v2.3.0 // indirect 56 | github.com/klauspost/reedsolomon v1.12.6 // indirect 57 | github.com/leodido/go-urn v1.4.0 // indirect 58 | github.com/libp2p/go-buffer-pool v0.1.0 // indirect 59 | github.com/libp2p/go-msgio v0.3.0 // indirect 60 | github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect 61 | github.com/mattn/go-isatty v0.0.20 // indirect 62 | github.com/miekg/dns v1.1.69 // indirect 63 | github.com/moby/docker-image-spec v1.3.1 // indirect 64 | github.com/moby/moby/api v1.52.0 // indirect 65 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 66 | github.com/modern-go/reflect2 v1.0.2 // indirect 67 | github.com/multiformats/go-varint v0.1.0 // indirect 68 | github.com/opencontainers/go-digest v1.0.0 // indirect 69 | github.com/opencontainers/image-spec v1.1.1 // indirect 70 | github.com/pelletier/go-toml/v2 v2.2.4 // indirect 71 | github.com/pkg/errors v0.9.1 // indirect 72 | github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect 73 | github.com/quic-go/qpack v0.6.0 // indirect 74 | github.com/quic-go/quic-go v0.57.1 // indirect 75 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 76 | github.com/shoenig/go-m1cpu v0.1.7 // indirect 77 | github.com/tjfoc/gmsm v1.4.1 // indirect 78 | github.com/tklauser/go-sysconf v0.3.16 // indirect 79 | github.com/tklauser/numcpus v0.11.0 // indirect 80 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 81 | github.com/ugorji/go/codec v1.3.1 // indirect 82 | github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect 83 | github.com/xtaci/kcp-go/v5 v5.6.55 // indirect 84 | github.com/yusufpapurcu/wmi v1.2.4 // indirect 85 | go.opentelemetry.io/auto/sdk v1.2.1 // indirect 86 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect 87 | go.opentelemetry.io/otel v1.39.0 // indirect 88 | go.opentelemetry.io/otel/metric v1.39.0 // indirect 89 | go.opentelemetry.io/otel/trace v1.39.0 // indirect 90 | go.uber.org/mock v0.6.0 // indirect 91 | golang.org/x/arch v0.23.0 // indirect 92 | golang.org/x/mod v0.31.0 // indirect 93 | golang.org/x/sync v0.19.0 // indirect 94 | golang.org/x/sys v0.39.0 // indirect 95 | golang.org/x/text v0.32.0 // indirect 96 | golang.org/x/time v0.14.0 // indirect 97 | golang.org/x/tools v0.40.0 // indirect 98 | golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect 99 | google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect 100 | ) 101 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "os" 8 | "sync" 9 | 10 | client "github.com/OpenIoTHub/gateway-go/v2/client" 11 | "github.com/OpenIoTHub/gateway-go/v2/config" 12 | "github.com/OpenIoTHub/gateway-go/v2/services" 13 | "github.com/OpenIoTHub/gateway-go/v2/utils/login" 14 | "github.com/OpenIoTHub/gateway-go/v2/utils/qr" 15 | "github.com/OpenIoTHub/gateway-go/v2/utils/str" 16 | utils_models "github.com/OpenIoTHub/utils/v2/models" 17 | uuid "github.com/satori/go.uuid" 18 | "github.com/urfave/cli/v2" 19 | "gopkg.in/yaml.v3" 20 | ) 21 | 22 | var ( 23 | version = "" 24 | commit = "" 25 | date = "" 26 | builtBy = "" 27 | ) 28 | 29 | func main() { 30 | client.IsLibrary = false 31 | myApp := cli.NewApp() 32 | myApp.Name = "gateway-go" 33 | myApp.Usage = "-c [config file path]" 34 | myApp.Version = str.BuildVersion(version, commit, date, builtBy) 35 | myApp.Commands = []*cli.Command{ 36 | { 37 | Name: "init", 38 | Aliases: []string{"i"}, 39 | Usage: "init config file", 40 | Flags: []cli.Flag{ 41 | &cli.StringFlag{ 42 | Name: "config", 43 | Aliases: []string{"c"}, 44 | Value: config.ConfigFilePath, 45 | Usage: "config file path", 46 | EnvVars: []string{"GatewayConfigFilePath"}, 47 | Destination: &config.ConfigFilePath, 48 | }, 49 | }, 50 | Action: func(c *cli.Context) error { 51 | config.InitConfigFile() 52 | return nil 53 | }, 54 | }, 55 | { 56 | Name: "test", 57 | Aliases: []string{"t"}, 58 | Usage: "test this command", 59 | Action: func(c *cli.Context) error { 60 | fmt.Println("ok") 61 | return nil 62 | }, 63 | }, 64 | // TODO 与当前本机运行的服务进行通信查询一些信息,比如id,token,log等 65 | // TODO 查询本机所在网络所包含的支持的服务 66 | } 67 | myApp.Flags = []cli.Flag{ 68 | //TODO 应该设置工作目录,各组件共享 69 | &cli.StringFlag{ 70 | Name: "config", 71 | Aliases: []string{"c"}, 72 | Value: config.ConfigFilePath, 73 | Usage: "config file path", 74 | EnvVars: []string{"GatewayConfigFilePath"}, 75 | Destination: &config.ConfigFilePath, 76 | }, 77 | //token 登录 78 | &cli.StringFlag{ 79 | Name: "token", 80 | Aliases: []string{"t"}, 81 | Value: config.GatewayLoginToken, 82 | Usage: "login server by gateway token ", 83 | EnvVars: []string{"GatewayLoginToken"}, 84 | Destination: &config.GatewayLoginToken, 85 | }, 86 | } 87 | myApp.Action = func(c *cli.Context) error { 88 | str.PrintOpenIoTHubLogo() 89 | if config.GatewayLoginToken != "" { 90 | UseGateWayToken() 91 | } else { 92 | _, err := os.Stat(config.ConfigFilePath) 93 | if err != nil { 94 | config.InitConfigFile() 95 | } 96 | UseConfigFile() 97 | } 98 | go client.Run() 99 | wg := sync.WaitGroup{} 100 | wg.Add(1) 101 | wg.Wait() 102 | return nil 103 | } 104 | err := myApp.Run(os.Args) 105 | if err != nil { 106 | log.Println(err.Error()) 107 | } 108 | } 109 | 110 | //export Run 111 | func Run() { 112 | go client.Run() 113 | } 114 | 115 | func UseGateWayToken() { 116 | //使用服务器签发的Token登录 117 | err := services.GatewayManager.AddServer(config.GatewayLoginToken) 118 | if err != nil { 119 | log.Println(err.Error()) 120 | log.Printf("登陆失败!请重新登陆。") 121 | return 122 | } 123 | log.Printf("登陆成功!\n") 124 | } 125 | 126 | func UseConfigFile() { 127 | //配置文件存在 128 | log.Println("使用的配置文件位置:", config.ConfigFilePath) 129 | content, err := os.ReadFile(config.ConfigFilePath) 130 | if err != nil { 131 | log.Println(err.Error()) 132 | return 133 | } 134 | err = yaml.Unmarshal(content, &config.ConfigMode) 135 | if err != nil { 136 | log.Println(err.Error()) 137 | return 138 | } 139 | //找到了配置文件 140 | if len(config.ConfigMode.GatewayUUID) < 35 { 141 | config.ConfigMode.GatewayUUID = uuid.Must(uuid.NewV4()).String() 142 | err = config.WriteConfigFile(config.ConfigMode, config.ConfigFilePath) 143 | if err != nil { 144 | log.Println(err.Error()) 145 | } 146 | } 147 | if config.ConfigMode.LoginWithTokenMap == nil { 148 | config.ConfigMode.LoginWithTokenMap = make(map[string]string) 149 | } 150 | //解析日志配置 151 | writers := []io.Writer{} 152 | if config.ConfigMode.LogConfig.EnableStdout { 153 | writers = append(writers, os.Stdout) 154 | } 155 | if config.ConfigMode.LogConfig.LogFilePath != "" { 156 | f, err := os.OpenFile(config.ConfigMode.LogConfig.LogFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) 157 | if err != nil { 158 | log.Fatal(err) 159 | } 160 | writers = append(writers, f) 161 | } 162 | fileAndStdoutWriter := io.MultiWriter(writers...) 163 | log.SetOutput(fileAndStdoutWriter) 164 | //解析配置文件,解析服务器配置文件列表 165 | //解析登录token列表 166 | //如果CLI模式尚未登录自动登陆服务器并创建一个二维码 167 | if len(config.ConfigMode.LoginWithTokenMap) == 0 { 168 | err = login.AutoLoginAndDisplayQRCode() 169 | if err != nil { 170 | log.Println(err) 171 | } 172 | } 173 | for _, v := range config.ConfigMode.LoginWithTokenMap { 174 | err = services.GatewayManager.AddServer(v) 175 | if err != nil { 176 | continue 177 | } 178 | // 通过gateway jwt(UUID)展示二维码 179 | tokenModel, err := utils_models.DecodeUnverifiedToken(v) 180 | if err != nil { 181 | return 182 | } 183 | err = qr.DisplayQRCodeById(tokenModel.RunId, tokenModel.Host) 184 | if err != nil { 185 | continue 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /client/lib.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/OpenIoTHub/gateway-go/v2/config" 7 | "github.com/OpenIoTHub/gateway-go/v2/info" 8 | "github.com/OpenIoTHub/gateway-go/v2/register" 9 | "github.com/OpenIoTHub/gateway-go/v2/services" 10 | "github.com/OpenIoTHub/gateway-go/v2/tasks" 11 | "github.com/OpenIoTHub/openiothub_grpc_api/pb-go/proto/gateway" 12 | "github.com/OpenIoTHub/utils/v2/models" 13 | "github.com/gin-gonic/gin" 14 | "github.com/grandcat/zeroconf" 15 | uuid "github.com/satori/go.uuid" 16 | "google.golang.org/grpc" 17 | "google.golang.org/grpc/reflection" 18 | "google.golang.org/protobuf/types/known/emptypb" 19 | "log" 20 | "net" 21 | ) 22 | 23 | var ( 24 | IsLibrary = true 25 | GRpcPort = 55443 26 | HttpPort = config.ConfigMode.HttpServicePort 27 | ) 28 | 29 | type LoginManager struct { 30 | pb.UnimplementedGatewayLoginManagerServer 31 | } 32 | 33 | var loginManager = new(LoginManager) 34 | 35 | func Run() { 36 | go start() 37 | } 38 | 39 | func start() { 40 | defer func() { 41 | if err := recover(); err != nil { 42 | log.Printf("gateway-go panic:%+v", err) 43 | } 44 | }() 45 | tasks.RunTasks() 46 | //启动http服务 47 | go func() { 48 | register.RegisterService("localhost-gateway-go", 49 | "_http._tcp", 50 | "local", 51 | "localhost", 52 | HttpPort, 53 | []string{"name=gateway-go", fmt.Sprintf("id=gateway-go@%s", uuid.Must(uuid.NewV4()).String()), "home-page=https://github.com/OpenIoTHub/gateway-go"}, 54 | 0, 55 | []net.IP{net.ParseIP("127.0.0.1")}, 56 | []net.IP{}, 57 | ) 58 | r := gin.Default() 59 | r.GET("/", services.GatewayManager.IndexHandler) 60 | r.GET("/DisplayQrHandler", services.GatewayManager.DisplayQrHandler) 61 | log.Printf("Http 监听端口: %d\n", HttpPort) 62 | r.Run(fmt.Sprintf(":%d", HttpPort)) 63 | }() 64 | //启动grpc服务 65 | s := grpc.NewServer() 66 | pb.RegisterGatewayLoginManagerServer(s, loginManager) 67 | //GRpcPort := services.GrpcPort 68 | //if runtime.GOOS == "android" { 69 | // GRpcPort = 55443 70 | //} 71 | 72 | lis, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.GRpcAddr, GRpcPort)) 73 | if err != nil { 74 | log.Fatalf("failed to listen: %v", err) 75 | return 76 | } 77 | //addr := lis.Addr().(*net.TCPAddr) 78 | log.Printf("Grpc 监听端口:%d\n", GRpcPort) 79 | reflection.Register(s) 80 | go regMDNS(GRpcPort) 81 | if err := s.Serve(lis); err != nil { 82 | log.Fatalf("failed to serve: %v", err) 83 | } 84 | } 85 | 86 | func regMDNS(gRpcPort int) { 87 | var Mac = "mac" 88 | interfaces, err := net.Interfaces() 89 | if err != nil { 90 | log.Println(err) 91 | } else if len(interfaces) > 0 { 92 | Mac = interfaces[0].HardwareAddr.String() 93 | } 94 | gatewayUUID, serverHost, err := services.GatewayManager.GetLoginInfo() 95 | //qrStr, err := qr.GetQrByIdAndHost(gatewayUUID, serverHost) 96 | //mDNS注册服务 97 | _, err = zeroconf.Register(fmt.Sprintf("OpenIoTHubGateway-%s", config.ConfigMode.GatewayUUID), "_openiothub-gateway._tcp", "local.", gRpcPort, 98 | []string{"name=网关", 99 | "model=com.iotserv.services.gateway", 100 | fmt.Sprintf("mac=%s", Mac), 101 | fmt.Sprintf("id=%s", config.ConfigMode.GatewayUUID), 102 | //提供网关添加信息 103 | fmt.Sprintf("run_id=%s", gatewayUUID), 104 | fmt.Sprintf("server_host=%s", serverHost), 105 | "author=Farry", 106 | "email=newfarry@126.com", 107 | "home-page=https://github.com/OpenIoTHub", 108 | "firmware-respository=https://github.com/OpenIoTHub/gateway-go/v2", 109 | //TODO 编译成库没有版本号 110 | fmt.Sprintf("firmware-version=%s", info.Version)}, nil) 111 | if err != nil { 112 | log.Println(err) 113 | return 114 | } 115 | } 116 | 117 | // rpc CheckGatewayLoginStatus (Empty) returns (LoginResponse) {} 118 | func (lm *LoginManager) CheckGatewayLoginStatus(ctx context.Context, in *emptypb.Empty) (*pb.LoginResponse, error) { 119 | return &pb.LoginResponse{ 120 | Code: 0, 121 | Message: "网关登录状态", 122 | LoginStatus: services.GatewayManager.Loged(), 123 | }, nil 124 | } 125 | 126 | // rpc LoginServerByServerInfo (ServerInfo) returns (LoginResponse) {} 127 | func (lm *LoginManager) LoginServerByToken(ctx context.Context, in *pb.Token) (*pb.LoginResponse, error) { 128 | //如果已经登录则阻止登录 129 | if services.GatewayManager.Loged() && !IsLibrary { 130 | return &pb.LoginResponse{ 131 | Code: 1, 132 | Message: "网关已经登录服务器", 133 | LoginStatus: services.GatewayManager.Loged(), 134 | }, nil 135 | } 136 | tokenModel, err := models.DecodeUnverifiedToken(in.Value) 137 | if err != nil { 138 | log.Println(err.Error()) 139 | return &pb.LoginResponse{ 140 | Code: 1, 141 | Message: "token错误", 142 | LoginStatus: services.GatewayManager.Loged(), 143 | }, err 144 | } 145 | //使用token登录 146 | err = services.GatewayManager.AddServer(in.Value) 147 | if err != nil { 148 | return &pb.LoginResponse{ 149 | Code: 1, 150 | Message: err.Error(), 151 | LoginStatus: services.GatewayManager.Loged(), 152 | }, nil 153 | } 154 | config.ConfigMode.LoginWithTokenMap[tokenModel.RunId] = in.Value 155 | err = config.WriteConfigFile(config.ConfigMode, config.ConfigFilePath) 156 | if err != nil { 157 | log.Println(err) 158 | } 159 | //标记为已经登录并返回结果 160 | return &pb.LoginResponse{ 161 | Code: 0, 162 | Message: "登录成功!", 163 | LoginStatus: services.GatewayManager.Loged(), 164 | }, nil 165 | } 166 | 167 | func (lm *LoginManager) testEmbeddedByValue() { 168 | } 169 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gateway-go 2 | 🇺🇸English | 🇨🇳[中文](https://docs.iothub.cloud/gateway/index.html) 3 | #### gateway-go is continuously running on the LAN to receive remote access requests for [OpenIoTHub](https://github.com/OpenIoTHub/OpenIoTHub) like frpc 4 | ###### Just run gateway-go,gateway-go showing a QR code, then use [OpenIoTHub](https://github.com/OpenIoTHub/OpenIoTHub) scan this QR code add this gateway-go, now you can access all network port and service from OpenIoTHub client 5 | 6 | [![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-white.svg)](https://snapcraft.io/gateway-go) 7 | 8 | You can install the pre-compiled binary (in several different ways), 9 | use Docker. 10 | 11 | Here are the steps for each of them: 12 | ## NAS Install 13 | 14 | #### [synology](https://www.synology.com) 15 | Currently, you can [install it using Docker](https://www.bilibili.com/video/BV1of4y1G7Jb) 16 | #### [CasaOS](https://www.casaos.io)、[ZimaOS](https://www.zimaspace.com) 17 | Download directly from the app market [gateway-go](https://github.com/IceWhaleTech/CasaOS-AppStore/tree/main/Apps/Gateway-go) 18 | #### [fnos](https://www.fnnas.com) 19 | Currently, you can [install it using Docker](#running-with-docker) 20 | #### [unRaid](https://unraid.net) 21 | Currently, you can [install it using Docker](https://www.bilibili.com/video/BV1EzWFeSEGC) 22 | #### [QNAP](https://www.qnap.com.cn/zh-cn) 23 | Currently, you can [install it using Docker](#running-with-docker) 24 | #### [ugnas](https://www.ugnas.com) 25 | Currently, you can [install it using Docker](#running-with-docker) 26 | #### [zspace](https://www.zspace.cn) 27 | Currently, you can [install it using Docker](#running-with-docker) 28 | ## Router Install 29 | 30 | #### [openwrt/entware/optware](https://openwrt.org/) 31 | ```sh 32 | opkg update 33 | opkg install gateway-go 34 | ``` 35 | #### use snapshot branch:https://downloads.openwrt.org/snapshots/ to get the latest version 36 | 37 | ## Install the pre-compiled binary 38 | 39 | **homebrew** (may not be the latest version): 40 | 41 | ```sh 42 | $ brew install gateway-go 43 | ``` 44 | homebrew pr [gateway-go](https://github.com/Homebrew/homebrew-core/blob/master/Formula/gateway-go.rb) 45 | >config file : 46 | >```text 47 | > /usr/local/etc/gateway-go/gateway-go.yaml 48 | >``` 49 | 50 | 51 | **snapcraft**: 52 | 53 | ```sh 54 | $ sudo snap install gateway-go 55 | ``` 56 | >config file : 57 | >```text 58 | > /root/snap/gateway-go/current/gateway-go.yaml 59 | >``` 60 | 61 | **scoop**: 62 | 63 | ```sh 64 | $ scoop bucket add OpenIoTHub https://github.com/OpenIoTHub/scoop-bucket.git 65 | $ scoop install gateway-go 66 | ``` 67 | 68 | **deb/rpm**: 69 | 70 | Download the `.deb` or `.rpm` from the [releases page][releases] and 71 | install with `dpkg -i` and `rpm -i` respectively. 72 | >config file : 73 | >```text 74 | > /etc/gateway-go/gateway-go.yaml 75 | >``` 76 | 77 | **manually**: 78 | 79 | Download the pre-compiled binaries from the [releases page][releases] and 80 | copy to the desired location. 81 | 82 | ## Running with Docker 83 | 84 | You can also use it within a Docker container. To do that, you'll need to 85 | execute something more-or-less like the following, console will show a QR code for adding: 86 | 87 | ```sh 88 | $ docker run -it --net=host openiothub/gateway-go:latest 89 | ``` 90 | 91 | Note that the image will almost always have the last stable Go version. 92 | 93 | [releases]: https://github.com/OpenIoTHub/gateway-go/v2/releases 94 | ## Build dynamic/static Library 95 | ```shell 96 | #build and push mobile lib 97 | #install gomobile(at system cli) 98 | go install golang.org/x/mobile/cmd/gobind@latest 99 | go install golang.org/x/mobile/cmd/gomobile@latest 100 | gomobile init 101 | gomobile version 102 | go get -u golang.org/x/mobile/... 103 | # 104 | export GO111MODULE="off" 105 | gomobile bind -target=android -o gateway.aar 106 | gomobile bind -ldflags '-w -s -extldflags "-lresolve"' --target=ios,macos,iossimulator -o OpenIoTHubGateway.xcframework ./client 107 | # 108 | #https://gitee.com/OpenIoThub/mobile-lib-podspec 109 | #git tag -a 0.0.1 -m '0.0.1' 110 | #git pus --tags 111 | #pod trunk push ./OpenIoTHubGateway.podspec --skip-import-validation --allow-warnings 112 | # 113 | mvn gpg:sign-and-deploy-file -DrepositoryId=ossrh -Dfile=gateway.aar -DpomFile=gateway.pom -Durl=https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 114 | mvn deploy:deploy-file -Dfile=client.aar -DgroupId=cloud.iothub -DartifactId=gateway -Dversion=0.0.1 -Dpackaging=aar -DrepositoryId=github -Durl=https://maven.pkg.github.com/OpenIoTHub/gateway-go/v2 115 | ``` 116 | ```shell 117 | #for build windows dll 118 | echo "building windows dll" 119 | #brew install mingw-w64 120 | #sudo apt-get install binutils-mingw-w64 121 | # shellcheck disable=SC2034 122 | export CGO_ENABLED=1 123 | export CC=x86_64-w64-mingw32-gcc 124 | export CXX=x86_64-w64-mingw32-g++ 125 | export GOOS=windows GOARCH=amd64 126 | go build -tags windows -ldflags=-w -trimpath -o ./build/windows/gateway_amd64.dll -buildmode=c-shared main.go 127 | ``` 128 | ```shell 129 | #for build linux/android so file 130 | echo "building linux/android so file" 131 | #linux和Android共用动态链接库 132 | export CGO_ENABLED=1 133 | export GOARCH=amd64 134 | export GOOS=linux 135 | go build -tags linux -ldflags=-w -trimpath -o build/linux/libgateway_amd64.so -buildmode c-shared main.go 136 | # shellcheck disable=SC2034 137 | export CGO_ENABLED=1 138 | export GOARCH=arm64 139 | export GOOS=linux 140 | #sudo apt install gcc-aarch64-linux-gnu 141 | export CC=aarch64-linux-gnu-gcc 142 | ##sudo apt install g++-aarch64-linux-gnu 143 | #export CXX=aarch64-linux-gnu-g++ 144 | ##sudo apt-get install binutils-aarch64-linux-gnu 145 | #export AR=aarch64-linux-gnu-ar 146 | go build -tags linux -ldflags=-w -trimpath -o build/linux/libgateway_arm64.so -buildmode c-shared main.go 147 | ``` -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | permissions: write-all 9 | 10 | jobs: 11 | gateway-go: 12 | runs-on: ubuntu-latest 13 | env: 14 | SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }} 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | - name: Set up Go 21 | uses: actions/setup-go@v5 22 | with: 23 | go-version: ^1.24 24 | - name: Set up QEMU 25 | uses: docker/setup-qemu-action@v3 26 | - name: Set up Docker Buildx 27 | uses: docker/setup-buildx-action@v3 28 | - name: Install Snapcraft 29 | run: | 30 | sudo snap install snapcraft --classic 31 | mkdir -p $HOME/.cache/snapcraft/download 32 | mkdir -p $HOME/.cache/snapcraft/stage-packages 33 | 34 | - name: Login to Docker Hub 35 | uses: docker/login-action@v3 36 | with: 37 | username: ${{ secrets.DOCKER_USERNAME }} 38 | password: ${{ secrets.DOCKERHUB_TOKEN }} 39 | 40 | - name: Login to GitHub Container Registry 41 | uses: docker/login-action@v3 42 | with: 43 | registry: ghcr.io 44 | username: ${{ github.actor }} 45 | password: ${{ secrets.MY_GITHUB_TOKEN }} 46 | 47 | - name: Run GoReleaser 48 | uses: goreleaser/goreleaser-action@v6 49 | if: startsWith(github.ref, 'refs/tags/') 50 | with: 51 | distribution: goreleaser 52 | version: latest 53 | args: release --clean 54 | env: 55 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 56 | SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} 57 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} 58 | linux-android-windows-libs: 59 | runs-on: ubuntu-latest 60 | steps: 61 | - name: Checkout 62 | uses: actions/checkout@v4 63 | with: 64 | fetch-depth: 0 65 | - name: Set up Go 66 | uses: actions/setup-go@v5 67 | with: 68 | go-version: ^1.24 69 | - uses: nttld/setup-ndk@v1 70 | id: setup-ndk 71 | with: 72 | ndk-version: r25c 73 | add-to-path: true 74 | - name: Install requirement 75 | run: sudo apt-get update && sudo apt-get install gcc-aarch64-linux-gnu binutils-mingw-w64 gcc-mingw-w64-x86-64 -y 76 | - name: Build linux libs 77 | run: ./scripts/build_libs/buildso.sh 78 | env: 79 | ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} 80 | - name: Build windows libs 81 | run: ./scripts/build_libs/builddll.sh 82 | - name: Release .so and .dll 83 | uses: softprops/action-gh-release@v1 84 | with: 85 | files: | 86 | build/linux/* 87 | build/windows/* 88 | # macos-iOS-libs: 89 | # runs-on: macos-latest 90 | # steps: 91 | # - name: Checkout 92 | # uses: actions/checkout@v4 93 | # with: 94 | # fetch-depth: 0 95 | # - name: Set up Go 96 | # uses: actions/setup-go@v5 97 | # with: 98 | # go-version: ^1.24 99 | # - name: Build libs 100 | # run: ./scripts/build_libs/builddylib.sh 101 | # - name: Release gateway.aar 102 | # uses: softprops/action-gh-release@v1 103 | # with: 104 | # files: | 105 | # build/ios/* 106 | # build/macos/* 107 | gomobile-android-libs: 108 | runs-on: ubuntu-latest 109 | steps: 110 | - name: Checkout 111 | uses: actions/checkout@v4 112 | with: 113 | fetch-depth: 0 114 | - name: Set up Go 115 | uses: actions/setup-go@v5 116 | with: 117 | go-version: ^1.24 118 | - uses: actions/setup-java@v4 119 | with: 120 | distribution: 'temurin' 121 | java-version: '17' 122 | - uses: nttld/setup-ndk@v1 123 | id: setup-ndk 124 | with: 125 | ndk-version: r21e 126 | add-to-path: false 127 | - name: install and setup gomobile 128 | run: | 129 | go get -u google.golang.org/genproto 130 | go get -u golang.org/x/mobile/... 131 | go install golang.org/x/mobile/cmd/gobind@latest 132 | go install golang.org/x/mobile/cmd/gomobile@latest 133 | gomobile init 134 | gomobile version 135 | gomobile clean 136 | env: 137 | GO111MODULE: on 138 | ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} 139 | - name: build android aar 140 | run: gomobile bind -target=android -o gateway.aar ./client 141 | env: 142 | ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} 143 | - name: Release gateway.aar 144 | uses: softprops/action-gh-release@v1 145 | with: 146 | files: | 147 | gateway.aar 148 | gomobile-apple-libs: 149 | runs-on: macos-latest 150 | steps: 151 | - name: Checkout 152 | uses: actions/checkout@v4 153 | with: 154 | fetch-depth: 0 155 | - name: Set up Go 156 | uses: actions/setup-go@v5 157 | with: 158 | go-version: ^1.24 159 | - name: install and setup gomobile 160 | run: | 161 | go get -u google.golang.org/genproto 162 | go get -u golang.org/x/mobile/... 163 | go install golang.org/x/mobile/cmd/gobind@latest 164 | go install golang.org/x/mobile/cmd/gomobile@latest 165 | gomobile init 166 | gomobile version 167 | gomobile clean 168 | env: 169 | GO111MODULE: on 170 | - name: build ios xcframework 171 | run: gomobile bind -ldflags '-w -s -extldflags "-lresolve"' --target=ios,macos,iossimulator -o OpenIoTHubGateway.xcframework ./client 172 | - run: zip -r OpenIoTHubGateway.xcframework.zip OpenIoTHubGateway.xcframework 173 | - name: Release OpenIoTHubGateway.xcframework 174 | uses: softprops/action-gh-release@v1 175 | with: 176 | files: | 177 | OpenIoTHubGateway.xcframework.zip -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | app: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | - name: Set up Go 18 | uses: actions/setup-go@v5 19 | with: 20 | go-version: ^1.24 21 | - name: Test golang code 22 | run: go test ./... 23 | - name: Build golang code 24 | run: go build 25 | linux-android-windows-libs: 26 | runs-on: ubuntu-latest 27 | needs: app 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v4 31 | with: 32 | fetch-depth: 0 33 | - name: Set up Go 34 | uses: actions/setup-go@v5 35 | with: 36 | go-version: ^1.24 37 | - uses: nttld/setup-ndk@v1 38 | id: setup-ndk 39 | with: 40 | ndk-version: r25c 41 | add-to-path: true 42 | - name: Install requirement 43 | run: sudo apt-get update && sudo apt-get install gcc-aarch64-linux-gnu binutils-mingw-w64 gcc-mingw-w64-x86-64 -y 44 | - name: Build linux libs 45 | run: ./scripts/build_libs/buildso.sh 46 | env: 47 | ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} 48 | - name: Build windows libs 49 | run: ./scripts/build_libs/builddll.sh 50 | - uses: actions/upload-artifact@v4 51 | with: 52 | name: linux-libs 53 | path: build/linux/* 54 | - uses: actions/upload-artifact@v4 55 | with: 56 | name: android-libs 57 | path: build/android 58 | - uses: actions/upload-artifact@v4 59 | with: 60 | name: windows-libs 61 | path: build/windows/* 62 | macos-iOS-libs: 63 | runs-on: macos-latest 64 | needs: app 65 | steps: 66 | - name: Checkout 67 | uses: actions/checkout@v4 68 | with: 69 | fetch-depth: 0 70 | - name: Set up Go 71 | uses: actions/setup-go@v5 72 | with: 73 | go-version: ^1.24 74 | - name: Build libs 75 | run: ./scripts/build_libs/builddylib.sh 76 | - uses: actions/upload-artifact@v4 77 | with: 78 | name: macos-libs 79 | path: build/macos/* 80 | - uses: actions/upload-artifact@v4 81 | with: 82 | name: ios-libs 83 | path: build/ios/* 84 | gomobile-android-libs: 85 | runs-on: ubuntu-latest 86 | needs: app 87 | steps: 88 | - name: Checkout 89 | uses: actions/checkout@v4 90 | with: 91 | fetch-depth: 0 92 | - name: Set up Go 93 | uses: actions/setup-go@v5 94 | with: 95 | go-version: ^1.24 96 | - uses: actions/setup-java@v4 97 | with: 98 | distribution: 'temurin' 99 | java-version: '17' 100 | - uses: nttld/setup-ndk@v1 101 | id: setup-ndk 102 | with: 103 | ndk-version: r21e 104 | add-to-path: false 105 | - name: install and setup gomobile 106 | run: | 107 | go get -u google.golang.org/genproto 108 | go get -u golang.org/x/mobile/... 109 | go install golang.org/x/mobile/cmd/gobind@latest 110 | go install golang.org/x/mobile/cmd/gomobile@latest 111 | gomobile init 112 | gomobile version 113 | gomobile clean 114 | env: 115 | GO111MODULE: on 116 | ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} 117 | - name: build android aar 118 | run: gomobile bind -target=android -o gateway.aar ./client 119 | env: 120 | ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} 121 | - uses: actions/upload-artifact@v4 122 | with: 123 | name: android-aar-libs 124 | path: gateway.aar 125 | gomobile-apple-libs: 126 | runs-on: macos-latest 127 | needs: app 128 | steps: 129 | - name: Checkout 130 | uses: actions/checkout@v4 131 | with: 132 | fetch-depth: 0 133 | - name: Set up Go 134 | uses: actions/setup-go@v5 135 | with: 136 | go-version: ^1.24 137 | - name: install and setup gomobile 138 | run: | 139 | go get -u google.golang.org/genproto 140 | go get -u golang.org/x/mobile/... 141 | go install golang.org/x/mobile/cmd/gobind@latest 142 | go install golang.org/x/mobile/cmd/gomobile@latest 143 | gomobile init 144 | gomobile version 145 | gomobile clean 146 | env: 147 | GO111MODULE: on 148 | - name: build ios xcframework 149 | run: gomobile bind -ldflags '-w -s -extldflags "-lresolve"' --target=ios,macos,iossimulator -o OpenIoTHubGateway.xcframework ./client 150 | - run: zip -r OpenIoTHubGateway.xcframework.zip OpenIoTHubGateway.xcframework 151 | - uses: actions/upload-artifact@v4 152 | with: 153 | name: ios-macos-xcframework-libs 154 | path: OpenIoTHubGateway.xcframework.zip 155 | goreleaser: 156 | runs-on: ubuntu-latest 157 | steps: 158 | - name: Checkout 159 | uses: actions/checkout@v4 160 | with: 161 | fetch-depth: 0 162 | - name: Set up Go 163 | uses: actions/setup-go@v5 164 | with: 165 | go-version: ^1.24 166 | - name: Set up QEMU 167 | uses: docker/setup-qemu-action@v3 168 | - name: Set up Docker Buildx 169 | uses: docker/setup-buildx-action@v3 170 | # - name: Install Snapcraft 171 | # uses: samuelmeuli/action-snapcraft@v3 172 | - name: Install Snapcraft 173 | run: | 174 | sudo snap install snapcraft --classic 175 | mkdir -p $HOME/.cache/snapcraft/download 176 | mkdir -p $HOME/.cache/snapcraft/stage-packages 177 | - name: Run GoReleaser 178 | uses: goreleaser/goreleaser-action@v6 179 | with: 180 | distribution: goreleaser 181 | version: latest 182 | args: release --clean --snapshot 183 | env: 184 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} 185 | - name: Upload assets 186 | uses: actions/upload-artifact@v4 187 | with: 188 | name: goreleaser-dist 189 | path: dist/* -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # This is an example goreleaser.yaml file with some sane defaults. 2 | # Make sure to check the documentation at http://goreleaser.com 3 | version: 2 4 | 5 | before: 6 | hooks: 7 | # You may remove this if you don't use go modules. 8 | - go mod download 9 | # you may remove this if you don't need go generate 10 | - go generate ./... 11 | builds: 12 | - env: 13 | - CGO_ENABLED=0 14 | flags: 15 | - -trimpath 16 | goos: 17 | - android 18 | - linux 19 | - windows 20 | - darwin 21 | # - freebsd 22 | goarch: 23 | - '386' 24 | - amd64 25 | - arm 26 | - arm64 27 | - mips 28 | - mipsle 29 | - mips64 30 | - mips64le 31 | goarm: 32 | - '5' 33 | - '6' 34 | - '7' 35 | gomips: 36 | - hardfloat 37 | - softfloat 38 | ignore: 39 | # we only need the arm64 build on android 40 | - goos: android 41 | goarch: arm 42 | - goos: android 43 | goarch: '386' 44 | - goos: android 45 | goarch: amd64 46 | ldflags: 47 | - -s -w -X github.com/OpenIoTHub/gateway-go/v2/info.Version={{.Version}} -X github.com/OpenIoTHub/gateway-go/v2/info.Commit={{.Commit}} -X github.com/OpenIoTHub/gateway-go/v2/info.Date={{ .CommitDate }} -X github.com/OpenIoTHub/gateway-go/v2/info.BuiltBy=goreleaser 48 | 49 | archives: 50 | # use zip for windows archives 51 | - format_overrides: 52 | - goos: windows 53 | format: zip 54 | # this name template makes the OS and Arch compatible with the results of uname. 55 | name_template: >- 56 | {{ .ProjectName }}_ 57 | {{- .Version }}_ 58 | {{- .Os }}_ 59 | {{- if eq .Arch "amd64" }}x86_64 60 | {{- else if eq .Arch "386" }}i386 61 | {{- else }}{{ .Arch }}{{ end }} 62 | {{- if .Mips }}_{{ .Mips }}{{ end }} 63 | {{- if .Arm }}v{{ .Arm }}{{ end }} 64 | 65 | checksum: 66 | name_template: 'checksums.txt' 67 | snapshot: 68 | version_template: "{{ incpatch .Version }}-devel" 69 | changelog: 70 | sort: asc 71 | filters: 72 | exclude: 73 | - '^docs:' 74 | - '^test:' 75 | 76 | #brews: 77 | # - tap: 78 | # owner: OpenIoTHub 79 | # name: homebrew-tap 80 | # folder: Formula 81 | # homepage: http://github.com/OpenIoTHub 82 | # description: OpenIoTHub GateWay 83 | # test: | 84 | # system "#{bin}/gateway-go -v" 85 | #scoop: 86 | # bucket: 87 | # owner: OpenIoTHub 88 | # name: scoop-bucket 89 | # homepage: http://github.com/OpenIoTHub 90 | # description: OpenIoTHub GateWay 91 | # license: MIT 92 | nfpms: 93 | - file_name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}' 94 | homepage: http://github.com/OpenIoTHub 95 | description: OpenIoTHub GateWay 96 | maintainer: OpenIoTHub 97 | license: MIT 98 | vendor: gateway-go 99 | formats: 100 | - deb 101 | - rpm 102 | scripts: 103 | preinstall: "scripts/install_remove/preinstall.sh" 104 | postinstall: "scripts/install_remove/postinstall.sh" 105 | preremove: "scripts/install_remove/preremove.sh" 106 | postremove: "scripts/install_remove/postremove.sh" 107 | contents: 108 | - src: systemd/** 109 | dst: /etc/systemd/system 110 | - src: gateway-go.yaml 111 | dst: /etc/gateway-go/gateway-go.yaml 112 | type: config 113 | snapcrafts: 114 | - name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}' 115 | summary: OpenIoTHub GateWay. 116 | description: | 117 | OpenIoTHub GateWay 118 | grade: stable 119 | confinement: strict 120 | # confinement: classic 121 | publish: true 122 | apps: 123 | gateway-go: 124 | plugs: ["network", "network-bind"] 125 | daemon: simple 126 | command: gateway-go 127 | dockers: 128 | - image_templates: 129 | - "{{ .Env.DOCKER_USERNAME }}/gateway-go:{{ .Tag }}-amd64" 130 | - "ghcr.io/{{ tolower .Env.GITHUB_REPOSITORY }}:{{ .Tag }}-amd64" 131 | use: buildx 132 | build_flag_templates: 133 | - "--platform=linux/amd64" 134 | - "--label=org.opencontainers.image.created={{.Date}}" 135 | - "--label=org.opencontainers.image.title={{.ProjectName}}" 136 | - "--label=org.opencontainers.image.revision={{.FullCommit}}" 137 | - "--label=org.opencontainers.image.version={{.Version}}" 138 | 139 | - image_templates: 140 | - "{{ .Env.DOCKER_USERNAME }}/gateway-go:{{ .Tag }}-arm64" 141 | - "ghcr.io/{{ tolower .Env.GITHUB_REPOSITORY }}:{{ .Tag }}-arm64" 142 | use: buildx 143 | build_flag_templates: 144 | - "--platform=linux/arm64" 145 | - "--label=org.opencontainers.image.created={{.Date}}" 146 | - "--label=org.opencontainers.image.title={{.ProjectName}}" 147 | - "--label=org.opencontainers.image.revision={{.FullCommit}}" 148 | - "--label=org.opencontainers.image.version={{.Version}}" 149 | goarch: arm64 150 | 151 | - image_templates: 152 | - "{{ .Env.DOCKER_USERNAME }}/gateway-go:{{ .Tag }}-armv7" 153 | - "ghcr.io/{{ tolower .Env.GITHUB_REPOSITORY }}:{{ .Tag }}-armv7" 154 | use: buildx 155 | build_flag_templates: 156 | - "--platform=linux/arm/v7" 157 | - "--label=org.opencontainers.image.created={{.Date}}" 158 | - "--label=org.opencontainers.image.title={{.ProjectName}}" 159 | - "--label=org.opencontainers.image.revision={{.FullCommit}}" 160 | - "--label=org.opencontainers.image.version={{.Version}}" 161 | goarch: arm 162 | goarm: 7 163 | 164 | docker_manifests: 165 | - name_template: "{{ .Env.DOCKER_USERNAME }}/gateway-go:{{ .Tag }}" 166 | image_templates: 167 | - "{{ .Env.DOCKER_USERNAME }}/gateway-go:{{ .Tag }}-amd64" 168 | - "{{ .Env.DOCKER_USERNAME }}/gateway-go:{{ .Tag }}-arm64" 169 | - "{{ .Env.DOCKER_USERNAME }}/gateway-go:{{ .Tag }}-armv7" 170 | 171 | - name_template: "{{ .Env.DOCKER_USERNAME }}/gateway-go:latest" 172 | image_templates: 173 | - "{{ .Env.DOCKER_USERNAME }}/gateway-go:{{ .Tag }}-amd64" 174 | - "{{ .Env.DOCKER_USERNAME }}/gateway-go:{{ .Tag }}-arm64" 175 | - "{{ .Env.DOCKER_USERNAME }}/gateway-go:{{ .Tag }}-armv7" 176 | 177 | - name_template: "ghcr.io/{{ tolower .Env.GITHUB_REPOSITORY }}:{{ .Tag }}" 178 | image_templates: 179 | - "ghcr.io/{{ tolower .Env.GITHUB_REPOSITORY }}:{{ .Tag }}-amd64" 180 | - "ghcr.io/{{ tolower .Env.GITHUB_REPOSITORY }}:{{ .Tag }}-arm64" 181 | - "ghcr.io/{{ tolower .Env.GITHUB_REPOSITORY }}:{{ .Tag }}-armv7" 182 | 183 | - name_template: "ghcr.io/{{ tolower .Env.GITHUB_REPOSITORY }}:latest" 184 | image_templates: 185 | - "ghcr.io/{{ tolower .Env.GITHUB_REPOSITORY }}:{{ .Tag }}-amd64" 186 | - "ghcr.io/{{ tolower .Env.GITHUB_REPOSITORY }}:{{ .Tag }}-arm64" 187 | - "ghcr.io/{{ tolower .Env.GITHUB_REPOSITORY }}:{{ .Tag }}-armv7" -------------------------------------------------------------------------------- /netservice/handle/handle.go: -------------------------------------------------------------------------------- 1 | package handle 2 | 3 | import ( 4 | connect "github.com/OpenIoTHub/gateway-go/v2/netservice/services/connect/conn" 5 | "github.com/OpenIoTHub/gateway-go/v2/netservice/services/connect/service" 6 | "github.com/OpenIoTHub/gateway-go/v2/netservice/services/login" 7 | "github.com/OpenIoTHub/utils/v2/models" 8 | "github.com/OpenIoTHub/utils/v2/msg" 9 | "github.com/OpenIoTHub/utils/v2/net/p2p/gateway" 10 | "log" 11 | 12 | "net" 13 | //"github.com/xtaci/smux" 14 | "github.com/libp2p/go-yamux" 15 | ) 16 | 17 | func HandleStream(stream net.Conn, tokenStr string) { 18 | defer func() { 19 | if err := recover(); err != nil { 20 | log.Printf("panic HandleStream: %+v", err) 21 | } 22 | }() 23 | var err error 24 | var tokenModel *models.TokenClaims 25 | if tokenStr != "" { 26 | tokenModel, err = models.DecodeUnverifiedToken(tokenStr) 27 | if err != nil { 28 | log.Println(err.Error()) 29 | //return 30 | } 31 | } 32 | rawMsg, err := msg.ReadMsg(stream) 33 | if err != nil { 34 | log.Println(err.Error() + "从stream读取数据错误") 35 | stream.Close() 36 | return 37 | } 38 | //log.Printf("begin Swc") 39 | switch m := rawMsg.(type) { 40 | case *models.ConnectTCP: 41 | { 42 | log.Printf("tcp") 43 | err = connect.JoinTCP(stream, m.TargetIP, m.TargetPort) 44 | if err != nil { 45 | log.Println(err.Error()) 46 | return 47 | } 48 | } 49 | case *models.ConnectSTCP: 50 | { 51 | log.Printf("stcp") 52 | err = connect.JoinSTCP(stream, m.TargetIP, m.TargetPort) 53 | if err != nil { 54 | log.Println(err.Error()) 55 | return 56 | } 57 | } 58 | case *models.ConnectUDP: 59 | { 60 | log.Printf("udp") 61 | err = connect.JoinUDP(stream, m.TargetIP, m.TargetPort) 62 | if err != nil { 63 | log.Println(err.Error()) 64 | return 65 | } 66 | } 67 | case *models.ConnectSerialPort: 68 | { 69 | log.Printf("sertp") 70 | err = connect.JoinSerialPort(stream, m) 71 | if err != nil { 72 | log.Println(err.Error()) 73 | return 74 | } 75 | } 76 | 77 | case *models.ConnectWs: 78 | { 79 | log.Printf("wstp") 80 | err = connect.JoinWs(stream, m.TargetUrl, m.Protocol, m.Origin) 81 | if err != nil { 82 | log.Println(err.Error()) 83 | return 84 | } 85 | } 86 | 87 | case *models.ConnectWss: 88 | { 89 | log.Printf("wsstp") 90 | err = connect.JoinWss(stream, m.TargetUrl, m.Protocol, m.Origin) 91 | if err != nil { 92 | log.Println(err.Error()) 93 | return 94 | } 95 | } 96 | case *models.ConnectSSH: 97 | { 98 | log.Printf("ssh") 99 | err = connect.JoinSSH(stream, m.TargetIP, m.TargetPort, m.UserName, m.PassWord) 100 | if err != nil { 101 | log.Println(err.Error()) 102 | return 103 | } 104 | } 105 | case *models.NewService: 106 | { 107 | //log.Printf("case *models.NewService") 108 | err = service.ServiceHdl(stream, m) 109 | if err != nil { 110 | log.Println(err.Error()) 111 | return 112 | } 113 | } 114 | case *models.NewSubSession: 115 | { 116 | //if tokenStr == "" { 117 | // stream.Close() 118 | // return 119 | //} 120 | //:TODO 新创建一个全新的子连接 121 | log.Printf("newSubSession") 122 | //snappyConn, err := modelsSnappy.Convert(stream, []byte("BUDIS**$(&CHSKCNNCJSH")) 123 | //if err != nil { 124 | // log.Printf(err.Error()) 125 | // stream.Close() 126 | // return 127 | //} 128 | config := yamux.DefaultConfig() 129 | //config.EnableKeepAlive = false 130 | session, err := yamux.Server(stream, config) 131 | if err != nil { 132 | stream.Close() 133 | return 134 | } 135 | go HandleSession(session, tokenStr) 136 | } 137 | 138 | case *models.RequestNewWorkConn: 139 | { 140 | //if tokenStr == "" { 141 | // stream.Close() 142 | // return 143 | //} 144 | log.Println("server请求一个新的工作连接") 145 | stream.Close() 146 | go newWorkConn(tokenStr) 147 | } 148 | 149 | case *models.Ping: 150 | { 151 | //log.Printf("Ping from server") 152 | err = msg.WriteMsg(stream, &models.Pong{}) 153 | if err != nil { 154 | log.Println(err.Error()) 155 | } 156 | //TODO 防止未关闭的连接,取决于请求方是否关闭 157 | //stream.Close() 158 | } 159 | 160 | case *models.ReqNewP2PCtrlAsServer: 161 | { 162 | log.Printf("作为listener方式从洞中获取kcp连接") 163 | if tokenModel == nil { 164 | stream.Close() 165 | return 166 | } 167 | go func() { 168 | session, listener, err := gateway.MakeP2PSessionAsServer(stream, m, tokenModel) 169 | if err != nil { 170 | if listener != nil { 171 | listener.Close() 172 | } 173 | log.Println("gateway.MakeP2PSessionAsServer:", err) 174 | return 175 | } 176 | HandleSession(session, tokenStr) 177 | if listener != nil { 178 | listener.Close() 179 | } 180 | }() 181 | 182 | } 183 | case *models.ReqNewP2PCtrlAsClient: 184 | { 185 | log.Printf("作为dial方式从从洞中创建kcp连接") 186 | if tokenModel == nil { 187 | stream.Close() 188 | return 189 | } 190 | go func() { 191 | session, listener, err := gateway.MakeP2PSessionAsClient(stream, m, tokenModel) 192 | if err != nil { 193 | if listener != nil { 194 | listener.Close() 195 | } 196 | log.Println("gateway.MakeP2PSessionAsClient:", err) 197 | return 198 | } 199 | HandleSession(session, tokenStr) 200 | if listener != nil { 201 | listener.Close() 202 | } 203 | }() 204 | } 205 | // 获取检查TCP或者UDP端口状态的请求 206 | case *models.CheckStatusRequest: 207 | { 208 | //log.Println("CheckStatusRequest") 209 | switch m.Type { 210 | case "tcp", "udp", "tls": 211 | { 212 | code, message := service.CheckTcpUdpTls(m.Type, m.Addr) 213 | err := msg.WriteMsg(stream, &models.CheckStatusResponse{ 214 | Code: code, 215 | Message: message, 216 | }) 217 | if err != nil { 218 | log.Println(err.Error()) 219 | } 220 | } 221 | default: 222 | err := msg.WriteMsg(stream, &models.CheckStatusResponse{ 223 | Code: 1, 224 | Message: "type not support", 225 | }) 226 | if err != nil { 227 | log.Println(err.Error()) 228 | } 229 | } 230 | //TODO 是否关闭 231 | stream.Close() 232 | } 233 | //由于用户在服务器账户删掉了这个网关,所有网关删掉服务器登录以供新用户绑定 234 | case *models.DeleteGatewayJwt: 235 | { 236 | // log.Println("删除配置:", tokenModel.RunId) 237 | // GatewayManager.DelServer(tokenModel.RunId) 238 | // delete(ConfigMode.LoginWithTokenMap, tokenModel.RunId) 239 | // err = WriteConfigFile(ConfigMode, ConfigFilePath) 240 | // if err != nil { 241 | // log.Println(err) 242 | // err = msg.WriteMsg(stream, &models.Error{ 243 | // Code: 1, 244 | // Message: err.Error(), 245 | // }) 246 | // if err != nil { 247 | // log.Println(err.Error()) 248 | // } 249 | // return 250 | // } 251 | // err = msg.WriteMsg(stream, &models.OK{}) 252 | // if err != nil { 253 | // log.Println(err.Error()) 254 | // } 255 | stream.Close() 256 | } 257 | default: 258 | log.Printf("type err") 259 | stream.Close() 260 | } 261 | } 262 | 263 | func HandleSession(session *yamux.Session, tokenStr string) { 264 | defer func() { 265 | if session != nil { 266 | err := session.Close() 267 | if err != nil { 268 | log.Println(err.Error()) 269 | } 270 | } 271 | }() 272 | defer func() { 273 | if err := recover(); err != nil { 274 | log.Printf("panic HandleSession: %+v", err) 275 | } 276 | }() 277 | for { 278 | // Accept a stream 279 | if session == nil { 280 | return 281 | } 282 | stream, err := session.AcceptStream() 283 | if err != nil { 284 | log.Println("accept stream form session got err:" + err.Error()) 285 | if stream != nil { 286 | stream.Close() 287 | } 288 | break 289 | } 290 | //log.Println("获取到一个连接需要处理") 291 | go HandleStream(stream, tokenStr) 292 | } 293 | } 294 | 295 | // 新创建的工作连接 296 | func newWorkConn(tokenStr string) { 297 | if tokenStr == "" { 298 | return 299 | } 300 | conn, err := login.LoginWorkConn(tokenStr) 301 | if err != nil { 302 | log.Println("创建一个到服务端的新的工作连接失败:") 303 | log.Println(err.Error()) 304 | if conn != nil { 305 | conn.Close() 306 | } 307 | return 308 | } 309 | log.Println("创建一个到服务端的新的工作连接成功!") 310 | go HandleStream(conn, tokenStr) 311 | } 312 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= 4 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 5 | github.com/OpenIoTHub/getip/v2 v2.0.1 h1:Bi8ACVyun0cwQBQiaslHDzw3dPbYBqB1GU05xhtoYNQ= 6 | github.com/OpenIoTHub/getip/v2 v2.0.1/go.mod h1:FB7ds/p5Iz/na1BPth74dsn0RGAD7mVn4akDjzep25s= 7 | github.com/OpenIoTHub/openiothub_grpc_api v1.2.4 h1:QM9nWiuvZKRMbMqZb+y14JTS8tyTPDM8LTphqZQfcNQ= 8 | github.com/OpenIoTHub/openiothub_grpc_api v1.2.4/go.mod h1:1lYU8qHaec044bNhMs1yVj/3Bgs5B7+JJNGwLCvisFo= 9 | github.com/OpenIoTHub/utils/v2 v2.0.4 h1:kT1+IexhkOAHFCC5gd45T3GrABdAfbIAhh7pkvZrIhE= 10 | github.com/OpenIoTHub/utils/v2 v2.0.4/go.mod h1:24QKtfV1+Rm8tXcZrKR8PtW0PEqY3aUMNzGTUbrR4sA= 11 | github.com/OpenIoTHub/water v0.0.3 h1:9r6IWHNJEw69x84Wf5fC0F5rUu7RAeenCOzmmiTMc0E= 12 | github.com/OpenIoTHub/water v0.0.3/go.mod h1:zY+k4u4Tc8vCGoAHJxydcuoow/U+oGW5ptfwom/ZM6M= 13 | github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= 14 | github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= 15 | github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= 16 | github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= 17 | github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= 18 | github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= 19 | github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= 20 | github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= 21 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 22 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 23 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 24 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 25 | github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= 26 | github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= 27 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 28 | github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= 29 | github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= 30 | github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= 31 | github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= 32 | github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= 33 | github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 34 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 35 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 36 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 37 | github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 38 | github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 39 | github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= 40 | github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= 41 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 42 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 43 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 44 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 45 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 46 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 47 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 48 | github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik= 49 | github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= 50 | github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= 51 | github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= 52 | github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= 53 | github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= 54 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 55 | github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= 56 | github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 57 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 58 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 59 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 60 | github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= 61 | github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= 62 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 63 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 64 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 65 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 66 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 67 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 68 | github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688= 69 | github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU= 70 | github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= 71 | github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 72 | github.com/goccy/go-yaml v1.19.0 h1:EmkZ9RIsX+Uq4DYFowegAuJo8+xdX3T/2dwNPXbxEYE= 73 | github.com/goccy/go-yaml v1.19.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= 74 | github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= 75 | github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= 76 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 77 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 78 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 79 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 80 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 81 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 82 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 83 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 84 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 85 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 86 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 87 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 88 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 89 | github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= 90 | github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 91 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 92 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 93 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 94 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 95 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 96 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 97 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 98 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 99 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 100 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 101 | github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE= 102 | github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs= 103 | github.com/hashicorp/mdns v1.0.6 h1:SV8UcjnQ/+C7KeJ/QeVD/mdN2EmzYfcGfufcuzxfCLQ= 104 | github.com/hashicorp/mdns v1.0.6/go.mod h1:X4+yWh+upFECLOki1doUPaKpgNQII9gy4bUdCYKNhmM= 105 | github.com/jacobsa/go-serial v0.0.0-20180131005756-15cf729a72d4 h1:G2ztCwXov8mRvP0ZfjE6nAlaCX2XbykaeHdbT6KwDz0= 106 | github.com/jacobsa/go-serial v0.0.0-20180131005756-15cf729a72d4/go.mod h1:2RvX5ZjVtsznNZPEt4xwJXNJrM3VTZoQf7V6gk0ysvs= 107 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 108 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 109 | github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= 110 | github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= 111 | github.com/klauspost/reedsolomon v1.12.6 h1:8pqE9aECQG/ZFitiUD1xK/E83zwosBAZtE3UbuZM8TQ= 112 | github.com/klauspost/reedsolomon v1.12.6/go.mod h1:ggJT9lc71Vu+cSOPBlxGvBN6TfAS77qB4fp8vJ05NSA= 113 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 114 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 115 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 116 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 117 | github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= 118 | github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= 119 | github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= 120 | github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= 121 | github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= 122 | github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= 123 | github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= 124 | github.com/libp2p/go-yamux v1.4.1 h1:P1Fe9vF4th5JOxxgQvfbOHkrGqIZniTLf+ddhZp8YTI= 125 | github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= 126 | github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= 127 | github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= 128 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 129 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 130 | github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= 131 | github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= 132 | github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc= 133 | github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g= 134 | github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= 135 | github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= 136 | github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg= 137 | github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= 138 | github.com/moby/moby/client v0.2.1 h1:1Grh1552mvv6i+sYOdY+xKKVTvzJegcVMhuXocyDz/k= 139 | github.com/moby/moby/client v0.2.1/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE= 140 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 141 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 142 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 143 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 144 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 145 | github.com/multiformats/go-varint v0.1.0 h1:i2wqFp4sdl3IcIxfAonHQV9qU5OsZ4Ts9IOoETFs5dI= 146 | github.com/multiformats/go-varint v0.1.0/go.mod h1:5KVAVXegtfmNQQm/lCY+ATvDzvJJhSkUlGQV9wgObdI= 147 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 148 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 149 | github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= 150 | github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= 151 | github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= 152 | github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= 153 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 154 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 155 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 156 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 157 | github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= 158 | github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 159 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 160 | github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= 161 | github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= 162 | github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10= 163 | github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= 164 | github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= 165 | github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 166 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 167 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 168 | github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM= 169 | github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 170 | github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= 171 | github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= 172 | github.com/shoenig/go-m1cpu v0.1.7 h1:C76Yd0ObKR82W4vhfjZiCp0HxcSZ8Nqd84v+HZ0qyI0= 173 | github.com/shoenig/go-m1cpu v0.1.7/go.mod h1:KkDOw6m3ZJQAPHbrzkZki4hnx+pDRR1Lo+ldA56wD5w= 174 | github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk= 175 | github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= 176 | github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= 177 | github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= 178 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 179 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 180 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 181 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 182 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 183 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 184 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 185 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 186 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 187 | github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= 188 | github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 189 | github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= 190 | github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= 191 | github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= 192 | github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= 193 | github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= 194 | github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= 195 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 196 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 197 | github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= 198 | github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= 199 | github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= 200 | github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= 201 | github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= 202 | github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= 203 | github.com/xtaci/kcp-go/v5 v5.6.55 h1:y5BsEkQvZC0u0eLiUwdTjzA6ItNwLPVh4+x1BcPaMnE= 204 | github.com/xtaci/kcp-go/v5 v5.6.55/go.mod h1:h7RBeE7Vm9xGLE0gK1JITz09q9rO2oFqDyciQhqlgfg= 205 | github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM= 206 | github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= 207 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 208 | github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= 209 | github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 210 | go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= 211 | go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= 212 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y= 213 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ= 214 | go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= 215 | go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= 216 | go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= 217 | go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= 218 | go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= 219 | go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= 220 | go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= 221 | go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= 222 | go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= 223 | go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= 224 | go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= 225 | go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= 226 | golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= 227 | golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= 228 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 229 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 230 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 231 | golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 232 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 233 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= 234 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 235 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 236 | golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= 237 | golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= 238 | golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= 239 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 240 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 241 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 242 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 243 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 244 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 245 | golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 246 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 247 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 248 | golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 249 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 250 | golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= 251 | golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= 252 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 253 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 254 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 255 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 256 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 257 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 258 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 259 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 260 | golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 261 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 262 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 263 | golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 264 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 265 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 266 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 267 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 268 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 269 | golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= 270 | golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= 271 | golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= 272 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 273 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 274 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 275 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 276 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 277 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 278 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 279 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 280 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 281 | golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 282 | golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= 283 | golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 284 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 285 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 286 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 287 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 288 | golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 289 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 290 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 291 | golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 292 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 293 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 294 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 295 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 296 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 297 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 298 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 299 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 300 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 301 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 302 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 303 | golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 304 | golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= 305 | golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 306 | golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 307 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 308 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 309 | golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 310 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 311 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 312 | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= 313 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 314 | golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= 315 | golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= 316 | golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= 317 | golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= 318 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 319 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 320 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 321 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 322 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 323 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 324 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 325 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 326 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 327 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 328 | golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= 329 | golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= 330 | golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= 331 | golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= 332 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 333 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 334 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 335 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 336 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 337 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 338 | golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 339 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 340 | golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= 341 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 342 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 343 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 344 | golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= 345 | golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= 346 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 347 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 348 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 349 | golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= 350 | golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= 351 | gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= 352 | gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= 353 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 354 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 355 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 356 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 357 | google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= 358 | google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= 359 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 360 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 361 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 362 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 363 | google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= 364 | google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= 365 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 366 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 367 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 368 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 369 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 370 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 371 | google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= 372 | google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 373 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 374 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 375 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 376 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 377 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 378 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 379 | gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= 380 | gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= 381 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 382 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 383 | pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= 384 | pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= 385 | --------------------------------------------------------------------------------