func dial(spec string) (*net.TCPConn, error) { host, port, err := net.SplitHostPort(spec) if err != nil { log.Infof("dial(): ERR: could not extract host and port from spec %v: %v", spec, err) return nil, err } remoteAddr, err := net.ResolveIPAddr("ip", host) if err != nil { log.Infof("dial(): ERR: could not resolve %v: %v", host, err) return nil, err } portInt, err := strconv.Atoi(port) if err != nil { log.Infof("dial(): ERR: could not convert network port from string \"%s\" to integer: %v", port, err) return nil, err } remoteAddrAndPort := &net.TCPAddr{IP: remoteAddr.IP, Port: portInt} var localAddr *net.TCPAddr localAddr = nil conn, err := net.DialTCP("tcp", localAddr, remoteAddrAndPort) if err != nil { log.Infof("dial(): ERR: could not connect to %v:%v: %v", remoteAddrAndPort.IP, remoteAddrAndPort.Port, err) } return conn, err }
func checkProxies() { gProxyServers = strings.Split(gProxyServerSpec, ",") // make sure proxies resolve and are listening on specified port, unless -s=1, then don't check for reachability for i, proxySpec := range gProxyServers { if strings.Contains(proxySpec, "@") { var authSplit = strings.Split(proxySpec, "@") var b64Auth = base64.StdEncoding.EncodeToString([]byte(authSplit[0])) gAuthProxyServers[authSplit[1]] = b64Auth proxySpec = authSplit[1] gProxyServers[i] = proxySpec log.Infof("Added authentication %v, %v\n", authSplit[0], b64Auth) } log.Infof("Added proxy server %v\n", proxySpec) if gSkipCheckUpstreamsReachable != 1 { conn, err := dial(proxySpec) if err != nil { log.Infof("Test connection to %v: failed. Removing from proxy server list\n", proxySpec) a := gProxyServers[:i] b := gProxyServers[i+1:] gProxyServers = append(a, b...) continue } conn.Close() } } // do we have at least one proxy server? if len(gProxyServers) == 0 { msg := "None of the proxy servers specified are available. Exiting." log.Infof("%s\n", msg) fmt.Fprintf(os.Stderr, msg) os.Exit(1) } }
func main() { flag.Parse() if gListenAddrPort == "" { flag.Usage() os.Exit(1) } runtime.GOMAXPROCS(runtime.NumCPU() / 2) setupLogging() setupProfiling() setupStats() dirFuncs := buildDirectors(gDirects) director = getDirector(dirFuncs) if gReverseLookups == 1 { gReverseLookupCache = NewReverseLookupCache() } log.RedirectStreams() // if user gave us upstream proxies, check and see if they are alive if gProxyServerSpec != "" { checkProxies() } lnaddr, err := net.ResolveTCPAddr("tcp", gListenAddrPort) if err != nil { panic(err) } listener, err := net.ListenTCP("tcp", lnaddr) if err != nil { panic(err) } defer listener.Close() log.Infof("Listening for connections on %v\n", listener.Addr()) for { conn, err := listener.AcceptTCP() if err != nil { log.Infof("Error accepting connection: %v\n", err) incrAcceptErrors() continue } incrAcceptSuccesses() go handleConnection(conn) } }
func handleConnection(clientConn *net.TCPConn) { if clientConn == nil { log.Debugf("handleConnection(): oops, clientConn is nil") return } // test if the underlying fd is nil remoteAddr := clientConn.RemoteAddr() if remoteAddr == nil { log.Debugf("handleConnection(): oops, clientConn.fd is nil!") return } ipv4, port, clientConn, err := getOriginalDst(clientConn) if err != nil { log.Infof("handleConnection(): can not handle this connection, error occurred in getting original destination ip address/port: %+v\n", err) return } // If no upstream proxies were provided on the command line, assume all traffic should be sent directly if gProxyServerSpec == "" { handleDirectConnection(clientConn, ipv4, port) return } // Evaluate for direct connection ip := net.ParseIP(ipv4) if ok, _ := director(&ip); ok { handleDirectConnection(clientConn, ipv4, port) return } handleProxyConnection(clientConn, ipv4, port) }
func handleDirectConnection(clientConn *net.TCPConn, ipv4 string, port uint16) { // TODO: remove log.Debugf("Enter handleDirectConnection: clientConn=%+v (%T)\n", clientConn, clientConn) if clientConn == nil { log.Debugf("handleDirectConnection(): oops, clientConn is nil!") return } // test if the underlying fd is nil remoteAddr := clientConn.RemoteAddr() if remoteAddr == nil { log.Debugf("handleDirectConnection(): oops, clientConn.fd is nil!") return } ipport := fmt.Sprintf("%s:%d", ipv4, port) directConn, err := dial(ipport) if err != nil { clientConnRemoteAddr := "?" if clientConn != nil { clientConnRemoteAddr = fmt.Sprintf("%v", clientConn.RemoteAddr()) } directConnRemoteAddr := "?" if directConn != nil { directConnRemoteAddr = fmt.Sprintf("%v", directConn.RemoteAddr()) } log.Infof("DIRECT|%v->%v|Could not connect, giving up: %v", clientConnRemoteAddr, directConnRemoteAddr, err) return } log.Debugf("DIRECT|%v->%v|Connected to remote end", clientConn.RemoteAddr(), directConn.RemoteAddr()) incrDirectConnections() go copy(clientConn, directConn, "client", "directserver") go copy(directConn, clientConn, "directserver", "client") }
func checkProxies() { gProxyServers = strings.Split(gProxyServerSpec, ",") // make sure proxies resolve and are listening on specified port for i, proxySpec := range gProxyServers { conn, err := dial(proxySpec) if err != nil { log.Infof("Test connection to %v: failed. Removing from proxy server list\n", proxySpec) a := gProxyServers[:i] b := gProxyServers[i+1:] gProxyServers = append(a, b...) continue } conn.Close() log.Infof("Added proxy server %v\n", proxySpec) } // do we have at least one proxy server? if len(gProxyServers) == 0 { msg := "None of the proxy servers specified are available. Exiting." log.Infof("%s\n", msg) fmt.Fprintf(os.Stderr, msg) os.Exit(1) } }
func setupStats() { c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGUSR1) go func() { for _ = range c { f, err := os.Create(STATSFILE) if err != nil { log.Infof("ERR: Could not open stats file \"%s\": %v", STATSFILE, err) continue } fmt.Fprintf(f, "%s\n\n", versionString()) fmt.Fprintf(f, "STATISTICS as of %v:\n", time.Now().Format(time.UnixDate)) fmt.Fprintf(f, " Go version: %v\n", runtime.Version()) fmt.Fprintf(f, " Number of logical CPUs on system: %v\n", runtime.NumCPU()) fmt.Fprintf(f, " GOMAXPROCS: %v\n", runtime.GOMAXPROCS(-1)) fmt.Fprintf(f, " Goroutines currently running: %v\n", runtime.NumGoroutine()) fmt.Fprintf(f, " Number of cgo calls made by any_proxy: %v\n", runtime.NumCgoCall()) fmt.Fprintf(f, "\n") fmt.Fprintf(f, " accept successes: %v\n", numAcceptSuccesses()) fmt.Fprintf(f, " accept errors: %v\n", numAcceptErrors()) fmt.Fprintf(f, " getsockopt(SO_ORIGINAL_DST) errors: %v\n", numGetOriginalDstErrors()) fmt.Fprintf(f, "\n") fmt.Fprintf(f, " connections sent directly: %v\n", numDirectConnections()) fmt.Fprintf(f, " direct connection read errors: %v\n", numDirectServerReadErr()) fmt.Fprintf(f, " direct connection write errors: %v\n", numDirectServerWriteErr()) fmt.Fprintf(f, "\n") fmt.Fprintf(f, " connections sent to upstream proxy: %v\n", numProxiedConnections()) fmt.Fprintf(f, " proxy connection read errors: %v\n", numProxyServerReadErr()) fmt.Fprintf(f, " proxy connection write errors: %v\n", numProxyServerWriteErr()) fmt.Fprintf(f, " code 200 response from upstream: %v\n", numProxy200Responses()) fmt.Fprintf(f, " code 400 response from upstream: %v\n", numProxy400Responses()) fmt.Fprintf(f, "other (non 200/400) response from upstream: %v\n", numProxyNon200Responses()) fmt.Fprintf(f, " no response to CONNECT from upstream: %v\n", numProxyNoConnectResponses()) f.Close() } }() }
func handleProxyConnection(clientConn *net.TCPConn, ipv4 string, port uint16) { var proxyConn net.Conn var err error var success bool = false var host string var headerXFF string = "" // TODO: remove log.Debugf("Enter handleProxyConnection: clientConn=%+v (%T)\n", clientConn, clientConn) if clientConn == nil { log.Debugf("handleProxyConnection(): oops, clientConn is nil!") return } // test if the underlying fd is nil remoteAddr := clientConn.RemoteAddr() if remoteAddr == nil { log.Debugf("handleProxyConnect(): oops, clientConn.fd is nil!") err = errors.New("ERR: clientConn.fd is nil") return } host, _, err = net.SplitHostPort(remoteAddr.String()) if err == nil { headerXFF = fmt.Sprintf("X-Forwarded-For: %s\r\n", host) } if gReverseLookups == 1 { hostname := gReverseLookupCache.lookup(ipv4) if hostname != "" { ipv4 = hostname } else { names, err := net.LookupAddr(ipv4) if err == nil && len(names) > 0 { gReverseLookupCache.store(ipv4, names[0]) ipv4 = names[0] } } } for _, proxySpec := range gProxyServers { proxyConn, err = dial(proxySpec) if err != nil { log.Debugf("PROXY|%v->%v->%s:%d|Trying next proxy.", clientConn.RemoteAddr(), proxySpec, ipv4, port) continue } log.Debugf("PROXY|%v->%v->%s:%d|Connected to proxy\n", clientConn.RemoteAddr(), proxyConn.RemoteAddr(), ipv4, port) var authString = "" if val, auth := gAuthProxyServers[proxySpec]; auth { authString = fmt.Sprintf("\r\nProxy-Authorization: Basic %s", val) } connectString := fmt.Sprintf("CONNECT %s:%d HTTP/1.0%s\r\n%s\r\n", ipv4, port, authString, headerXFF) log.Debugf("PROXY|%v->%v->%s:%d|Sending to proxy: %s\n", clientConn.RemoteAddr(), proxyConn.RemoteAddr(), ipv4, port, strconv.Quote(connectString)) fmt.Fprintf(proxyConn, connectString) status, err := bufio.NewReader(proxyConn).ReadString('\n') log.Debugf("PROXY|%v->%v->%s:%d|Received from proxy: %s", clientConn.RemoteAddr(), proxyConn.RemoteAddr(), ipv4, port, strconv.Quote(status)) if err != nil { log.Infof("PROXY|%v->%v->%s:%d|ERR: Could not find response to CONNECT: err=%v. Trying next proxy", clientConn.RemoteAddr(), proxyConn.RemoteAddr(), ipv4, port, err) incrProxyNoConnectResponses() continue } if strings.Contains(status, "400") { // bad request log.Debugf("PROXY|%v->%v->%s:%d|Status from proxy=400 (Bad Request)", clientConn.RemoteAddr(), proxyConn.RemoteAddr(), ipv4, port) log.Debugf("%v: Response from proxy=400", proxySpec) incrProxy400Responses() copy(clientConn, proxyConn, "client", "proxyserver") return } if strings.Contains(status, "301") || strings.Contains(status, "302") && gClientRedirects == 1 { log.Debugf("PROXY|%v->%v->%s:%d|Status from proxy=%s (Redirect), relaying response to client", clientConn.RemoteAddr(), proxyConn.RemoteAddr(), ipv4, port, strconv.Quote(status)) incrProxy300Responses() fmt.Fprintf(clientConn, status) copy(clientConn, proxyConn, "client", "proxyserver") return } if strings.Contains(status, "200") == false { log.Infof("PROXY|%v->%v->%s:%d|ERR: Proxy response to CONNECT was: %s. Trying next proxy.\n", clientConn.RemoteAddr(), proxyConn.RemoteAddr(), ipv4, port, strconv.Quote(status)) incrProxyNon200Responses() continue } else { incrProxy200Responses() } log.Debugf("PROXY|%v->%v->%s:%d|Proxied connection", clientConn.RemoteAddr(), proxyConn.RemoteAddr(), ipv4, port) success = true break } if proxyConn == nil { log.Debugf("handleProxyConnection(): oops, proxyConn is nil!") return } if success == false { log.Infof("PROXY|%v->UNAVAILABLE->%s:%d|ERR: Tried all proxies, but could not establish connection. Giving up.\n", clientConn.RemoteAddr(), ipv4, port) fmt.Fprintf(clientConn, "HTTP/1.0 503 Service Unavailable\r\nServer: go-any-proxy\r\nX-AnyProxy-Error: ERR_NO_PROXIES\r\n\r\n") clientConn.Close() return } incrProxiedConnections() go copy(clientConn, proxyConn, "client", "proxyserver") go copy(proxyConn, clientConn, "proxyserver", "client") }
func getOriginalDst(clientConn *net.TCPConn) (ipv4 string, port uint16, newTCPConn *net.TCPConn, err error) { if clientConn == nil { log.Debugf("copy(): oops, dst is nil!") err = errors.New("ERR: clientConn is nil") return } // test if the underlying fd is nil remoteAddr := clientConn.RemoteAddr() if remoteAddr == nil { log.Debugf("getOriginalDst(): oops, clientConn.fd is nil!") err = errors.New("ERR: clientConn.fd is nil") return } srcipport := fmt.Sprintf("%v", clientConn.RemoteAddr()) newTCPConn = nil // net.TCPConn.File() will cause the receiver's (clientConn) socket to be placed in blocking mode. // The workaround is to take the File returned by .File(), do getsockopt() to get the original // destination, then create a new *net.TCPConn by calling net.Conn.FileConn(). The new TCPConn // will be in non-blocking mode. What a pain. clientConnFile, err := clientConn.File() if err != nil { log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: could not get a copy of the client connection's file object", srcipport) return } else { clientConn.Close() } // Get original destination // this is the only syscall in the Golang libs that I can find that returns 16 bytes // Example result: &{Multiaddr:[2 0 31 144 206 190 36 45 0 0 0 0 0 0 0 0] Interface:0} // port starts at the 3rd byte and is 2 bytes long (31 144 = port 8080) // IPv4 address starts at the 5th byte, 4 bytes long (206 190 36 45) addr, err := syscall.GetsockoptIPv6Mreq(int(clientConnFile.Fd()), syscall.IPPROTO_IP, SO_ORIGINAL_DST) log.Debugf("getOriginalDst(): SO_ORIGINAL_DST=%+v\n", addr) if err != nil { log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: getsocketopt(SO_ORIGINAL_DST) failed: %v", srcipport, err) return } newConn, err := net.FileConn(clientConnFile) if err != nil { log.Infof("GETORIGINALDST|%v->?->%v|ERR: could not create a FileConn fron clientConnFile=%+v: %v", srcipport, addr, clientConnFile, err) return } if _, ok := newConn.(*net.TCPConn); ok { newTCPConn = newConn.(*net.TCPConn) clientConnFile.Close() } else { errmsg := fmt.Sprintf("ERR: newConn is not a *net.TCPConn, instead it is: %T (%v)", newConn, newConn) log.Infof("GETORIGINALDST|%v->?->%v|%s", srcipport, addr, errmsg) err = errors.New(errmsg) return } ipv4 = itod(uint(addr.Multiaddr[4])) + "." + itod(uint(addr.Multiaddr[5])) + "." + itod(uint(addr.Multiaddr[6])) + "." + itod(uint(addr.Multiaddr[7])) port = uint16(addr.Multiaddr[2])<<8 + uint16(addr.Multiaddr[3]) return }