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

     1  package main
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"encoding/binary"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"fmt"
     9  	"log"
    10  	"math/rand"
    11  	"net"
    12  	"net/http"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/ethereum/go-ethereum/rlp"
    17  	"github.com/geph-official/geph2/libs/cshirt2"
    18  	"github.com/patrickmn/go-cache"
    19  )
    20  
    21  // cache of all bridge info. string => bridgeInfo
    22  var bridgeCache = cache.New(time.Minute*2, time.Minute)
    23  
    24  type bridgeInfo struct {
    25  	Cookie     []byte
    26  	Host       string
    27  	LastSeen   time.Time
    28  	AllocGroup string
    29  }
    30  
    31  func addBridge(nfo bridgeInfo) {
    32  	bridgeCache.SetDefault(string(nfo.Cookie), nfo)
    33  }
    34  
    35  // cache of bridge *mappings*. string => []string
    36  var bridgeMapCache = cache.New(time.Hour, time.Hour)
    37  
    38  func getBridges(id string) []string {
    39  	if mapping, ok := bridgeMapCache.Get(id); ok {
    40  		return mapping.([]string)
    41  	}
    42  	itms := bridgeCache.Items()
    43  	seed := fmt.Sprintf("%v-%v", id, time.Now())
    44  	probability := 10.0 / float64(len(itms))
    45  	candidates := make([][]string, 10)
    46  	candidateWeights := make([]int, 10)
    47  	// try 10 times
    48  	for i := 0; i < 10; i++ {
    49  		for k := range itms {
    50  			h := sha256.Sum256([]byte(k + seed))
    51  			num := binary.BigEndian.Uint32(h[:4])
    52  			if float64(num) < probability*float64(4294967295) || true {
    53  				candidates[i] = append(candidates[i], k)
    54  			}
    55  			//TODO diversity
    56  			candidateWeights[i] = len(candidates[i])
    57  		}
    58  	}
    59  	var toret []string
    60  	trweight := -1
    61  	for i := 0; i < 10; i++ {
    62  		if candidateWeights[i] > trweight {
    63  			trweight = candidateWeights[i]
    64  			toret = candidates[i]
    65  		}
    66  	}
    67  	// shuffle
    68  	rand.Shuffle(len(toret), func(i, j int) {
    69  		toret[i], toret[j] = toret[j], toret[i]
    70  	})
    71  	bridgeMapCache.SetDefault(id, toret)
    72  	return toret
    73  }
    74  
    75  func handleGetWarpfronts(w http.ResponseWriter, r *http.Request) {
    76  	countUserAgent(r)
    77  	host2front, err := getWarpfronts()
    78  	if err != nil {
    79  		w.WriteHeader(http.StatusInternalServerError)
    80  		return
    81  	}
    82  	w.Header().Set("content-type", "application/json")
    83  	json.NewEncoder(w).Encode(host2front)
    84  }
    85  
    86  var counterCache = cache.New(time.Minute, time.Hour)
    87  
    88  func handleGetBridges(w http.ResponseWriter, r *http.Request) {
    89  	countUserAgent(r)
    90  	id := strings.Split(r.Header.Get("X-Forwarded-For"), ",")[0]
    91  	// result, _ := goodIPCache.Get(id)
    92  	// if result == nil {
    93  	// 	log.Println("BAD IP:", id)
    94  	// 	w.WriteHeader(http.StatusForbidden)
    95  	// 	return
    96  	// }
    97  	// id = fmt.Sprintf("%v", result)
    98  	// fmt.Println("good IP with id", id)
    99  
   100  	isEphemeral := r.FormValue("type") == "ephemeral"
   101  	if isEphemeral {
   102  		w.WriteHeader(http.StatusBadRequest)
   103  		return
   104  	}
   105  	// TODO validate the ticket
   106  	bridges := getBridges(id)
   107  	w.Header().Set("content-type", "application/json")
   108  	idhash := sha256.Sum256([]byte(id))
   109  	w.Header().Set("X-Requestor-ID", hex.EncodeToString(idhash[:]))
   110  	seenAGs := make(map[string]bool)
   111  	var laboo []bridgeInfo
   112  	vacate := func() {
   113  		bridgeMapCache.Delete(id)
   114  	}
   115  	for _, str := range bridges {
   116  		vali, ok := bridgeCache.Get(str)
   117  		if ok {
   118  			val := vali.(bridgeInfo)
   119  			if !seenAGs[val.AllocGroup] {
   120  				seenAGs[val.AllocGroup] = true
   121  				hostPort := strings.Split(val.Host, ":")
   122  				if len(hostPort) == 2 {
   123  					val.Host = fmt.Sprintf("%v.sslip.io:%v", strings.Replace(hostPort[0], ".", "-", -1), hostPort[1])
   124  					laboo = append(laboo, val)
   125  				}
   126  			}
   127  		} else {
   128  			vacate()
   129  			return
   130  		}
   131  	}
   132  	if len(laboo) == 0 {
   133  		vacate()
   134  		return
   135  	}
   136  	json.NewEncoder(w).Encode(laboo)
   137  }
   138  
   139  func handleAddBridge(w http.ResponseWriter, r *http.Request) {
   140  	countUserAgent(r)
   141  	// first get the cookie
   142  	cookie, err := hex.DecodeString(r.FormValue("cookie"))
   143  	if err != nil {
   144  		log.Println("can't add bridge (bad cookie)")
   145  		w.WriteHeader(http.StatusInternalServerError)
   146  		return
   147  	}
   148  	// check the cookie
   149  	_, pwd, _ := r.BasicAuth()
   150  	ok, err := checkBridgeKey(pwd)
   151  	if err != nil {
   152  		log.Println("can't add bridge (bad DB)")
   153  		w.WriteHeader(http.StatusInternalServerError)
   154  		return
   155  	}
   156  	if !ok {
   157  		log.Printf("can't add bridge (bad bridge key %v)", pwd)
   158  		w.WriteHeader(http.StatusForbidden)
   159  		return
   160  	}
   161  	bi := bridgeInfo{
   162  		Cookie:     cookie,
   163  		Host:       r.FormValue("host"),
   164  		LastSeen:   time.Now(),
   165  		AllocGroup: r.FormValue("allocGroup"),
   166  	}
   167  	// add the bridge
   168  	addBridge(bi)
   169  }
   170  
   171  func testBridge(bi bridgeInfo) bool {
   172  	rawconn, err := net.Dial("tcp", bi.Host)
   173  	if err != nil {
   174  		log.Println("bridge test failed for", bi.Host, err)
   175  		return false
   176  	}
   177  	defer rawconn.Close()
   178  	realconn, err := cshirt2.ClientLegacy(bi.Cookie, rawconn)
   179  	if err != nil {
   180  		log.Println("bridge test failed for", bi.Host, err)
   181  		return false
   182  	}
   183  	defer realconn.Close()
   184  	realconn.SetDeadline(time.Now().Add(time.Second * 10))
   185  	start := time.Now()
   186  	rlp.Encode(realconn, "ping")
   187  	var resp string
   188  	rlp.Decode(realconn, &resp)
   189  	if resp != "ping" {
   190  		log.Println(bi.Host, "failed ping test!")
   191  		return false
   192  	}
   193  	log.Println(bi.Host, "passed ping test in", time.Since(start))
   194  	return true
   195  }