├── 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 |
5 |
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 |
6 |
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 | [](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 |
--------------------------------------------------------------------------------