github.com/geph-official/geph2@v0.22.6-0.20210211030601-f527cb59b0df/cmd/geph-client/getmux.go (about)

     1  package main
     2  
     3  import (
     4  	"crypto/ed25519"
     5  	"encoding/hex"
     6  	"errors"
     7  	"fmt"
     8  	"math/rand"
     9  	"net"
    10  	"os"
    11  	"strings"
    12  	"sync"
    13  	"time"
    14  
    15  	log "github.com/sirupsen/logrus"
    16  
    17  	"github.com/ethereum/go-ethereum/rlp"
    18  	"github.com/geph-official/geph2/libs/bdclient"
    19  	"github.com/geph-official/geph2/libs/cshirt2"
    20  	"github.com/geph-official/geph2/libs/tinyss"
    21  )
    22  
    23  func negotiateTinySS(greeting *[2][]byte, rawConn net.Conn, pk []byte, nextProto byte) (cryptConn *tinyss.Socket, err error) {
    24  	rawConn.SetDeadline(time.Now().Add(time.Second * 20))
    25  	cryptConn, err = tinyss.Handshake(rawConn, nextProto)
    26  	if err != nil {
    27  		err = fmt.Errorf("tinyss handshake failed: %w", err)
    28  		rawConn.Close()
    29  		return
    30  	}
    31  	// verify the actual msg
    32  	var sssig []byte
    33  	err = rlp.Decode(cryptConn, &sssig)
    34  	if err != nil {
    35  		err = fmt.Errorf("cannot decode sssig: %w", err)
    36  		rawConn.Close()
    37  		return
    38  	}
    39  	if !ed25519.Verify(pk, cryptConn.SharedSec(), sssig) {
    40  		err = errors.New("man in the middle")
    41  		rawConn.Close()
    42  		return
    43  	}
    44  	if greeting != nil {
    45  		// send the greeting
    46  		rlp.Encode(cryptConn, greeting)
    47  		// wait for the reply
    48  		var reply string
    49  		err = rlp.Decode(cryptConn, &reply)
    50  		if err != nil {
    51  			err = fmt.Errorf("cannot decode reply: %w", err)
    52  			rawConn.Close()
    53  			return
    54  		}
    55  		if reply != "OK" {
    56  			err = errors.New("authentication failed")
    57  			rawConn.Close()
    58  			log.Println("authentication failed", reply)
    59  			os.Exit(11)
    60  		}
    61  	}
    62  	return
    63  }
    64  
    65  func dialBridge(host string, cookie []byte) (net.Conn, error) {
    66  	var port uint64
    67  	portrng := cshirt2.NewRNG(cookie)
    68  	for i := 0; i < rand.Int()%16+1; i++ {
    69  		port = portrng() % 65536
    70  	}
    71  	recombinedHost := fmt.Sprintf("%v:%v", strings.Split(host, ":")[0], port)
    72  	conn, err := net.DialTimeout("tcp", recombinedHost, time.Second*15)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	conn.(*net.TCPConn).SetKeepAlive(false)
    77  	return cshirt2.Client(cookie, conn)
    78  }
    79  
    80  var greetingCache struct {
    81  	ubmsg   []byte
    82  	ubsig   []byte
    83  	expires time.Time
    84  	lock    sync.Mutex
    85  }
    86  
    87  func getGreeting() (ubmsg, ubsig []byte, err error) {
    88  	greetingCache.lock.Lock()
    89  	defer greetingCache.lock.Unlock()
    90  	if time.Now().Before(greetingCache.expires) {
    91  		ubmsg, ubsig = greetingCache.ubmsg, greetingCache.ubsig
    92  		return
    93  	}
    94  	// obtain a ticket
    95  	var ticket bdclient.TicketResp
    96  	err = binders.Do(func(b *bdclient.Client) error {
    97  		var err error
    98  		ubmsg, ubsig, ticket, err = b.GetTicket(username, password)
    99  		return err
   100  	})
   101  	if err != nil {
   102  		log.Errorln("error authenticating:", err)
   103  		if errors.Is(err, bdclient.ErrBadAuth) && loginCheck {
   104  			os.Exit(11)
   105  		}
   106  		return
   107  	}
   108  	if loginCheck {
   109  		os.Exit(0)
   110  	}
   111  	useStats(func(sc *stats) {
   112  		sc.Username = username
   113  		sc.Expiry = ticket.PaidExpiry
   114  		sc.Tier = ticket.Tier
   115  		sc.PayTxes = ticket.Transactions
   116  	})
   117  	greetingCache.ubmsg = ubmsg
   118  	greetingCache.ubsig = ubsig
   119  	greetingCache.expires = time.Now().Add(time.Second * 30)
   120  	return
   121  }
   122  
   123  var bridgesCache struct {
   124  	bridges []bdclient.BridgeInfo
   125  	expires time.Time
   126  	lock    sync.Mutex
   127  }
   128  
   129  func getBridges(ubmsg, ubsig []byte) ([]bdclient.BridgeInfo, error) {
   130  	bridgesCache.lock.Lock()
   131  	defer bridgesCache.lock.Unlock()
   132  	if time.Now().Before(bridgesCache.expires) {
   133  		return bridgesCache.bridges, nil
   134  	}
   135  	var bridges []bdclient.BridgeInfo
   136  	e := binders.Do(func(b *bdclient.Client) error {
   137  		var err error
   138  		bridges, err = b.GetBridges(ubmsg, ubsig)
   139  		return err
   140  	})
   141  	if e != nil {
   142  		return nil, e
   143  	}
   144  	if additionalBridges != "" {
   145  		relays := strings.Split(additionalBridges, ";")
   146  		for _, str := range relays {
   147  			splitted := strings.Split(str, "@")
   148  			if len(splitted) != 2 {
   149  				panic("-additionalBridges must be cookie1@host1:port1;cookie2@host2:port2 etc")
   150  			}
   151  			cookie, err := hex.DecodeString(splitted[0])
   152  			if err != nil {
   153  				panic(err)
   154  			}
   155  			bridges = append(bridges, bdclient.BridgeInfo{Cookie: cookie, Host: splitted[1]})
   156  		}
   157  	}
   158  	log.Infoln("Obtained", len(bridges), "bridges")
   159  	for _, b := range bridges {
   160  		log.Infof(".... %v %x", b.Host, b.Cookie)
   161  	}
   162  	bridgesCache.bridges, bridgesCache.expires = bridges, time.Now().Add(time.Minute)
   163  	return bridges, nil
   164  }