github.com/erda-project/erda-infra@v1.0.9/providers/remote-forward/client/provider.go (about) 1 // Copyright (c) 2021 Terminus, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package client 16 17 import ( 18 "encoding/hex" 19 "errors" 20 "fmt" 21 "net" 22 "os" 23 "reflect" 24 "time" 25 26 "github.com/erda-project/erda-infra/base/logs" 27 "github.com/erda-project/erda-infra/base/servicehub" 28 forward "github.com/erda-project/erda-infra/providers/remote-forward" 29 yamux "github.com/hashicorp/yamux" 30 uuid "github.com/satori/go.uuid" 31 ) 32 33 // Interface . 34 type Interface interface { 35 RemoteShadowAddr() string 36 Values() map[string]interface{} 37 } 38 39 var _ (Interface) = (*provider)(nil) 40 41 type config struct { 42 RemoteAddr string `file:"remote_addr"` 43 RemoteShadowAddr string `file:"remote_shadow_addr"` 44 TargetAddr string `file:"target_addr"` 45 Name string `file:"name"` 46 Token string `file:"token"` 47 } 48 49 type provider struct { 50 Cfg *config 51 Log logs.Logger 52 conn net.Conn 53 response *forward.ResponseHeader 54 } 55 56 func (p *provider) RemoteShadowAddr() string { return p.response.ShadowAddr } 57 func (p *provider) Values() map[string]interface{} { return p.response.Values } 58 59 func (p *provider) Init(ctx servicehub.Context) error { 60 if len(p.Cfg.Name) <= 0 { 61 hostname, err := os.Hostname() 62 if err == nil { 63 p.Cfg.Name = fmt.Sprintf("%s@%s->%s", hex.EncodeToString(uuid.NewV4().Bytes()[8:16]), hostname, p.Cfg.TargetAddr) 64 } else { 65 p.Cfg.Name = hex.EncodeToString(uuid.NewV4().Bytes()) 66 } 67 p.Log.Infof("forward name is %q", p.Cfg.Name) 68 } 69 conn, err := net.Dial("tcp", p.Cfg.RemoteAddr) 70 if err != nil { 71 return fmt.Errorf("failed to connect remote forward server: %s", err) 72 } 73 p.conn = conn 74 p.response, err = p.handshake(conn) 75 if err != nil { 76 return err 77 } 78 return nil 79 } 80 81 func (p *provider) Start() error { 82 conn := p.conn 83 session, err := yamux.Server(conn, nil) 84 if err != nil { 85 if errors.Is(err, net.ErrClosed) { 86 return nil 87 } 88 return err 89 } 90 defer session.Close() 91 defer conn.Close() 92 for { 93 conn, err := session.Accept() 94 if err != nil { 95 if errors.Is(err, net.ErrClosed) { 96 return nil 97 } 98 return err 99 } 100 go p.handleConn(conn) 101 } 102 } 103 104 func (p *provider) Close() error { 105 if p.conn != nil { 106 conn := p.conn 107 p.conn = nil 108 err := conn.Close() 109 if !errors.Is(err, net.ErrClosed) { 110 return err 111 } 112 } 113 return nil 114 } 115 116 func (p *provider) handshake(conn net.Conn) (resp *forward.ResponseHeader, err error) { 117 defer func() { 118 if err != nil { 119 err = fmt.Errorf("handshake error: %w", err) 120 } 121 }() 122 err = conn.SetDeadline(time.Now().Add(60 * time.Second)) 123 if err != nil { 124 return nil, err 125 } 126 err = forward.EncodeRequestHeader(conn, &forward.RequestHeader{ 127 Version: forward.ProtocolVersion, 128 Name: p.Cfg.Name, 129 Token: p.Cfg.Token, 130 ShadowAddr: p.Cfg.RemoteShadowAddr, 131 }) 132 if err != nil { 133 return nil, err 134 } 135 resp, err = forward.DecodeResponseHeader(conn) 136 if err != nil { 137 return nil, err 138 } 139 if len(resp.Error) > 0 { 140 return nil, errors.New(resp.Error) 141 } 142 err = conn.SetDeadline(time.Time{}) 143 if err != nil { 144 return nil, err 145 } 146 p.Log.Infof("remote shadow addr: %s", resp.ShadowAddr) 147 return resp, nil 148 } 149 150 func (p *provider) handleConn(conn net.Conn) { 151 defer conn.Close() 152 localConn, err := net.Dial("tcp", p.Cfg.TargetAddr) 153 if err != nil { 154 p.Log.Error(err) 155 return 156 } 157 defer localConn.Close() 158 p.Log.Debugf("forward %s -> %s", conn.RemoteAddr(), p.Cfg.TargetAddr) 159 forward.Pipe(p.Log, conn, localConn) 160 } 161 162 func init() { 163 servicehub.Register("remote-forward-client", &servicehub.Spec{ 164 Services: []string{"remote-forward-client"}, 165 Types: []reflect.Type{reflect.TypeOf((*Interface)(nil)).Elem()}, 166 ConfigFunc: func() interface{} { return &config{} }, 167 Creator: func() servicehub.Provider { 168 return &provider{} 169 }, 170 }) 171 }