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 (c *reverseLookupCache) lookup(ipv4 string) string { hit := c.hostnames[ipv4] if hit != nil { if hit.expires.After(time.Now()) { log.Debugf("lookup(): CACHE_HIT") return hit.hostname } else { log.Debugf("lookup(): CACHE_EXPIRED") delete(c.hostnames, ipv4) } } else { log.Debugf("lookup(): CACHE_MISS") } return "" }
func copy(dst io.ReadWriteCloser, src io.ReadWriteCloser, dstname string, srcname string) { if dst == nil { log.Debugf("copy(): oops, dst is nil!") return } if src == nil { log.Debugf("copy(): oops, src is nil!") return } _, err := io.Copy(dst, src) if err != nil { if operr, ok := err.(*net.OpError); ok { if srcname == "directserver" || srcname == "proxyserver" { log.Debugf("copy(): %s->%s: Op=%s, Net=%s, Addr=%v, Err=%v", srcname, dstname, operr.Op, operr.Net, operr.Addr, operr.Err) } if operr.Op == "read" { if srcname == "proxyserver" { incrProxyServerReadErr() } if srcname == "directserver" { incrDirectServerReadErr() } } if operr.Op == "write" { if srcname == "proxyserver" { incrProxyServerWriteErr() } if srcname == "directserver" { incrDirectServerWriteErr() } } } } dst.Close() src.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 }