github.com/geph-official/geph2@v0.22.6-0.20210211030601-f527cb59b0df/cmd/geph-client/main.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "flag" 6 "fmt" 7 "math/rand" 8 mrand "math/rand" 9 "net" 10 "net/http" 11 "net/http/httputil" 12 "net/url" 13 "os" 14 "os/user" 15 "runtime/debug" 16 "strings" 17 "time" 18 19 "github.com/vharitonsky/iniflags" 20 21 "github.com/acarl005/stripansi" 22 "github.com/ethereum/go-ethereum/rlp" 23 "github.com/geph-official/geph2/libs/bdclient" 24 "golang.org/x/net/proxy" 25 26 log "github.com/sirupsen/logrus" 27 ) 28 29 var username string 30 var password string 31 var ticketFile string 32 var binderFront string 33 var binderHost string 34 var exitName string 35 var exitKey string 36 var forceBridges bool 37 38 var loginCheck bool 39 var binderProxy string 40 41 var direct bool 42 43 var socksAddr string 44 var httpAddr string 45 var statsAddr string 46 var dnsAddr string 47 var fakeDNS bool 48 var bypassChinese bool 49 50 var singleHop string 51 var upstreamProxy string 52 var additionalBridges string 53 var forceWarpfront bool 54 55 var sWrap *multipool 56 57 // GitVersion is the build version 58 var GitVersion string 59 60 var binders *bdclient.Multiclient 61 62 func getBindInfo() (string, string) { 63 fronts := strings.Split(binderFront, ",") 64 hosts := strings.Split(binderHost, ",") 65 randIdx := rand.Int() % len(fronts) 66 return fronts[randIdx], hosts[randIdx] 67 } 68 69 // find the fastest binder and stick to it 70 func binderRace() { 71 fronts := strings.Split(binderFront, ",") 72 hosts := strings.Split(binderHost, ",") 73 if len(fronts) != len(hosts) { 74 panic("binderFront and binderHost must be of identical length") 75 } 76 var bbb []*bdclient.Client 77 for i := 0; i < len(fronts); i++ { 78 i := i 79 bdc := bdclient.NewClient(fronts[i], hosts[i], fmt.Sprintf("geph_client/%v", GitVersion)) 80 bbb = append(bbb, bdc) 81 } 82 binders = bdclient.NewMulticlient(bbb) 83 } 84 85 func memMiser() { 86 for { 87 debug.FreeOSMemory() 88 time.Sleep(time.Second * 5) 89 } 90 } 91 92 func main() { 93 // debug.SetGCPercent(5) 94 // go memMiser() 95 go mrand.Seed(time.Now().UnixNano()) 96 log.SetFormatter(&log.TextFormatter{ 97 FullTimestamp: false, 98 ForceColors: true, 99 }) 100 log.SetLevel(log.DebugLevel) 101 102 // configfile path 103 usr, err := user.Current() 104 if err != nil { 105 log.Println("cannot read current user info, consider using -config=/path/to/cfgfile") 106 } else { 107 // default config file: $HOME/.config/client.conf, make sure chmod 600! 108 // use -config=/path/to/cfgfile to override 109 iniflags.SetConfigFile(usr.HomeDir + "/.config/client.conf") 110 iniflags.SetAllowMissingConfigFile(true) 111 } 112 113 // flags 114 flag.StringVar(&username, "username", "", "username") 115 flag.StringVar(&password, "password", "", "password") 116 flag.StringVar(&ticketFile, "ticketFile", "", "location for caching auth tickets") 117 flag.StringVar(&binderFront, "binderFront", "https://www.cdn77.com/v2,https://netlify.com/v2,https://ajax.aspnetcdn.com/v2", "binder domain-fronting hosts, comma separated") 118 flag.StringVar(&binderHost, "binderHost", "1680337695.rsc.cdn77.org,loving-bell-981479.netlify.app,gephbinder-vzn.azureedge.net", "real hostname of the binder, comma separated") 119 flag.StringVar(&exitName, "exitName", "us-sfo-01.exits.geph.io", "qualified name of the exit node selected") 120 flag.StringVar(&exitKey, "exitKey", "2f8571e4795032433098af285c0ce9e43c973ac3ad71bf178e4f2aaa39794aec", "ed25519 pubkey of the selected exit") 121 flag.BoolVar(&forceBridges, "forceBridges", false, "force the use of obfuscated bridges") 122 flag.StringVar(&socksAddr, "socksAddr", "localhost:9909", "SOCKS5 listening address") 123 flag.StringVar(&httpAddr, "httpAddr", "localhost:9910", "HTTP proxy listener") 124 flag.StringVar(&statsAddr, "statsAddr", "localhost:9809", "HTTP listener for statistics") 125 flag.StringVar(&dnsAddr, "dnsAddr", "localhost:9983", "local DNS listener") 126 flag.BoolVar(&fakeDNS, "fakeDNS", true, "return fake results for DNS") 127 flag.BoolVar(&loginCheck, "loginCheck", false, "do a login check and immediately exit with code 0") 128 flag.StringVar(&binderProxy, "binderProxy", "", "if set, proxy the binder at the given listening address and do nothing else") 129 // flag.StringVar(&cachePath, "cachePath", os.TempDir()+"/geph-cache.db", "location of state cache") 130 flag.StringVar(&upstreamProxy, "upstreamProxy", "", "upstream SOCKS5 proxy") 131 flag.StringVar(&additionalBridges, "additionalBridges", "", "additional bridges, in the form of cookie1@host1;cookie2@host2 etc") 132 flag.StringVar(&singleHop, "singleHop", "", "if set in form pk@host:port, location of a single-hop server. OVERRIDES BINDER AND AUTHENTICATION!") 133 flag.BoolVar(&bypassChinese, "bypassChinese", false, "bypass proxy for Chinese domains") 134 flag.BoolVar(&forceWarpfront, "forceWarpfront", false, "force use of warpfront") 135 iniflags.Parse() 136 hackDNS() 137 if dnsAddr != "" { 138 go doDNS() 139 } 140 go listenStats() 141 if GitVersion == "" { 142 GitVersion = "NOVER" 143 } 144 logPipeR, logPipeW, _ := os.Pipe() 145 log.SetOutput(logPipeW) 146 go func() { 147 buffi := bufio.NewReader(logPipeR) 148 for { 149 line, err := buffi.ReadString('\n') 150 if err != nil { 151 return 152 } 153 fmt.Fprint(os.Stderr, line) 154 useStats(func(sc *stats) { 155 sc.LogLines = append(sc.LogLines, 156 stripansi.Strip(strings.TrimSpace(line))) 157 }) 158 } 159 }() 160 binderRace() 161 162 log.Println("GephNG version", GitVersion) 163 // special actions 164 if loginCheck { 165 log.Println("loginCheck mode") 166 go func() { 167 time.Sleep(time.Second * 60) 168 os.Exit(10) 169 }() 170 } 171 if singleHop == "" { 172 if binderProxy != "" { 173 log.Println("binderProxy mode on", binderProxy) 174 revProx := &httputil.ReverseProxy{ 175 Director: func(req *http.Request) { 176 bURL, bHost := getBindInfo() 177 bURLP, err := url.Parse(bURL) 178 if err != nil { 179 panic(err) 180 } 181 log.Println("reverse proxying", req.Method, req.URL) 182 req.Host = bHost 183 req.URL.Scheme = bURLP.Scheme 184 req.URL.Host = bURLP.Host 185 req.URL.Path = bURLP.Path + "/" + req.URL.Path 186 }, 187 ModifyResponse: func(resp *http.Response) error { 188 resp.Header.Add("Access-Control-Allow-Origin", "*") 189 resp.Header.Add("Access-Control-Expose-Headers", "*") 190 resp.Header.Add("Access-Control-Allow-Methods", "*") 191 resp.Header.Add("Access-Control-Allow-Headers", "*") 192 return nil 193 }, 194 } 195 if err := http.ListenAndServe(binderProxy, revProx); err != nil { 196 panic(err) 197 } 198 } 199 200 // connect to bridge 201 // automatically pick mode 202 if upstreamProxy != "" { 203 log.Println("upstream proxy enabled, no bridges") 204 direct = true 205 } else if !forceBridges { 206 retry: 207 var country string 208 binders.Do(func(b *bdclient.Client) error { 209 var cinfo bdclient.ClientInfo 210 cinfo, err = b.GetClientInfo() 211 country = cinfo.Country 212 return err 213 }) 214 if err != nil { 215 log.Println("cannot get country", err) 216 goto retry 217 } else { 218 log.Println("country is", country) 219 if country == "CN" { 220 log.Println("in CHINA, must use bridges") 221 } else { 222 log.Println("disabling bridges") 223 direct = true 224 } 225 } 226 } else { 227 direct = false 228 } 229 } 230 sWrap = newMultipool() 231 232 // confirm we are connected 233 func() { 234 for { 235 rm, _, ok := sWrap.DialCmd("ip") 236 if !ok { 237 log.Println("FAILED to get IP, retrying...") 238 time.Sleep(time.Second) 239 continue 240 } 241 defer rm.Close() 242 var ip string 243 err := rlp.Decode(rm, &ip) 244 if err != nil { 245 log.Println("Uh oh, cannot get IP!", err) 246 continue 247 } 248 ip = strings.TrimSpace(ip) 249 log.Println("Successfully got external IP", ip) 250 useStats(func(sc *stats) { 251 sc.Connected = true 252 sc.PublicIP = ip 253 }) 254 return 255 } 256 }() 257 go listenHTTP() 258 listenSocks() 259 } 260 261 func dialTun(dest string) (conn net.Conn, err error) { 262 sks, err := proxy.SOCKS5("tcp", socksAddr, nil, proxy.Direct) 263 if err != nil { 264 return 265 } 266 conn, err = sks.Dial("tcp", dest) 267 return 268 }