github.com/geph-official/geph2@v0.22.6-0.20210211030601-f527cb59b0df/cmd/geph-client/multipool.go (about) 1 package main 2 3 import ( 4 "crypto/rand" 5 "encoding/hex" 6 "net" 7 "net/http" 8 "strings" 9 "time" 10 11 "github.com/ethereum/go-ethereum/rlp" 12 "github.com/geph-official/geph2/libs/backedtcp" 13 "github.com/geph-official/geph2/libs/bdclient" 14 "github.com/geph-official/geph2/libs/cshirt2" 15 "github.com/geph-official/geph2/libs/tinysocks" 16 log "github.com/sirupsen/logrus" 17 "github.com/xtaci/smux" 18 ) 19 20 type mpMember struct { 21 session *smux.Session 22 btcp *backedtcp.Socket 23 score float64 24 } 25 26 type multipool struct { 27 pool chan *smux.Session 28 metasess [32]byte 29 } 30 31 func newMultipool() *multipool { 32 tr := &multipool{} 33 tr.pool = make(chan *smux.Session, 256) 34 rand.Read(tr.metasess[:]) 35 go func() { 36 for i := 0; i < 8; i++ { 37 tr.fillOne() 38 } 39 }() 40 return tr 41 } 42 43 func (mp *multipool) fillOne() { 44 getConn := func() net.Conn { 45 retry: 46 conn, err := getCleanConn() 47 if err != nil { 48 log.Println("failed getCleanConn():", err) 49 time.Sleep(time.Second) 50 goto retry 51 } 52 return conn 53 } 54 btcp := getConn() 55 btcp.Write(mp.metasess[:]) 56 sm, err := smux.Client(btcp, &smux.Config{ 57 Version: 2, 58 KeepAliveInterval: time.Minute * 10, 59 KeepAliveTimeout: time.Minute * 40, 60 MaxFrameSize: 32768, 61 MaxReceiveBuffer: 1000 * 1024, 62 MaxStreamBuffer: 1000 * 1024, 63 }) 64 if err != nil { 65 panic(err) 66 } 67 mp.pool <- sm 68 } 69 70 func (mp *multipool) DialCmd(cmds ...string) (conn net.Conn, remAddr string, ok bool) { 71 const RESET = 1500 72 timeout := time.Millisecond * RESET 73 for { 74 sm := <-mp.pool 75 stream, err := sm.OpenStream() 76 if err != nil { 77 sm.Close() 78 log.Println("error while opening stream, throwing away:", err.Error()) 79 go mp.fillOne() 80 continue 81 } 82 mp.pool <- sm 83 rlp.Encode(stream, cmds) 84 var connected bool 85 // we try to connect to the other end within 500 milliseconds 86 // if we time out, we move on. 87 // but if we encounter any other error, we close the connection and spawn a new one. 88 stream.SetDeadline(time.Now().Add(timeout)) 89 err = rlp.Decode(stream, &connected) 90 if err != nil { 91 if strings.Contains(err.Error(), "timeout") && timeout < time.Second*5 { 92 log.Debugln("timeout after", timeout, "so let's try again") 93 timeout = timeout * 2 94 continue 95 } 96 log.Println("error while waiting for stream, throwing away:", err.Error()) 97 sm.Close() 98 timeout = time.Millisecond * RESET 99 continue 100 } 101 stream.SetDeadline(time.Time{}) 102 return stream, sm.RemoteAddr().String(), true 103 } 104 } 105 106 var cleanHTTPClient = &http.Client{ 107 Transport: &http.Transport{ 108 Proxy: nil, 109 IdleConnTimeout: time.Second * 120, 110 }, 111 Timeout: time.Second * 120, 112 } 113 114 // get a clean, authenticated channel all the way to the exit 115 func getCleanConn() (conn net.Conn, err error) { 116 var rawConn net.Conn 117 if singleHop != "" { 118 splitted := strings.Split(singleHop, "@") 119 if len(splitted) != 2 { 120 panic("-singleHop must be pk@host") 121 } 122 var tcpConn net.Conn 123 var e error 124 if upstreamProxy != "" { 125 tcpConn, e = net.DialTimeout("tcp", upstreamProxy, time.Second*5) 126 if e != nil { 127 log.Warnln("failed to connect to SOCKS5 font proxy server:", e) 128 err = e 129 return 130 } 131 e, _ = tinysocks.Client(tcpConn, tinysocks.ParseAddr(splitted[1]), tinysocks.CmdConnect) 132 if e != nil { 133 tcpConn.Close() 134 log.Warnln("failed handshake with second SOCKS5 server:", e) 135 err = e 136 return 137 } 138 } else { 139 tcpConn, e = net.DialTimeout("tcp", splitted[1], time.Second*5) 140 if e != nil { 141 log.Warn("cannot connect to singleHop server:", e) 142 err = e 143 return 144 } 145 } 146 tcpConn.SetDeadline(time.Now().Add(time.Second * 10)) 147 pk, e := hex.DecodeString(splitted[0]) 148 if e != nil { 149 panic(e) 150 } 151 obfsConn, e := cshirt2.Client(pk, tcpConn) 152 if e != nil { 153 log.Warn("cannot negotiate cshirt2 with singleHop server:", e) 154 err = e 155 return 156 } 157 cryptConn, e := negotiateTinySS(nil, obfsConn, pk, 'N') 158 if e != nil { 159 log.Warn("cannot negotiate tinyss with singleHop server:", e) 160 err = e 161 return 162 } 163 conn = cryptConn 164 return 165 } 166 ubsig, ubmsg, err := getGreeting() 167 if err != nil { 168 return 169 } 170 171 if direct { 172 if upstreamProxy != "" { 173 rawConn, err = net.DialTimeout("tcp", upstreamProxy, time.Second*5) 174 if err != nil { 175 log.Warnln("failed to connect to singlehop server:", err) 176 return 177 } 178 err, _ = tinysocks.Client(rawConn, tinysocks.ParseAddr(exitName+":2389"), tinysocks.CmdConnect) 179 if err != nil { 180 rawConn.Close() 181 log.Warnln("failed handshake with second SOCKS5 server:", err) 182 return 183 } 184 } else { 185 rawConn, err = net.DialTimeout("tcp", exitName+":2389", time.Second*5) 186 if err != nil { 187 log.Warnln("failed to connect to exit server: %v", err) 188 return 189 } 190 } 191 if err == nil { 192 rawConn.(*net.TCPConn).SetKeepAlive(false) 193 } 194 } else { 195 getWarpfrontCon := func() (warpConn net.Conn, err error) { 196 var wfstuff map[string]string 197 binders.Do(func(client *bdclient.Client) error { 198 wfstuff, err = client.GetWarpfronts() 199 return err 200 }) 201 if err != nil { 202 log.Warnln("can't get warp front:", err) 203 return 204 } 205 warpConn, err = getWarpfront(wfstuff) 206 return 207 } 208 if forceWarpfront { 209 rawConn, err = getWarpfrontCon() 210 if err != nil { 211 return 212 } 213 } else { 214 bridges, e := getBridges(ubmsg, ubsig) 215 if e != nil { 216 err = e 217 log.Warnln("getting bridges failed, retrying", err) 218 return 219 } 220 rawConn, err = getSingleTCP(bridges) 221 if err != nil { 222 log.Warnf("can't connect to bridges (%v); time to W A R P F R O N T", err) 223 rawConn, err = getWarpfrontCon() 224 if err != nil { 225 return 226 } 227 } 228 } 229 } 230 rawConn.SetDeadline(time.Now().Add(time.Second * 10)) 231 cryptConn, err := negotiateTinySS(&[2][]byte{ubsig, ubmsg}, rawConn, exitPK(), 'N') 232 if err != nil { 233 log.Println("error while negotiating cryptConn", err) 234 return 235 } 236 rawConn.SetDeadline(time.Time{}) 237 conn = cryptConn 238 log.Debugln("new conn to", conn.RemoteAddr()) 239 return 240 } 241 242 func exitPK() []byte { 243 realExitKey, err := hex.DecodeString(exitKey) 244 if err != nil { 245 panic(err) 246 } 247 return realExitKey 248 }