├── .github └── workflows │ └── release.yml ├── .gitignore ├── .goreleaser.yml ├── client ├── app │ ├── cache.go │ ├── cron.go │ ├── rdp.go │ ├── server.go │ ├── udp.go │ └── util.go ├── cmd │ └── main.go └── config │ └── config.go ├── common ├── clientType.go ├── msgType.go └── option.go ├── go.mod ├── go.sum ├── readme.md └── server ├── app ├── cron.go ├── svr.go └── util.go ├── cmd └── main.go ├── config └── config.go └── go.sum /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2.3.4 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: Set up Go 18 | uses: actions/setup-go@v2 19 | with: 20 | go-version: '1.17' 21 | 22 | - name: Run GoReleaser 23 | uses: goreleaser/goreleaser-action@v2 24 | with: 25 | version: latest 26 | args: release --rm-dist 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | 30 | #- name: Checkout Dist 31 | # uses: actions/checkout@v2 32 | # with: 33 | # repository: 'gocq/dist' 34 | # ref: master 35 | # ssh-key: ${{ secrets.SSH_KEY }} 36 | # path: upstream/dist 37 | 38 | #- name: Update Dist 39 | # run: | 40 | # chmod +x scripts/upload_dist.sh 41 | # ./scripts/upload_dist.sh -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | env: 2 | - GO111MODULE=on 3 | before: 4 | hooks: 5 | - go mod tidy 6 | builds: 7 | - id: nowin-server 8 | main: ./server/cmd 9 | binary: server 10 | env: 11 | - CGO_ENABLED=0 12 | - GO111MODULE=on 13 | goos: 14 | - linux 15 | - darwin 16 | goarch: 17 | - 386 18 | - amd64 19 | - arm 20 | - arm64 21 | goarm: 22 | - 7 23 | ignore: 24 | - goos: darwin 25 | goarch: arm 26 | - goos: darwin 27 | goarch: 386 28 | mod_timestamp: "{{ .CommitTimestamp }}" 29 | flags: 30 | - -trimpath 31 | ldflags: 32 | - -s -w -X main.Version=v{{.Version}} 33 | 34 | - id: win-server 35 | main: ./server/cmd 36 | binary: server 37 | env: 38 | - CGO_ENABLED=0 39 | - GO111MODULE=on 40 | goos: 41 | - windows 42 | goarch: 43 | - 386 44 | - amd64 45 | - arm 46 | # - arm64 47 | goarm: 48 | - 7 49 | mod_timestamp: "{{ .CommitTimestamp }}" 50 | flags: 51 | - -trimpath 52 | ldflags: 53 | - -s -w -X main.Version=v{{.Version}} 54 | 55 | - id: nowin-client 56 | main: ./client/cmd 57 | binary: client 58 | env: 59 | - CGO_ENABLED=0 60 | - GO111MODULE=on 61 | goos: 62 | - linux 63 | - darwin 64 | goarch: 65 | - 386 66 | - amd64 67 | - arm 68 | - arm64 69 | goarm: 70 | - 7 71 | ignore: 72 | - goos: darwin 73 | goarch: arm 74 | - goos: darwin 75 | goarch: 386 76 | mod_timestamp: "{{ .CommitTimestamp }}" 77 | flags: 78 | - -trimpath 79 | ldflags: 80 | - -s -w -X main.Version=v{{.Version}} 81 | 82 | - id: win-client 83 | main: ./client/cmd 84 | binary: client 85 | env: 86 | - CGO_ENABLED=0 87 | - GO111MODULE=on 88 | goos: 89 | - windows 90 | goarch: 91 | - 386 92 | - amd64 93 | - arm 94 | # - arm64 95 | goarm: 96 | - 7 97 | mod_timestamp: "{{ .CommitTimestamp }}" 98 | flags: 99 | - -trimpath 100 | ldflags: 101 | - -s -w -X main.Version=v{{.Version}} 102 | 103 | checksum: 104 | name_template: "{{ .ProjectName }}_checksums.txt" 105 | changelog: 106 | sort: asc 107 | filters: 108 | exclude: 109 | - "^docs:" 110 | - "^test:" 111 | - fix typo 112 | - Merge pull request 113 | - Merge branch 114 | - Merge remote-tracking 115 | - go mod tidy 116 | 117 | archives: 118 | - id: binary-server 119 | builds: 120 | - win-server 121 | name_template: "{{ .ProjectName }}_server_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" 122 | format_overrides: 123 | - goos: windows 124 | format: binary 125 | - id: binary-client 126 | builds: 127 | - win-client 128 | name_template: "{{ .ProjectName }}_client_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" 129 | format_overrides: 130 | - goos: windows 131 | format: binary 132 | - id: nowin-server 133 | builds: 134 | - nowin-server 135 | - win-server 136 | name_template: "{{ .ProjectName }}_server_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" 137 | format_overrides: 138 | - goos: windows 139 | format: zip 140 | - id: nowin-client 141 | builds: 142 | - nowin-client 143 | - win-client 144 | name_template: "{{ .ProjectName }}_client_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" 145 | format_overrides: 146 | - goos: windows 147 | format: zip 148 | #nfpms: 149 | # - license: AGPL 3.0 150 | # homepage: https://go-cqhttp.org 151 | # file_name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" 152 | # formats: 153 | # - deb 154 | # - rpm 155 | # maintainer: Mrs4s -------------------------------------------------------------------------------- /client/app/cache.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/scjtqs2/p2p_rdp/common" 6 | "math/rand" 7 | "net" 8 | "strconv" 9 | "time" 10 | ) 11 | 12 | //var cacheLock sync.Mutex 13 | 14 | // 生成seq 15 | func makeSeq() string { 16 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 17 | r.Seed(time.Now().UnixNano()) 18 | return strconv.Itoa(r.Intn(1000000)) 19 | } 20 | 21 | func (l *UdpListener) setSeq(seq string) error { 22 | return l.Cache.SetWithExpire(seq, "1", 10*time.Second) 23 | } 24 | 25 | func (l *UdpListener) checkSeq(seq string) bool { 26 | check, err := l.Cache.Get(seq) 27 | if err != nil { 28 | return false 29 | } 30 | return check == "1" 31 | } 32 | 33 | func (l *UdpListener) returnSeq(seq string, resturnAddr *net.UDPAddr) { 34 | reSeq, _ := json.Marshal(&common.UDPMsg{Code: common.UDP_TYPE_SEQ_RESPONSE, Data: []byte(seq), Seq: seq}) 35 | l.LocalConn.WriteToUDP(reSeq, resturnAddr) 36 | } 37 | -------------------------------------------------------------------------------- /client/app/cron.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "context" 5 | "github.com/robfig/cron/v3" 6 | "time" 7 | ) 8 | 9 | //用来发送心跳包 10 | 11 | func (l *UdpListener) startCron(ctx context.Context) { 12 | l.Cron = cron.New(cron.WithParser(cron.NewParser( 13 | cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor, 14 | ))) 15 | //定时执行任务 16 | l.Cron.AddFunc("*/30 * * * * *", l.keepAliveSendToSvcReport) 17 | l.Cron.AddFunc("*/5 * * * * *", l.keepaliveSendToSvcGetIp) 18 | l.Cron.AddFunc("*/5 * * * * *", l.checkStatusCron) 19 | l.Cron.Start() 20 | } 21 | 22 | // keepAliveSendToSvc 给svc侧发送心跳包 23 | func (l *UdpListener) keepAliveSendToSvcReport() { 24 | l.ReportToSvc(context.Background()) 25 | } 26 | 27 | func (l *UdpListener) keepaliveSendToSvcGetIp() { 28 | l.getIpFromSvr(context.Background()) 29 | } 30 | 31 | // 检测 p2p状态 32 | func (l *UdpListener) checkStatusCron() { 33 | if l.ClientServerIp.Addr == "" { 34 | return 35 | } 36 | if l.ClientServerIp.Addr != "" && l.ClientServerIp.Time.UnixNano() < (time.Now().UnixNano()-30*time.Second.Nanoseconds()) { 37 | l.ClientServerIp.Addr = "" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /client/app/rdp.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/pkg/errors" 7 | "github.com/scjtqs2/p2p_rdp/common" 8 | log "github.com/sirupsen/logrus" 9 | "github.com/xtaci/kcptun/generic" 10 | "github.com/xtaci/smux" 11 | "io" 12 | "net" 13 | "time" 14 | ) 15 | 16 | const ( 17 | ScaVengeTTL = 600 18 | AutoExpire = 60 19 | SmuxBuf = 4194304 20 | StreamBuf = 2097152 21 | KeepAlive = 10 22 | MTU = 1350 23 | SmuxVer = 1 24 | SndWnd = 1024 25 | RcvWnd = 1024 26 | DSCP = 0 27 | NoDelay = 1 28 | Interval = 20 29 | Resend = 2 30 | NoCongestion = 1 31 | sockbuf = 4194304 32 | AckNodelay = true 33 | Quiet = false 34 | ) 35 | 36 | type timedSession struct { 37 | session *smux.Session 38 | expiryDate time.Time 39 | } 40 | 41 | func (l *UdpListener) writeClientIpChange(ip string) { 42 | l.clientIpChange <- ip 43 | } 44 | 45 | // handleClient aggregates connection p1 on mux with 'writeLock' 46 | func handleClient(session *smux.Session, p1 net.Conn, quiet bool) { 47 | logln := func(v ...interface{}) { 48 | if !quiet { 49 | log.Println(v...) 50 | } 51 | } 52 | defer p1.Close() 53 | p2, err := session.OpenStream() 54 | if err != nil { 55 | logln(err) 56 | return 57 | } 58 | 59 | defer p2.Close() 60 | 61 | logln("stream opened", "in:", p1.RemoteAddr(), "out:", fmt.Sprint(p2.RemoteAddr(), "(", p2.ID(), ")")) 62 | defer logln("stream closed", "in:", p1.RemoteAddr(), "out:", fmt.Sprint(p2.RemoteAddr(), "(", p2.ID(), ")")) 63 | 64 | // start tunnel & wait for tunnel termination 65 | streamCopy := func(dst io.Writer, src io.ReadCloser) { 66 | if _, err := generic.Copy(dst, src); err != nil { 67 | // report protocol error 68 | if err == smux.ErrInvalidProtocol { 69 | log.Println("smux", err, "in:", p1.RemoteAddr(), "out:", fmt.Sprint(p2.RemoteAddr(), "(", p2.ID(), ")")) 70 | } 71 | } 72 | p1.Close() 73 | p2.Close() 74 | } 75 | 76 | go streamCopy(p1, p2) 77 | streamCopy(p2, p1) 78 | } 79 | 80 | func scavenger(ch chan timedSession) { 81 | // When AutoExpire is set to 0 (default), sessionList will keep empty. 82 | // Then this routine won't need to do anything; thus just terminate it. 83 | if AutoExpire <= 0 { 84 | return 85 | } 86 | 87 | ticker := time.NewTicker(time.Second) 88 | defer ticker.Stop() 89 | var sessionList []timedSession 90 | for { 91 | select { 92 | case item := <-ch: 93 | sessionList = append(sessionList, timedSession{ 94 | item.session, 95 | item.expiryDate.Add(time.Duration(ScaVengeTTL) * time.Second)}) 96 | case <-ticker.C: 97 | if len(sessionList) == 0 { 98 | continue 99 | } 100 | 101 | var newList []timedSession 102 | for k := range sessionList { 103 | s := sessionList[k] 104 | if s.session.IsClosed() { 105 | log.Println("scavenger: session normally closed:", s.session.LocalAddr()) 106 | } else if time.Now().After(s.expiryDate) { 107 | s.session.Close() 108 | log.Println("scavenger: session closed due to ttl:", s.session.LocalAddr()) 109 | } else { 110 | newList = append(newList, sessionList[k]) 111 | } 112 | } 113 | sessionList = newList 114 | } 115 | } 116 | } 117 | 118 | // server侧初始化 kcplistenser client侧初始化 tcplistener 119 | func (l *UdpListener) initListener(ctx context.Context) { 120 | var err error 121 | switch l.Conf.Type { 122 | case common.CLIENT_CLIENT_TYPE: 123 | //初始化listener 124 | localTcpAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:%d", l.Conf.RdpP2pPort)) 125 | l.RdpListener, err = net.ListenTCP("tcp", localTcpAddr) 126 | if err != nil { 127 | log.Fatalf("init rdp listener faild port=%d ,err=%s", l.Conf.RdpP2pPort, err.Error()) 128 | } 129 | case common.CLIENT_SERVER_TYPE: 130 | l.KcpListener, err = NewKcpServerWithUDPConn(l.ClientConn) 131 | if err != nil { 132 | log.Fatalf("init kcp listener faild err=%s", err.Error()) 133 | } 134 | if err := l.KcpListener.SetDSCP(DSCP); err != nil { 135 | log.Printf("SetDSCP:%s", err.Error()) 136 | } 137 | if err := l.KcpListener.SetReadBuffer(sockbuf); err != nil { 138 | log.Printf("SetReadBuffer:%s", err.Error()) 139 | } 140 | if err := l.KcpListener.SetWriteBuffer(sockbuf); err != nil { 141 | log.Printf("SetWriteBuffer:%s", err.Error()) 142 | } 143 | } 144 | } 145 | 146 | func (l *UdpListener) localRdpClientListener(ctx context.Context) { 147 | switch l.Conf.Type { 148 | case common.CLIENT_CLIENT_TYPE: 149 | for { 150 | clientAddr := <-l.clientIpChange 151 | log.Infof("更新clientip地址 addr=%s", clientAddr) 152 | //rdpListener accept 153 | go l.rdpClientLoop(clientAddr) 154 | } 155 | case common.CLIENT_SERVER_TYPE: 156 | // server侧。不需要remote addr 157 | for { 158 | if conn, err := l.KcpListener.AcceptKCP(); err == nil { 159 | log.Println("remote address:", conn.RemoteAddr()) 160 | conn.SetStreamMode(true) 161 | conn.SetWriteDelay(false) 162 | conn.SetMtu(MTU) 163 | conn.SetNoDelay(NoDelay, Interval, Resend, NoCongestion) 164 | conn.SetWindowSize(SndWnd, RcvWnd) 165 | conn.SetACKNoDelay(AckNodelay) 166 | go l.handleServerMux(generic.NewCompStream(conn)) 167 | } else { 168 | log.Printf("%+v", err) 169 | } 170 | } 171 | } 172 | } 173 | 174 | func (l *UdpListener) rdpClientLoop(clientAddr string) { 175 | createConn := func() (*smux.Session, error) { 176 | clientConn, err := NewKcpConnWithUDPConn(l.ClientConn, clientAddr) 177 | if err != nil { 178 | log.Fatalf("new kcp client faild err:%s", err.Error()) 179 | panic(err) 180 | } 181 | clientConn.SetStreamMode(true) 182 | clientConn.SetWriteDelay(false) 183 | clientConn.SetMtu(MTU) 184 | clientConn.SetNoDelay(NoDelay, Interval, Resend, NoCongestion) 185 | clientConn.SetWindowSize(SndWnd, RcvWnd) 186 | clientConn.SetACKNoDelay(AckNodelay) 187 | if err := clientConn.SetDSCP(DSCP); err != nil { 188 | log.Printf("SetDscp:%s", err.Error()) 189 | } 190 | if err := clientConn.SetReadBuffer(sockbuf); err != nil { 191 | log.Printf("SetReadBuffer:%s", err.Error()) 192 | } 193 | if err := clientConn.SetWriteBuffer(sockbuf); err != nil { 194 | log.Printf("SetWriteBuffer:%s", err.Error()) 195 | } 196 | log.Printf("smux version:%s on connection:%s -> %s", SmuxVer, clientConn.LocalAddr().String(), clientConn.RemoteAddr().String()) 197 | smuxConfig := smux.DefaultConfig() 198 | smuxConfig.Version = SmuxVer 199 | smuxConfig.MaxReceiveBuffer = SmuxBuf 200 | smuxConfig.MaxStreamBuffer = StreamBuf 201 | smuxConfig.KeepAliveInterval = time.Duration(KeepAlive) * time.Second 202 | if err := smux.VerifyConfig(smuxConfig); err != nil { 203 | log.Fatalf("%+v", err) 204 | } 205 | var session *smux.Session 206 | session, err = smux.Client(generic.NewCompStream(clientConn), smuxConfig) 207 | if err != nil { 208 | log.Errorf("createConn faild.err=%s", err.Error()) 209 | return nil, errors.Wrap(err, "createConn()") 210 | } 211 | return session, nil 212 | } 213 | // wait until a connection is ready 214 | waitConn := func() *smux.Session { 215 | for { 216 | if session, err := createConn(); err == nil { 217 | log.Infof("kcp session created") 218 | return session 219 | } else { 220 | log.Println("re-connecting:", err) 221 | time.Sleep(time.Second) 222 | } 223 | } 224 | } 225 | // start scavenger 226 | chScavenger := make(chan timedSession, 128) 227 | go scavenger(chScavenger) 228 | // start listener 229 | numconn := uint16(1) 230 | muxes := make([]timedSession, numconn) 231 | rr := uint16(0) 232 | for { 233 | rdpConn, err := l.RdpListener.AcceptTCP() 234 | if err != nil { 235 | fmt.Println("accept failed, err:", err) 236 | continue 237 | } 238 | log.Printf("tcp conn started") 239 | idx := rr % numconn 240 | // do auto expiration && reconnection 241 | if muxes[idx].session == nil || muxes[idx].session.IsClosed() || 242 | (AutoExpire > 0 && time.Now().After(muxes[idx].expiryDate)) { 243 | log.Printf("kcp conn start") 244 | muxes[idx].session = waitConn() 245 | muxes[idx].expiryDate = time.Now().Add(time.Duration(AutoExpire) * time.Second) 246 | if AutoExpire > 0 { // only when autoexpire set 247 | chScavenger <- muxes[idx] 248 | } 249 | } 250 | log.Printf("kcp conn started") 251 | go handleClient(muxes[idx].session, rdpConn, Quiet) 252 | rr++ 253 | } 254 | } 255 | 256 | // handle multiplex-ed connection 257 | func (l *UdpListener) handleServerMux(conn net.Conn) { 258 | log.Println("smux version:", SmuxVer, "on connection:", conn.LocalAddr(), `->`, conn.RemoteAddr()) 259 | // stream multiplex 260 | smuxConfig := smux.DefaultConfig() 261 | smuxConfig.Version = SmuxVer 262 | smuxConfig.MaxReceiveBuffer = SmuxBuf 263 | smuxConfig.MaxStreamBuffer = StreamBuf 264 | smuxConfig.KeepAliveInterval = time.Duration(KeepAlive) * time.Second 265 | 266 | mux, err := smux.Server(conn, smuxConfig) 267 | if err != nil { 268 | log.Println(err) 269 | return 270 | } 271 | defer mux.Close() 272 | 273 | for { 274 | stream, err := mux.AcceptStream() 275 | if err != nil { 276 | log.Println(err) 277 | return 278 | } 279 | 280 | go func(p1 *smux.Stream) { 281 | var p2 net.Conn 282 | var err error 283 | p2, err = net.Dial("tcp", l.Conf.RemoteRdpAddr) 284 | 285 | if err != nil { 286 | log.Println(err) 287 | p1.Close() 288 | return 289 | } 290 | handleServer(p1, p2, Quiet) 291 | }(stream) 292 | } 293 | } 294 | 295 | func handleServer(p1 *smux.Stream, p2 net.Conn, quiet bool) { 296 | logln := func(v ...interface{}) { 297 | if !quiet { 298 | log.Println(v...) 299 | } 300 | } 301 | 302 | defer p1.Close() 303 | defer p2.Close() 304 | 305 | logln("stream opened", "in:", fmt.Sprint(p1.RemoteAddr(), "(", p1.ID(), ")"), "out:", p2.RemoteAddr()) 306 | defer logln("stream closed", "in:", fmt.Sprint(p1.RemoteAddr(), "(", p1.ID(), ")"), "out:", p2.RemoteAddr()) 307 | 308 | // start tunnel & wait for tunnel termination 309 | streamCopy := func(dst io.Writer, src io.ReadCloser) { 310 | if _, err := generic.Copy(dst, src); err != nil { 311 | if err == smux.ErrInvalidProtocol { 312 | log.Println("smux", err, "in:", fmt.Sprint(p1.RemoteAddr(), "(", p1.ID(), ")"), "out:", p2.RemoteAddr()) 313 | } 314 | } 315 | p1.Close() 316 | p2.Close() 317 | } 318 | 319 | go streamCopy(p2, p1) 320 | streamCopy(p1, p2) 321 | } 322 | -------------------------------------------------------------------------------- /client/app/server.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "context" 5 | "github.com/bluele/gcache" 6 | "github.com/robfig/cron/v3" 7 | "github.com/scjtqs2/kcp-go/v5" 8 | "github.com/scjtqs2/p2p_rdp/client/config" 9 | "github.com/scjtqs2/p2p_rdp/common" 10 | "net" 11 | "time" 12 | ) 13 | 14 | type UdpListener struct { 15 | Conf *config.ClientConfig //本地配置信息 16 | LocalConn *net.UDPConn //本地监听conn 17 | ClientConn *net.UDPConn //本地监听conn 18 | ClientServerIp common.Ip //server侧的客户端的地址 19 | KcpListener *kcp.Listener //server侧的kcp listener 20 | RdpListener *net.TCPListener //rdp的3389端口转发 21 | RdpAddr string //rdp客户端地址 22 | Cron *cron.Cron 23 | Cache gcache.Cache 24 | Client2 *kcp.UDPSession // 通过 ClientConn连接 p2p 25 | clientIpChange chan string 26 | p2pChan chan bool 27 | } 28 | 29 | type Status struct { 30 | Status bool 31 | Time time.Time 32 | } 33 | 34 | func (l *UdpListener) Run(ctx context.Context, config *config.ClientConfig) (err error) { 35 | l.Conf = config 36 | l.ClientServerIp = common.Ip{Addr: ""} 37 | l.Cache = gcache.New(200).LRU().Expiration(10 * time.Second).Build() 38 | l.clientIpChange = make(chan string, 10) 39 | l.p2pChan = make(chan bool, 128) 40 | //固定本地端口的监听 41 | l.LocalConn, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: config.ClientPortFroSvc}) 42 | if err != nil { 43 | panic(err) 44 | } 45 | l.ClientConn, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: config.ClientPortForP2PTrance}) 46 | if err != nil { 47 | panic(err) 48 | } 49 | l.initListener(ctx) 50 | // 监听svc发送过来的数据 51 | go l.handleUdpLocal(ctx, l.LocalConn) 52 | l.ReportToSvc(ctx) 53 | l.getIpFromSvr(ctx) 54 | 55 | go l.localRdpClientListener(ctx) 56 | go l.sendP2PBackend(l.ClientConn) 57 | 58 | l.startCron(ctx) 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /client/app/udp.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "github.com/scjtqs2/p2p_rdp/common" 7 | log "github.com/sirupsen/logrus" 8 | "net" 9 | ) 10 | 11 | func (l *UdpListener) handleUdpLocal(contx context.Context, conn *net.UDPConn) { 12 | defer conn.Close() 13 | for { 14 | data := make([]byte, common.PACKAGE_SIZE) 15 | n, _, err := conn.ReadFromUDP(data) 16 | if err != nil { 17 | log.Errorf("read udp package from svc faild,error %s", err.Error()) 18 | continue 19 | } 20 | var msg common.UDPMsg 21 | err = json.Unmarshal(data[:n], &msg) 22 | if err != nil { 23 | log.Error("json parase err:%s", err.Error()) 24 | continue 25 | } 26 | ctx := context.WithValue(contx, "seq", msg.Seq) 27 | switch msg.Code { 28 | case common.UDP_TYPE_DISCOVERY: //正常拿到 clientip 29 | //处理和svr之间的通信 30 | var svcmsg common.Msg 31 | json.Unmarshal(msg.Data, &svcmsg) 32 | log.Debugf("从svc拿到数据 msg:%+v", svcmsg) 33 | l.progressSvc(ctx, svcmsg) 34 | case common.UDP_TYPE_DISCOVERY_FROCE_P2P: //强制触发p2p打洞 35 | var svcmsg common.Msg 36 | json.Unmarshal(msg.Data, &svcmsg) 37 | var clientIp common.Ip 38 | err := json.Unmarshal([]byte(svcmsg.Res.Message), &clientIp) 39 | if err != nil { 40 | log.Errorf("MESSAGE_TYPE_FOR_CLIENT_SERVER_WITH_CLIENT_IPS 解码json失败 err %s", err.Error()) 41 | } 42 | log.Infof("svc 强推消息 msg:%+v", svcmsg) 43 | l.ClientServerIp = clientIp 44 | l.makeP2P() 45 | //ip变更了 TODO 46 | if clientIp.Addr != l.ClientServerIp.Addr && l.Conf.Type == common.CLIENT_CLIENT_TYPE { 47 | l.writeClientIpChange(clientIp.Addr) 48 | } 49 | } 50 | } 51 | } 52 | 53 | func (l *UdpListener) progressSvc(ctx context.Context, msg common.Msg) { 54 | switch msg.Type { 55 | case common.MESSAGE_TYPE_FOR_CLIENT_SERVER_WITH_CLIENT_IPS: 56 | if msg.Res.Code != 0 { 57 | log.Errorf("对方客户端未就绪,%+v", msg.Res) 58 | return 59 | } 60 | var clientIp common.Ip 61 | err := json.Unmarshal([]byte(msg.Res.Message), &clientIp) 62 | if err != nil { 63 | log.Errorf("MESSAGE_TYPE_FOR_CLIENT_SERVER_WITH_CLIENT_IPS 解码json失败 err %s", err.Error()) 64 | } 65 | //ip变更了 TODO 66 | if clientIp.Addr != l.ClientServerIp.Addr { 67 | l.ClientServerIp = clientIp 68 | log.Infof("开始kcp 发送p2p包给 client侧") 69 | l.makeP2P() 70 | if l.Conf.Type == common.CLIENT_CLIENT_TYPE { 71 | l.writeClientIpChange(clientIp.Addr) 72 | } 73 | } else { 74 | l.ClientServerIp = clientIp 75 | } 76 | case common.MESSAGE_TYPE_FOR_CLIENT_CLIENT_WITH_SERVER_IPS: 77 | if msg.Res.Code != 0 { 78 | log.Errorf("对方客户端未就绪,%+v", msg.Res) 79 | return 80 | } 81 | var clientIp common.Ip 82 | err := json.Unmarshal([]byte(msg.Res.Message), &clientIp) 83 | if err != nil { 84 | log.Errorf("MESSAGE_TYPE_FOR_CLIENT_CLIENT_WITH_SERVER_IPS 解码json失败 err %s", err.Error()) 85 | } 86 | //ip变更了 TODO 87 | if clientIp.Addr != l.ClientServerIp.Addr { 88 | l.ClientServerIp = clientIp 89 | log.Infof("开始kcp 发送p2p包给 server侧") 90 | l.makeP2P() 91 | if l.Conf.Type == common.CLIENT_CLIENT_TYPE { 92 | l.writeClientIpChange(clientIp.Addr) 93 | } 94 | } else { 95 | l.ClientServerIp = clientIp 96 | } 97 | } 98 | } 99 | 100 | // 发送p2p的打洞数据包 101 | func (l *UdpListener) makeP2P() { 102 | l.p2pChan <- true 103 | } 104 | 105 | func (l *UdpListener) sendP2PBackend(conn *net.UDPConn) { 106 | for { 107 | select { 108 | case <-l.p2pChan: 109 | if l.ClientServerIp.Addr == "" { 110 | log.Error("没有获取到另一侧的udp地址") 111 | return 112 | } 113 | seq := makeSeq() 114 | ctx := context.WithValue(context.TODO(), "seq", seq) 115 | log.WithContext(ctx) 116 | msg, _ := json.Marshal(&common.UDPMsg{ 117 | Code: common.UDP_TYPE_BI_DIRECTION_HOLE, 118 | Data: []byte("我是打洞消息"), 119 | Seq: seq, 120 | }) 121 | log.Infof("开始发送打洞消息 addr=%s", l.ClientServerIp.Addr) 122 | dstAddr, _ := net.ResolveUDPAddr("udp", l.ClientServerIp.Addr) 123 | conn.WriteToUDP(msg, dstAddr) 124 | conn.WriteToUDP(msg, dstAddr) 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /client/app/util.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "context" 5 | "crypto/sha1" 6 | "encoding/json" 7 | "fmt" 8 | "github.com/scjtqs2/kcp-go/v5" 9 | "github.com/scjtqs2/p2p_rdp/client/config" 10 | "github.com/scjtqs2/p2p_rdp/common" 11 | log "github.com/sirupsen/logrus" 12 | "golang.org/x/crypto/pbkdf2" 13 | "net" 14 | "reflect" 15 | "strconv" 16 | "strings" 17 | ) 18 | 19 | // 发送信息给svc 20 | func (l *UdpListener) ReportToSvc(ctx context.Context) { 21 | log.Debugf("上报ip地址给svc,addr=%s:%d", l.Conf.ServerHost, l.Conf.ServerPort) 22 | req := &common.Msg{AppName: l.Conf.AppName} 23 | switch l.Conf.Type { 24 | case common.CLIENT_SERVER_TYPE: 25 | req.Type = common.CLIENT_SERVER_TYPE 26 | case common.CLIENT_CLIENT_TYPE: 27 | req.Type = common.CLIENT_CLIENT_TYPE 28 | } 29 | seq := makeSeq() 30 | req.Seq = seq 31 | req.AppName = l.Conf.AppName 32 | msg, _ := json.Marshal(req) 33 | msg1, _ := json.Marshal(&common.UDPMsg{ 34 | Code: common.UDP_TYPE_REPORT, 35 | Data: msg, 36 | }) 37 | //l.Client2.Write(msg1) 38 | svcAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", l.Conf.ServerHost, l.Conf.ServerPort)) 39 | if err != nil { 40 | log.Fatalf("错误的 svc 地址") 41 | } 42 | l.ClientConn.WriteToUDP(msg1, svcAddr) 43 | } 44 | 45 | func (l *UdpListener) getIpFromSvr(ctx context.Context) { 46 | log.Debugf("发送消息给svc,addr=%s:%d", l.Conf.ServerHost, l.Conf.ServerPort) 47 | req := &common.Msg{AppName: l.Conf.AppName} 48 | switch l.Conf.Type { 49 | case common.CLIENT_SERVER_TYPE: 50 | req.Type = common.CLIENT_SERVER_TYPE 51 | case common.CLIENT_CLIENT_TYPE: 52 | req.Type = common.CLIENT_CLIENT_TYPE 53 | } 54 | seq := makeSeq() 55 | req.Seq = seq 56 | req.AppName = l.Conf.AppName 57 | msg, _ := json.Marshal(req) 58 | msg1, _ := json.Marshal(&common.UDPMsg{ 59 | Code: common.UDP_TYPE_GET_CLIENT_IP, 60 | Data: msg, 61 | }) 62 | //l.Client1.Write(msg1) 63 | svcAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", l.Conf.ServerHost, l.Conf.ServerPort)) 64 | if err != nil { 65 | log.Fatalf("错误的 svc 地址") 66 | } 67 | l.LocalConn.WriteToUDP(msg1, svcAddr) 68 | } 69 | 70 | func parseAddr(addr string) *net.UDPAddr { 71 | t := strings.Split(addr, ":") 72 | port, _ := strconv.Atoi(t[1]) 73 | return &net.UDPAddr{ 74 | IP: net.ParseIP(t[0]), 75 | Port: port, 76 | } 77 | } 78 | 79 | func IsNul(i interface{}) bool { 80 | vi := reflect.ValueOf(i) 81 | if vi.Kind() == reflect.Ptr { 82 | return vi.IsNil() 83 | } 84 | return false 85 | } 86 | 87 | func NewKcpServer(config *config.ClientConfig) (*kcp.Listener, error) { 88 | key := pbkdf2.Key([]byte("demo pass"), []byte("demo salt"), 1024, 32, sha1.New) 89 | block, _ := kcp.NewAESBlockCrypt(key) 90 | return kcp.ListenWithOptions(fmt.Sprintf("0.0.0.0:%d", config.ClientPortForP2PTrance), block, 10, 3) 91 | } 92 | 93 | // serves KCP protocol for a single packet connection. 94 | func NewKcpServerWithUDPConn(conn net.PacketConn) (*kcp.Listener, error) { 95 | key := pbkdf2.Key([]byte("demo pass"), []byte("demo salt"), 1024, 32, sha1.New) 96 | block, _ := kcp.NewAESBlockCrypt(key) 97 | return kcp.ServeConn(block, 10, 3, conn) 98 | } 99 | 100 | func NewKcpClient(addr string, localPort int) (*kcp.UDPSession, error) { 101 | key := pbkdf2.Key([]byte("demo pass"), []byte("demo salt"), 1024, 32, sha1.New) 102 | block, _ := kcp.NewAESBlockCrypt(key) 103 | localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: localPort} 104 | return kcp.DialWithOptions(addr, block, 10, 3, localAddr) 105 | } 106 | 107 | func NewKcpConnWithUDPConn(conn net.PacketConn, raddr string) (*kcp.UDPSession, error) { 108 | key := pbkdf2.Key([]byte("demo pass"), []byte("demo salt"), 1024, 32, sha1.New) 109 | block, _ := kcp.NewAESBlockCrypt(key) 110 | return kcp.NewConn(raddr, block, 10, 3, conn) 111 | } 112 | -------------------------------------------------------------------------------- /client/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "github.com/scjtqs2/p2p_rdp/client/app" 7 | "github.com/scjtqs2/p2p_rdp/client/config" 8 | "github.com/scjtqs2/utils/util" 9 | log "github.com/sirupsen/logrus" 10 | "os" 11 | "os/signal" 12 | ) 13 | 14 | var ( 15 | h bool 16 | d bool 17 | Version = "v1.0.0" 18 | Build string 19 | configPath = "config.yml" 20 | ) 21 | 22 | func init() { 23 | var debug bool 24 | flag.BoolVar(&d, "d", false, "running as a daemon") 25 | flag.BoolVar(&h, "h", false, "this help") 26 | flag.StringVar(&configPath, "c", "config.yml", "config file path default is config.yml") 27 | flag.Parse() 28 | LogLevel := log.InfoLevel 29 | if debug { 30 | log.SetReportCaller(true) 31 | LogLevel = log.DebugLevel 32 | } 33 | log.SetFormatter(&log.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"}) //使用json的格式 34 | log.SetLevel(LogLevel) 35 | } 36 | 37 | func main() { 38 | if h { 39 | help() 40 | } 41 | if d { 42 | util.Daemon() 43 | } 44 | ctx:=context.Background() 45 | log.WithContext(ctx) 46 | conf := config.GetConfigFronPath(configPath) 47 | conf.Save(configPath) 48 | log.Infof("welcome to use p2p_rdp client by scjtqs https://github.com/scjtqs2/p2p_rdp/client %s,build in %s", Version, Build) 49 | udplistener := &app.UdpListener{} 50 | udplistener.Run(ctx,conf) 51 | defer func() { 52 | if udplistener.LocalConn != nil { 53 | udplistener.LocalConn.Close() 54 | } 55 | }() 56 | defer udplistener.Cron.Stop() 57 | c := make(chan os.Signal, 1) 58 | signal.Notify(c, os.Interrupt, os.Kill) 59 | log.Infof("初始化完成") 60 | <-c 61 | } 62 | 63 | // help cli命令行-h的帮助提示 64 | func help() { 65 | log.Infof(`p2p_rdp client 66 | version: %s 67 | built-on: %s 68 | 69 | Usage: 70 | 71 | client [OPTIONS] 72 | 73 | Options: 74 | `, Version, Build) 75 | flag.PrintDefaults() 76 | os.Exit(0) 77 | } 78 | -------------------------------------------------------------------------------- /client/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/go-yaml/yaml" 5 | "github.com/scjtqs2/p2p_rdp/common" 6 | "github.com/scjtqs2/utils/util" 7 | ) 8 | 9 | type ClientConfig struct { 10 | ServerHost string //服务器地址 11 | ServerPort int //服务端 12 | Type string //rdp的服务端还是客户端 13 | ClientPortFroSvc int //客户端开的端口 为了和svc进行p2p通信 14 | ClientPortForP2PTrance int //客户端开的端口,为了rdp的p2p通信 15 | RdpP2pPort int //rdp的转发请求端口 16 | AppName string //当前的服务组名称。client侧和server侧必须有相同的appName才能匹配上。 17 | RemoteRdpAddr string //要控制的rdp的地址 18 | } 19 | 20 | // 通过路径获取配置信息 21 | func GetConfigFronPath(c string) *ClientConfig { 22 | conf := &ClientConfig{} 23 | if !util.PathExists(c) { 24 | conf = defaultConf() 25 | } else { 26 | err := yaml.Unmarshal([]byte(util.ReadAllText(c)), conf) 27 | if err != nil { 28 | conf = defaultConf() 29 | } 30 | } 31 | return parseConfFromEnv(conf) 32 | } 33 | 34 | func defaultConf() *ClientConfig { 35 | return &ClientConfig{ 36 | ServerHost: "1.1.1.1", 37 | ServerPort: 30124, 38 | Type: common.CLIENT_CLIENT_TYPE, 39 | AppName: "rdp-p2p", 40 | RdpP2pPort: 30122, 41 | ClientPortForP2PTrance: 30123, 42 | ClientPortFroSvc: 30124, 43 | RemoteRdpAddr: "127.0.0.1:3389", 44 | } 45 | } 46 | 47 | // 从环境变量中替换配置文件 48 | func parseConfFromEnv(c *ClientConfig) *ClientConfig { 49 | //todo 50 | return c 51 | } 52 | 53 | // 保存配置文件 54 | func (c *ClientConfig) Save(p string) error { 55 | s, _ := yaml.Marshal(c) 56 | return util.WriteAllText(p, string(s)) 57 | } 58 | -------------------------------------------------------------------------------- /common/clientType.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | const ( 4 | CLIENT_SERVER_TYPE = "client_server_type" //client的服务侧请求 5 | CLIENT_CLIENT_TYPE = "client_client_type" //client的客户侧请求 6 | ) 7 | 8 | const ( 9 | UDP_TYPE_KEEP_ALIVE = iota // 0:心跳 10 | UDP_TYPE_BI_DIRECTION_HOLE // 1:打洞消息 11 | UDP_TYPE_TRANCE // 2:转发消息 12 | UDP_TYPE_DISCOVERY // 3:和svc服务之间的通信 13 | UDP_TYPE_RDP // 4:udp协议的rdp数据转发 14 | UDP_TYPE_SEQ_RESPONSE // 5: 回包确认包收到了 15 | UDP_TYPE_REPORT // 6: 纯上报 16 | UDP_TYPE_GET_CLIENT_IP // 7: 上报,并拿到另一侧的ip地址 17 | UDP_TYPE_DISCOVERY_FROCE_P2P // 8: svr下发通知,强行发送p2p打洞消息 18 | ) 19 | 20 | var PACKAGE_SIZE int = 65535 //tcp的包分割大小 21 | -------------------------------------------------------------------------------- /common/msgType.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | const ( 4 | MESSAGE_TYPE_FOR_CLIENT_SERVER_WITH_CLIENT_IPS = "MESSAGE_TYPE_FOR_CLIENT_SERVER_WITH_CLIENT_IPS" //给服务侧的客户端返回客户侧的ip列表 5 | MESSAGE_TYPE_FOR_CLIENT_CLIENT_WITH_SERVER_IPS = "MESSAGE_TYPE_FOR_CLIENT_CLIENT_WITH_SERVER_IPS" //给客户侧的客户端返回服务侧的ip列表。单个 6 | MESSAGE_TYPE_KEEP_ALIVE = "MESSAGE_TYPE_KEEP_ALIVE" //心跳 保活 7 | ) 8 | 9 | -------------------------------------------------------------------------------- /common/option.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | type Msg struct { 9 | Type string //消息类型 10 | AppName string //应用类型 11 | Res Res 12 | Seq string 13 | } 14 | 15 | 16 | type Res struct { 17 | Code int64 //错误码 0成功 18 | Message string //消息 19 | } 20 | 21 | type Ip struct { 22 | Addr string 23 | Time time.Time 24 | } 25 | 26 | type Peer struct { 27 | Server Ip 28 | Client Ip 29 | } 30 | 31 | type UDPMsg struct { 32 | Code int //0:心跳 1:打洞消息 2:转发消息 3:和svc之间的通信 33 | Data []byte //转发/携带 的数据 34 | Seq string //包标记 35 | Count int //当前标记的总包数量 36 | Offset int //当前包的指针 37 | Lenth int //完整的包长度 38 | Addr *net.UDPAddr //透传的对方的地址 39 | } 40 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/scjtqs2/p2p_rdp 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/bluele/gcache v0.0.2 7 | github.com/go-yaml/yaml v2.1.0+incompatible 8 | github.com/jonboulle/clockwork v0.2.2 // indirect 9 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible 10 | github.com/lestrrat-go/strftime v1.0.5 // indirect 11 | github.com/pkg/errors v0.9.1 12 | github.com/robfig/cron/v3 v3.0.1 13 | github.com/scjtqs2/kcp-go/v5 v5.6.2-0.20211207064710-e23be26b69b1 14 | github.com/scjtqs2/utils v0.0.0-20211110033646-3f01f3014931 15 | github.com/sirupsen/logrus v1.8.1 16 | github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 17 | github.com/xtaci/kcptun v0.0.0-20210922134509-94c9cacf4f87 18 | github.com/xtaci/smux v1.5.16 19 | golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de 20 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 21 | gopkg.in/yaml.v2 v2.4.0 // indirect 22 | ) 23 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw= 3 | github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= 4 | github.com/coreos/go-iptables v0.4.2/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= 9 | github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= 10 | github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= 11 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 12 | github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= 13 | github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= 14 | github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= 15 | github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 16 | github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= 17 | github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= 18 | github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54= 19 | github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo= 20 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 21 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 22 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 23 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 24 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 25 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 26 | github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= 27 | github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= 28 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= 29 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= 30 | github.com/lestrrat-go/strftime v1.0.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa2xeOE= 31 | github.com/lestrrat-go/strftime v1.0.5/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= 32 | github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 h1:ULR/QWMgcgRiZLUjSSJMU+fW+RDMstRdmnDWj9Q+AsA= 33 | github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls= 34 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 35 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 36 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 37 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 38 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 39 | github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= 40 | github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= 41 | github.com/scjtqs2/kcp-go/v5 v5.6.2-0.20211207064710-e23be26b69b1 h1:C0788IDBMhbnU4rJF5jVmFswyFSaYaaCGrlEmKKne4Q= 42 | github.com/scjtqs2/kcp-go/v5 v5.6.2-0.20211207064710-e23be26b69b1/go.mod h1:rwG5U4jSrFwAIq3HOUt9fI6wiAGjalo+VTB1KIalO+Q= 43 | github.com/scjtqs2/utils v0.0.0-20211110033646-3f01f3014931 h1:y81SHMqf2P8K2zX9p8LI12SPe0wu7aUkoCZ9YwHgLcw= 44 | github.com/scjtqs2/utils v0.0.0-20211110033646-3f01f3014931/go.mod h1:L70lmuizN79Y16+EBY/+N6QSWxqQ440zfa9aI5bePqs= 45 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 46 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 47 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 48 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 49 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 50 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 51 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 52 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 53 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 54 | github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 h1:J6v8awz+me+xeb/cUTotKgceAYouhIB3pjzgRd6IlGk= 55 | github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816/go.mod h1:tzym/CEb5jnFI+Q0k4Qq3+LvRF4gO3E2pxS8fHP8jcA= 56 | github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk= 57 | github.com/templexxx/cpu v0.0.7/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk= 58 | github.com/templexxx/cpu v0.0.8 h1:va6GebSxedVdR5XEyPJD49t94p5ZsjWO6Wh/PfbmZnc= 59 | github.com/templexxx/cpu v0.0.8/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk= 60 | github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORkVg= 61 | github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo= 62 | github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM= 63 | github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= 64 | github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ= 65 | github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI= 66 | github.com/xtaci/kcp-go/v5 v5.6.1/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo= 67 | github.com/xtaci/kcptun v0.0.0-20210922134509-94c9cacf4f87 h1:HCnpCHSLlNUlkBEO4dg1QWWlbUAbRz4OPZSyX89/7bQ= 68 | github.com/xtaci/kcptun v0.0.0-20210922134509-94c9cacf4f87/go.mod h1:FcPwiKFRtG+1EP3ODOafBQaqvnAPaDbOi9svA9HJkBw= 69 | github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM= 70 | github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= 71 | github.com/xtaci/smux v1.5.16 h1:FBPYOkW8ZTjLKUM4LI4xnnuuDC8CQ/dB04HD519WoEk= 72 | github.com/xtaci/smux v1.5.16/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY= 73 | github.com/xtaci/tcpraw v1.2.25/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk= 74 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 75 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 76 | golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= 77 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 78 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 79 | golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 80 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 81 | golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= 82 | golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 83 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 84 | golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= 85 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 86 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 87 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 88 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 89 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 90 | golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= 91 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 92 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 93 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 94 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 95 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 96 | golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 97 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 98 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 99 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 100 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 101 | golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9 h1:yi1hN8dcqI9l8klZfy4B8mJvFmmAxJEePIQQFNSd7Cs= 102 | golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 103 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 104 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 105 | golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 106 | golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123 h1:4JSJPND/+4555t1HfXYF4UEqDqiSKCgeV0+hbA8hMs4= 107 | golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 108 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 109 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 110 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 111 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 112 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 113 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 114 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 115 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 116 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 117 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 118 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 119 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 120 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 121 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 122 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 使用说明 2 | 3 | 已初步可以使用。 4 | 5 | 1.需要用到一个具有公网ip的机器S 作为 发现服务器 eg `111.231.198.54:30124` 6 | 7 | 2.client侧分两端,一个被控侧(rdp服务端内网运行)。一个控制侧(想要远控的电脑运行) 8 | 9 | 3.分别运行两侧的client端。 10 | 11 | ps: 使用上面的公共server发现端的话,只需要下载client的包就行了。 12 | ## 配置文件说明 13 | 14 | 1. 发现服务端 (运行名为server的包) 15 | 16 | ```yaml 17 | host: "" #目前没有使用,直接写死了0.0.0.0。后面再优化 18 | port: 30124 #暴露出去的端口 19 | ``` 20 | 21 | 2. 控制侧客户端 (运行名为client的包) 22 | 23 | ```yaml 24 | serverhost: 111.231.198.54 #发现服务端的ip地址 支持域名 25 | serverport: 30124 #发现服务端的暴露udp端口 26 | type: client_client_type #这个类型代表client侧 27 | clientportfrosvc: 30124 #一个用来和发现服务端 通信的udp端口 28 | clientportforp2ptrance: 30123 #用来p2p打洞的udp端口 29 | rdpp2pport: 30122 #转发rdp的端口 eg:用远程桌面客户端 请求 127.0.0.1:30122 30 | appname: rdp-p2p #p2p打洞分组。需要和server侧客户端一致。可以当做一个简单的密码 31 | remoterdpaddr: "" #client侧不用管 32 | ``` 33 | 34 | 3. 被控侧客户端 (运行名为client的包) 35 | 36 | ```yaml 37 | serverhost: 111.231.198.54 #发现服务端的ip地址 支持域名 38 | serverport: 30124 #发现服务端的暴露udp端口 39 | type: client_server_type #这个类型代表server侧 40 | clientportfrosvc: 30124 #一个用来和发现服务端 通信的udp端口 41 | clientportforp2ptrance: 30123 #用来p2p打洞的udp端口 42 | rdpp2pport: 30122 #server侧不用理会 43 | appname: rdp-p2p #p2p打洞分组。需要和client侧客户端一致。可以当做一个简单的密码 44 | remoterdpaddr: 192.168.50.80:3389 #需要控制的rdp服务端的地址 45 | ``` 46 | 47 | ## 编译说明 48 | 49 | 1. 发现服务端 50 | 51 | ```shell 52 | go build -o server ./server/cmd 53 | #windows下 54 | go build -o server.exe ./server/cmd 55 | ``` 56 | 57 | 2. client侧/server侧客户端 58 | 59 | ```shell 60 | go build -o client ./client/cmd 61 | #windows下 62 | go build -o client.exe ./client/cmd 63 | ``` 64 | 65 | ## 后面有空再做优化 -------------------------------------------------------------------------------- /server/app/cron.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | // 定时清理 peers中"掉线"的记录 4 | 5 | import ( 6 | "github.com/robfig/cron/v3" 7 | "github.com/scjtqs2/p2p_rdp/common" 8 | "os" 9 | "os/signal" 10 | "syscall" 11 | "time" 12 | ) 13 | 14 | func (l *UdpListener) startCron() { 15 | l.Cron = cron.New(cron.WithParser(cron.NewParser( 16 | cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor, 17 | ))) 18 | //定时执行任务 19 | l.Cron.AddFunc("* * * * *", l.clearPeers) // peers for rdp 20 | l.Cron.AddFunc("* * * * *", l.clearPeers2) // peers for svc 21 | l.Cron.Start() 22 | ch := make(chan os.Signal, 2) 23 | signal.Notify(ch, os.Interrupt, syscall.SIGTERM) 24 | } 25 | 26 | func (l *UdpListener) clearPeers() { 27 | //拉取keys 28 | keys := l.PeersKeys() 29 | go func() { 30 | for _, v := range keys { 31 | appName := v 32 | peers := l.PeersGet(appName) 33 | //校验server侧 并清理 34 | if peers != nil && !checkExpire(peers.Server) { 35 | peers.Server.Addr = "" 36 | l.PeersSet(appName, peers) 37 | } 38 | //校验client 并清理 39 | go l.clearClientPeers(appName) 40 | } 41 | }() 42 | } 43 | 44 | // 用来清理clients 45 | func (l *UdpListener) clearClientPeers(appName string) { 46 | peers := l.PeersGet(appName) 47 | if peers != nil && !checkExpire(peers.Client) { 48 | peers.Client.Addr = "" 49 | } 50 | l.PeersSet(appName, peers) 51 | } 52 | 53 | // 校验ip地址是否过期 54 | func checkExpire(ip common.Ip) bool { 55 | check := time.Now().UnixNano() - 5*time.Minute.Nanoseconds() 56 | if ip.Time.UnixNano() < check { 57 | return false 58 | } 59 | return true 60 | } 61 | 62 | 63 | 64 | 65 | func (l *UdpListener) clearPeers2() { 66 | //拉取keys 67 | keys := l.Peers2Keys() 68 | go func() { 69 | for _, v := range keys { 70 | appName := v 71 | peers := l.Peers2Get(appName) 72 | //校验server侧 并清理 73 | if peers != nil && !checkExpire(peers.Server) { 74 | peers.Server.Addr = "" 75 | l.Peers2Set(appName, peers) 76 | } 77 | //校验client 并清理 78 | go l.clearClientPeers2(appName) 79 | } 80 | }() 81 | } 82 | 83 | // 用来清理clients 84 | func (l *UdpListener) clearClientPeers2(appName string) { 85 | peers := l.Peers2Get(appName) 86 | if peers != nil && !checkExpire(peers.Client) { 87 | peers.Client.Addr = "" 88 | } 89 | l.Peers2Set(appName, peers) 90 | } 91 | -------------------------------------------------------------------------------- /server/app/svr.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/robfig/cron/v3" 7 | "github.com/scjtqs2/p2p_rdp/common" 8 | "github.com/scjtqs2/p2p_rdp/server/config" 9 | log "github.com/sirupsen/logrus" 10 | "net" 11 | "sync" 12 | "time" 13 | ) 14 | 15 | type UdpListener struct { 16 | Port int 17 | peers *Peers //专门用于rdp转发的p2p端口的地址 18 | peersForSvcTrance *Peers //专门用于和svc通信的p2p端口的地址 19 | Cron *cron.Cron 20 | //ch chan UdpSend 21 | } 22 | 23 | type Peers struct { 24 | Mu *sync.RWMutex 25 | peers map[string]*common.Peer 26 | } 27 | 28 | //Run 启动脚本 29 | func (l *UdpListener) Run(config *config.ServerConfig) (err error) { 30 | udpAddr, _ := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", config.Host, config.Port)) 31 | //conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: config.Port}) 32 | conn, err := net.ListenUDP("udp", udpAddr) 33 | l.Port = config.Port 34 | if err != nil { 35 | log.Errorf("监听udp失败 host=%s:%d ,err=%s", config.Host, config.Port, err.Error()) 36 | return err 37 | } 38 | //go l.sendBackend(conn) 39 | //l.ch = make(chan UdpSend, 128) 40 | log.Printf("本地地址: <%s:%d> ", config.Host, l.Port) 41 | l.peers = &Peers{ 42 | peers: make(map[string]*common.Peer), 43 | Mu: &sync.RWMutex{}, 44 | } 45 | l.peersForSvcTrance = &Peers{ 46 | peers: make(map[string]*common.Peer), 47 | Mu: &sync.RWMutex{}, 48 | } 49 | go func(conn *net.UDPConn) { 50 | defer conn.Close() 51 | for { 52 | data := make([]byte, common.PACKAGE_SIZE) 53 | n, remoteAddr, err := conn.ReadFromUDP(data) 54 | if err != nil { 55 | log.Errorf("read udp package from client faild,error %s", err.Error()) 56 | continue 57 | } 58 | //log.Printf("<%s> %s\n", remoteAddr, data[:n]) 59 | var udpMsg common.UDPMsg 60 | err = json.Unmarshal(data[:n], &udpMsg) 61 | if err != nil { 62 | log.Errorf("错误的udp包 remoteAdd=%s err=%s", remoteAddr, err.Error()) 63 | continue 64 | } 65 | switch udpMsg.Code { 66 | case common.UDP_TYPE_REPORT: 67 | l.progressReport(udpMsg, remoteAddr) 68 | case common.UDP_TYPE_GET_CLIENT_IP: 69 | var msg common.Msg 70 | json.Unmarshal(udpMsg.Data, &msg) 71 | switch msg.Type { 72 | case common.CLIENT_SERVER_TYPE: 73 | log.Infof("group %s server type of client req addr:%s", msg.AppName, remoteAddr) 74 | l.progressServerClient(remoteAddr, msg, conn) 75 | case common.CLIENT_CLIENT_TYPE: 76 | log.Infof("group %s client type of client req addr:%s", msg.AppName, remoteAddr) 77 | l.progressClientClient(remoteAddr, msg, conn) 78 | default: 79 | log.Errorf("error type of udp req") 80 | continue 81 | } 82 | } 83 | } 84 | }(conn) 85 | l.startCron() 86 | return nil 87 | } 88 | 89 | func (l *UdpListener) progressReport(req common.UDPMsg, add net.Addr) { 90 | var msg common.Msg 91 | json.Unmarshal(req.Data, &msg) 92 | log.Infof("report group %s type %s req addr:%s", msg.AppName, msg.Type, add.String()) 93 | l.checkipInListAndUpdateTime(add.String(), msg.AppName, msg.Type) 94 | } 95 | 96 | //progressClientClient 处理客户侧的客户端请求 97 | func (l *UdpListener) progressClientClient(add *net.UDPAddr, req common.Msg, conn *net.UDPConn) { 98 | check := l.checkipInListAndUpdateTimeFroSvc(add.String(), req.AppName, req.Type) 99 | peers := l.PeersGet(req.AppName) 100 | //查找server侧的客户端地址并返回 101 | if peers == nil || peers.Server.Addr == "" { 102 | msg, _ := json.Marshal(&common.Msg{ 103 | AppName: req.AppName, 104 | Type: common.MESSAGE_TYPE_FOR_CLIENT_CLIENT_WITH_SERVER_IPS, 105 | Res: common.Res{ 106 | Code: 404, //不存在,404 107 | Message: "服务侧不在线", 108 | }, 109 | }) 110 | udpMsg, _ := json.Marshal(&common.UDPMsg{ 111 | Code: common.UDP_TYPE_DISCOVERY, 112 | Data: msg, 113 | }) 114 | //回一个包,确认打通udp通道。 115 | conn.WriteToUDP(udpMsg, add) 116 | //go l.WriteToUdb(udpMsg, add) 117 | return 118 | } else { 119 | // server侧的ip存在 120 | serverIp, _ := json.Marshal(l.PeersGet(req.AppName).Server) 121 | msg, _ := json.Marshal(common.Msg{ 122 | AppName: req.AppName, 123 | Type: common.MESSAGE_TYPE_FOR_CLIENT_CLIENT_WITH_SERVER_IPS, 124 | Res: common.Res{ 125 | Code: 0, 126 | Message: string(serverIp), 127 | }, 128 | }) 129 | udpMsg, _ := json.Marshal(&common.UDPMsg{ 130 | Code: common.UDP_TYPE_DISCOVERY, 131 | Data: msg, 132 | }) 133 | //发给client侧 server的ip地址。 134 | conn.WriteToUDP(udpMsg, add) 135 | //go l.WriteToUdb(udpMsg, add) 136 | //同时给server侧发送client的ip 137 | peers2 := l.Peers2Get(req.AppName) 138 | dstAddr, _ := net.ResolveUDPAddr("udp", peers2.Server.Addr) 139 | clientIp, _ := json.Marshal(peers.Client) 140 | msg2server, _ := json.Marshal(&common.Msg{ 141 | AppName: req.AppName, 142 | Type: common.MESSAGE_TYPE_FOR_CLIENT_SERVER_WITH_CLIENT_IPS, 143 | Res: common.Res{ 144 | Code: 0, 145 | Message: string(clientIp), 146 | }, 147 | }) 148 | msg2serverudpMsg, _ := json.Marshal(&common.UDPMsg{ 149 | Code: common.UDP_TYPE_DISCOVERY, 150 | Data: msg2server, 151 | }) 152 | if !check { 153 | msg2serverudpMsg, _ = json.Marshal(&common.UDPMsg{ 154 | Code: common.UDP_TYPE_DISCOVERY_FROCE_P2P, 155 | Data: msg2server, 156 | }) 157 | } 158 | conn.WriteToUDP(msg2serverudpMsg, dstAddr) 159 | //go l.WriteToUdb(msg2serverudpMsg, dstAddr) 160 | return 161 | } 162 | } 163 | 164 | // 处理服务侧的客户端请求 165 | func (l *UdpListener) progressServerClient(add *net.UDPAddr, req common.Msg, conn *net.UDPConn) { 166 | check := l.checkipInListAndUpdateTimeFroSvc(add.String(), req.AppName, req.Type) 167 | //查找client侧的客户端是否有地址 168 | peers := l.PeersGet(req.AppName) 169 | if peers == nil || peers.Client.Addr == "" { 170 | msg, _ := json.Marshal(common.Msg{ 171 | AppName: req.AppName, 172 | Type: common.MESSAGE_TYPE_FOR_CLIENT_SERVER_WITH_CLIENT_IPS, 173 | Res: common.Res{ 174 | Code: 404, //不存在,404 175 | Message: "客户侧不在线", 176 | }, 177 | }) 178 | udpMsg, _ := json.Marshal(&common.UDPMsg{ 179 | Code: common.UDP_TYPE_DISCOVERY, 180 | Data: msg, 181 | }) 182 | //回一个包,确认打通udp通道。 183 | conn.WriteToUDP(udpMsg, add) 184 | //go l.WriteToUdb(udpMsg, add) 185 | return 186 | } else { 187 | //clients有ip存在 188 | //直接回包client侧ip的地址列表 189 | clientIp, _ := json.Marshal(peers.Client) 190 | msg2server, _ := json.Marshal(common.Msg{ 191 | AppName: req.AppName, 192 | Type: common.MESSAGE_TYPE_FOR_CLIENT_SERVER_WITH_CLIENT_IPS, 193 | Res: common.Res{ 194 | Code: 0, 195 | Message: string(clientIp), 196 | }, 197 | }) 198 | msg2serverudpMsg, _ := json.Marshal(&common.UDPMsg{ 199 | Code: common.UDP_TYPE_DISCOVERY, 200 | Data: msg2server, 201 | }) 202 | conn.WriteToUDP(msg2serverudpMsg, add) 203 | //go l.WriteToUdb(msg2serverudpMsg, add) 204 | //对client客户端回server的ip地址。 205 | serverIp, _ := json.Marshal(peers.Server) 206 | msg2client, _ := json.Marshal(common.Msg{ 207 | AppName: req.AppName, 208 | Type: common.MESSAGE_TYPE_FOR_CLIENT_CLIENT_WITH_SERVER_IPS, 209 | Res: common.Res{ 210 | Code: 0, 211 | Message: string(serverIp), 212 | }, 213 | }) 214 | msg2clientudpMsg, _ := json.Marshal(&common.UDPMsg{ 215 | Code: common.UDP_TYPE_DISCOVERY, 216 | Data: msg2client, 217 | }) 218 | if !check { 219 | msg2clientudpMsg, _ = json.Marshal(&common.UDPMsg{ 220 | Code: common.UDP_TYPE_DISCOVERY_FROCE_P2P, 221 | Data: msg2client, 222 | }) 223 | } 224 | cip := l.Peers2Get(req.AppName).Client 225 | dstAddr, _ := net.ResolveUDPAddr("udp", cip.Addr) 226 | conn.WriteToUDP(msg2clientudpMsg, dstAddr) 227 | //go l.WriteToUdb(msg2clientudpMsg, dstAddr) 228 | } 229 | } 230 | 231 | // 检查客户侧的客户端的Ip是否存在 232 | func (l *UdpListener) checkipInListAndUpdateTime(addr, appName, clientType string) bool { 233 | peers := l.PeersGet(appName) 234 | if peers == nil { 235 | switch clientType { 236 | case common.CLIENT_CLIENT_TYPE: 237 | l.PeersSet(appName, &common.Peer{Client: common.Ip{Addr: addr, Time: time.Now()}}) 238 | case common.CLIENT_SERVER_TYPE: 239 | l.PeersSet(appName, &common.Peer{Server: common.Ip{Addr: addr, Time: time.Now()}}) 240 | } 241 | return false 242 | } 243 | switch clientType { 244 | case common.CLIENT_CLIENT_TYPE: 245 | peers.Client.Time = time.Now() 246 | if peers.Client.Addr == addr { 247 | l.PeersSet(appName, peers) 248 | return true 249 | } 250 | peers.Client.Addr = addr 251 | case common.CLIENT_SERVER_TYPE: 252 | peers.Server.Time = time.Now() 253 | if peers.Server.Addr == addr { 254 | //更新时间点 255 | l.PeersSet(appName, peers) 256 | return true 257 | } 258 | peers.Server.Addr = addr 259 | } 260 | l.PeersSet(appName, peers) 261 | return false 262 | } 263 | 264 | // PeersGet 读取peers 265 | func (l *UdpListener) PeersGet(appName string) *common.Peer { 266 | l.peers.Mu.RLock() 267 | defer l.peers.Mu.RUnlock() 268 | return l.peers.peers[appName] 269 | } 270 | 271 | // PeersSet 设置peers 272 | func (l *UdpListener) PeersSet(appName string, peer *common.Peer) { 273 | l.peers.Mu.Lock() 274 | defer l.peers.Mu.Unlock() 275 | l.peers.peers[appName] = peer 276 | } 277 | 278 | func (l *UdpListener) PeersCount() int { 279 | l.peers.Mu.RLock() 280 | defer l.peers.Mu.RUnlock() 281 | return len(l.peers.peers) 282 | } 283 | 284 | func (l *UdpListener) PeersKeys() []string { 285 | l.peers.Mu.RLock() 286 | defer l.peers.Mu.RUnlock() 287 | keys := make([]string, len(l.peers.peers)) 288 | for k := range l.peers.peers { 289 | keys = append(keys, k) 290 | } 291 | return keys 292 | } 293 | 294 | // 检查客户侧的客户端的Ip是否存在 295 | func (l *UdpListener) checkipInListAndUpdateTimeFroSvc(addr, appName, clientType string) bool { 296 | peers := l.Peers2Get(appName) 297 | if peers == nil { 298 | switch clientType { 299 | case common.CLIENT_CLIENT_TYPE: 300 | l.Peers2Set(appName, &common.Peer{Client: common.Ip{Addr: addr, Time: time.Now()}, Server: common.Ip{Addr: ""}}) 301 | case common.CLIENT_SERVER_TYPE: 302 | l.Peers2Set(appName, &common.Peer{Server: common.Ip{Addr: addr, Time: time.Now()}, Client: common.Ip{Addr: ""}}) 303 | } 304 | return false 305 | } 306 | switch clientType { 307 | case common.CLIENT_CLIENT_TYPE: 308 | peers.Client.Time = time.Now() 309 | if peers.Client.Addr == addr { 310 | l.Peers2Set(appName, peers) 311 | return true 312 | } 313 | peers.Client.Addr = addr 314 | case common.CLIENT_SERVER_TYPE: 315 | peers.Server.Time = time.Now() 316 | if peers.Server.Addr == addr { 317 | //更新时间点 318 | l.Peers2Set(appName, peers) 319 | return true 320 | } 321 | peers.Server.Addr = addr 322 | } 323 | l.Peers2Set(appName, peers) 324 | return false 325 | } 326 | 327 | // PeersGet 读取peersForSvcTrance 328 | func (l *UdpListener) Peers2Get(appName string) *common.Peer { 329 | l.peersForSvcTrance.Mu.RLock() 330 | defer l.peersForSvcTrance.Mu.RUnlock() 331 | return l.peersForSvcTrance.peers[appName] 332 | } 333 | 334 | // PeersSet 设置peersForSvcTrance 335 | func (l *UdpListener) Peers2Set(appName string, peer *common.Peer) { 336 | l.peersForSvcTrance.Mu.Lock() 337 | defer l.peersForSvcTrance.Mu.Unlock() 338 | l.peersForSvcTrance.peers[appName] = peer 339 | } 340 | 341 | func (l *UdpListener) Peers2Count() int { 342 | l.peersForSvcTrance.Mu.RLock() 343 | defer l.peersForSvcTrance.Mu.RUnlock() 344 | return len(l.peersForSvcTrance.peers) 345 | } 346 | 347 | func (l *UdpListener) Peers2Keys() []string { 348 | l.peersForSvcTrance.Mu.RLock() 349 | defer l.peersForSvcTrance.Mu.RUnlock() 350 | keys := make([]string, len(l.peersForSvcTrance.peers)) 351 | for k := range l.peersForSvcTrance.peers { 352 | keys = append(keys, k) 353 | } 354 | return keys 355 | } 356 | 357 | // 358 | //// 异步发包 的结构体 359 | //type UdpSend struct { 360 | // Addr *net.UDPAddr 361 | // Data []byte 362 | //} 363 | // 364 | //// 将要发送的udp包写入消息队列 365 | //func (l *UdpListener) WriteToUdb(data []byte, addr *net.UDPAddr) { 366 | // udpSend := UdpSend{ 367 | // Data: data, 368 | // Addr: addr, 369 | // } 370 | // l.ch <- udpSend 371 | //} 372 | // 373 | //// 异步消费发包,提高性能 374 | //func (l *UdpListener) sendBackend(conn *net.UDPConn) { 375 | // for { 376 | // udpSend := <-l.ch 377 | // log.Printf("udp回包 toAddr=%s,data=%s", udpSend.Addr.String(), string(udpSend.Data)) 378 | // go func(conn *net.UDPConn, send UdpSend) { 379 | // _, err := conn.WriteToUDP(send.Data, send.Addr) 380 | // if err != nil { 381 | // log.Errorf("回包失败 err:=%s", err.Error()) 382 | // } 383 | // }(conn, udpSend) 384 | // } 385 | //} 386 | -------------------------------------------------------------------------------- /server/app/util.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "crypto/sha1" 5 | "fmt" 6 | "github.com/scjtqs2/kcp-go/v5" 7 | "github.com/scjtqs2/p2p_rdp/server/config" 8 | "golang.org/x/crypto/pbkdf2" 9 | "math/rand" 10 | "net" 11 | "strconv" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | // 生成seq 17 | func makeSeq() string { 18 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 19 | r.Seed(time.Now().UnixNano()) 20 | return strconv.Itoa(r.Intn(1000000)) 21 | } 22 | 23 | func NewUdpServer(config *config.ServerConfig) (*net.UDPConn, error) { 24 | return net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: config.Port}) 25 | } 26 | 27 | func NewKcpServer(config *config.ServerConfig) (*kcp.Listener, error) { 28 | key := pbkdf2.Key([]byte("demo pass"), []byte("demo salt"), 1024, 32, sha1.New) 29 | block, _ := kcp.NewAESBlockCrypt(key) 30 | return kcp.ListenWithOptions(fmt.Sprintf("0.0.0.0:%d", config.Port), block, 10, 3) 31 | } 32 | 33 | // serves KCP protocol for a single packet connection. 34 | func NewKcpServerWithUDPConn(conn net.PacketConn) (*kcp.Listener, error) { 35 | key := pbkdf2.Key([]byte("demo pass"), []byte("demo salt"), 1024, 32, sha1.New) 36 | block, _ := kcp.NewAESBlockCrypt(key) 37 | return kcp.ServeConn(block, 10, 3, conn) 38 | } 39 | 40 | func NewKcpClient(addr string, localPort int) (*kcp.UDPSession, error) { 41 | key := pbkdf2.Key([]byte("demo pass"), []byte("demo salt"), 1024, 32, sha1.New) 42 | block, _ := kcp.NewAESBlockCrypt(key) 43 | localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: localPort} 44 | return kcp.DialWithOptions(addr, block, 10, 3, localAddr) 45 | } 46 | 47 | func NewKcpConnWithUDPConn(conn net.PacketConn, raddr string) (*kcp.UDPSession, error) { 48 | key := pbkdf2.Key([]byte("demo pass"), []byte("demo salt"), 1024, 32, sha1.New) 49 | block, _ := kcp.NewAESBlockCrypt(key) 50 | return kcp.NewConn(raddr, block, 10, 3, conn) 51 | } 52 | 53 | func parseAddr(addr string) *net.UDPAddr { 54 | t := strings.Split(addr, ":") 55 | port, _ := strconv.Atoi(t[1]) 56 | return &net.UDPAddr{ 57 | IP: net.ParseIP(t[0]), 58 | Port: port, 59 | } 60 | } -------------------------------------------------------------------------------- /server/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | rotatelogs "github.com/lestrrat-go/file-rotatelogs" 6 | "github.com/scjtqs2/p2p_rdp/server/app" 7 | "github.com/scjtqs2/p2p_rdp/server/config" 8 | "github.com/scjtqs2/utils/util" 9 | log "github.com/sirupsen/logrus" 10 | easy "github.com/t-tomalak/logrus-easy-formatter" 11 | "os" 12 | "os/signal" 13 | "path" 14 | "time" 15 | ) 16 | 17 | var ( 18 | h bool 19 | d bool 20 | Version = "v1.0.0" 21 | Build string 22 | configPath = "config.yml" 23 | ) 24 | 25 | func init() { 26 | var debug bool 27 | flag.BoolVar(&d, "d", false, "running as a daemon") 28 | flag.BoolVar(&h, "h", false, "this help") 29 | flag.StringVar(&configPath, "c", "config.yml", "config file path default is config.yml") 30 | flag.Parse() 31 | logFormatter := &easy.Formatter{ 32 | TimestampFormat: "2006-01-02 15:04:05", 33 | LogFormat: "[%time%] [%lvl%]: %msg% \n", 34 | } 35 | w, err := rotatelogs.New(path.Join("logs", "%Y-%m-%d.log"), rotatelogs.WithRotationTime(time.Hour*24)) 36 | if err != nil { 37 | log.Errorf("rotatelogs init err: %v", err) 38 | panic(err) 39 | } 40 | LogLevel := "info" 41 | if debug { 42 | log.SetReportCaller(true) 43 | LogLevel = "debug" 44 | } 45 | log.AddHook(util.NewLocalHook(w, logFormatter, util.GetLogLevel(LogLevel)...)) 46 | } 47 | 48 | func main() { 49 | if h { 50 | help() 51 | } 52 | if d { 53 | util.Daemon() 54 | } 55 | conf := config.GetConfigFronPath(configPath) 56 | conf.Save(configPath) 57 | log.Infof("welcome to use p2p_rdp server by scjtqs https://github.com/scjtqs2/p2p_rdp/server %s,build in %s", Version, Build) 58 | udplistener := &app.UdpListener{} 59 | udplistener.Run(conf) 60 | //defer udplistener.Conn.Close() 61 | defer udplistener.Cron.Stop() 62 | c := make(chan os.Signal, 1) 63 | signal.Notify(c, os.Interrupt, os.Kill) 64 | log.Infof("初始化完成") 65 | <-c 66 | } 67 | 68 | // help cli命令行-h的帮助提示 69 | func help() { 70 | log.Infof(`p2p_rdp service 71 | version: %s 72 | built-on: %s 73 | 74 | Usage: 75 | 76 | server [OPTIONS] 77 | 78 | Options: 79 | `, Version, Build) 80 | flag.PrintDefaults() 81 | os.Exit(0) 82 | } 83 | -------------------------------------------------------------------------------- /server/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/go-yaml/yaml" 5 | "github.com/scjtqs2/utils/util" 6 | ) 7 | 8 | type ServerConfig struct { 9 | Host string 10 | Port int 11 | } 12 | 13 | // 通过路径获取配置信息 14 | func GetConfigFronPath(c string) *ServerConfig { 15 | conf := &ServerConfig{} 16 | if !util.PathExists(c) { 17 | conf = defaultConf() 18 | } else { 19 | err := yaml.Unmarshal([]byte(util.ReadAllText(c)), conf) 20 | if err != nil { 21 | conf = defaultConf() 22 | } 23 | } 24 | return parseConfFromEnv(conf) 25 | } 26 | 27 | func defaultConf() *ServerConfig { 28 | return &ServerConfig{ 29 | Host: "0.0.0.0", 30 | Port: 30124, 31 | } 32 | } 33 | 34 | // 从环境变量中替换配置文件 35 | func parseConfFromEnv(c *ServerConfig) *ServerConfig { 36 | //todu 37 | return c 38 | } 39 | 40 | // 保存配置文件 41 | func (c *ServerConfig) Save(p string) error { 42 | s, _ := yaml.Marshal(c) 43 | return util.WriteAllText(p, string(s)) 44 | } 45 | -------------------------------------------------------------------------------- /server/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= 5 | github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= 6 | github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= 7 | github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= 8 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 9 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 10 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 11 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 12 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 13 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 14 | github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= 15 | github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= 16 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= 17 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= 18 | github.com/lestrrat-go/strftime v1.0.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa2xeOE= 19 | github.com/lestrrat-go/strftime v1.0.5/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= 20 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 21 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 22 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 23 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 24 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 25 | github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= 26 | github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= 27 | github.com/scjtqs2/p2p_rdp v0.0.0-20211129060045-99d590c791ea h1:UZKNy19EKcCjLuwzsjvfFgS06lx18sYWJwfae2R2WC4= 28 | github.com/scjtqs2/p2p_rdp v0.0.0-20211129060045-99d590c791ea/go.mod h1:/zU4PRWUtre9toZF/uem+as+cSA/BqwWQE0Tdpy8pyo= 29 | github.com/scjtqs2/utils v0.0.0-20211110033646-3f01f3014931 h1:y81SHMqf2P8K2zX9p8LI12SPe0wu7aUkoCZ9YwHgLcw= 30 | github.com/scjtqs2/utils v0.0.0-20211110033646-3f01f3014931/go.mod h1:L70lmuizN79Y16+EBY/+N6QSWxqQ440zfa9aI5bePqs= 31 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 32 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 33 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 34 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 35 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 36 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 37 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 38 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 39 | github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 h1:J6v8awz+me+xeb/cUTotKgceAYouhIB3pjzgRd6IlGk= 40 | github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816/go.mod h1:tzym/CEb5jnFI+Q0k4Qq3+LvRF4gO3E2pxS8fHP8jcA= 41 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 42 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= 43 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 44 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 45 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 46 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 47 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 48 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 49 | --------------------------------------------------------------------------------