コード例 #1
0
ファイル: any_proxy.go プロジェクト: ryanchapman/go-any-proxy
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
}
コード例 #2
0
ファイル: any_proxy.go プロジェクト: ryanchapman/go-any-proxy
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)
	}
}
コード例 #3
0
ファイル: any_proxy.go プロジェクト: ryanchapman/go-any-proxy
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)
	}
}
コード例 #4
0
ファイル: any_proxy.go プロジェクト: ryanchapman/go-any-proxy
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)
}
コード例 #5
0
ファイル: any_proxy.go プロジェクト: ryanchapman/go-any-proxy
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")
}
コード例 #6
0
ファイル: any_proxy.go プロジェクト: rajing/go-any-proxy
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)
	}
}
コード例 #7
0
ファイル: stats.go プロジェクト: cnlangzi/go-any-proxy
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()
		}
	}()
}
コード例 #8
0
ファイル: any_proxy.go プロジェクト: ryanchapman/go-any-proxy
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")
}
コード例 #9
0
ファイル: any_proxy.go プロジェクト: ryanchapman/go-any-proxy
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
}