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  }