首页 > 其他分享 >同一个ip:port上同时开启socks5和http代理服务器

同一个ip:port上同时开启socks5和http代理服务器

时间:2024-08-18 16:04:46浏览次数:4  
标签:http err nil ip 代理服务器 com net buf addr

代码如下所示:

package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"strconv"
	"sync"
)

func main() {
	addr := flag.String("s", ":1080", "proxy server address")
	flag.Parse()

	if err := proxy(*addr); err != nil {
		panic(err)
	}
}

const netTCP = "tcp"

func proxy(addr string) error {
	lc, err := net.Listen(netTCP, addr)
	if err != nil {
		return err
	}
	//goland:noinspection GoUnhandledErrorResult
	defer lc.Close()

	fmt.Printf("proxy server listening on %s\n", addr)
	for {
		if ac, ea := lc.Accept(); ea == nil {
			go handleProxyFunc(ac)
		}
	}
}

type poolBuf struct {
	buf0, buf1 []byte

	buf *bufio.Reader
}

var bytePool = &sync.Pool{New: func() any {
	const leakyBufSize = 64 << 10
	return &poolBuf{
		buf0: make([]byte, leakyBufSize),
		buf1: make([]byte, leakyBufSize),
		buf:  new(bufio.Reader),
	}
}}

func handleProxyFunc(c net.Conn) {
	if err := handleProxy(c); err != nil {
		log.Printf("%+v", err)
	}
}

//goland:noinspection GoUnhandledErrorResult
func handleProxy(c net.Conn) error {
	defer c.Close()

	bp := bytePool.Get().(*poolBuf)
	defer bytePool.Put(bp)

	bp.buf.Reset(c)
	tp, err := bp.buf.Peek(2)
	if err != nil {
		return err
	}

	var rc net.Conn
	switch tp[0] {
	case socksVer5:
		rc, err = dialSocks5(c, bp.buf, bp.buf0, int(tp[1]))
		if err != nil {
			return err
		}
		defer rc.Close()

		_, err = c.Write(socks5Established)
		if err != nil {
			return err
		}
	case 'C', 'c', 'G', 'g', 'P', 'p', 'O', 'o', 'H', 'h', 'D', 'd', 'T', 't':
		// CONNECT,GET,POST/PUT/PATCH,OPTIONS,HEAD,DELETE,TRACE
		req, err := http.ReadRequest(bp.buf)
		if err != nil {
			return err
		}

		if req.Method == http.MethodConnect {
			rc, err = net.Dial(netTCP, req.Host)
			if err != nil {
				return err
			}
			defer rc.Close()

			_, err = c.Write(httpEstablished)
			if err != nil {
				return err
			}
		} else {
			req.Close = true
			resp, err := http.DefaultTransport.RoundTrip(req)
			if err != nil {
				return err
			}

			err = resp.Write(c)
			if resp.Body != nil {
				resp.Body.Close()
			}
			return err
		}
	default:
		return errors.New("unsupported protocol version")
	}

	ret := make(chan struct{})
	go func() {
		copyBuffer(c, rc, bp.buf0)
		ret <- struct{}{}
	}()
	copyBuffer(rc, bp.buf, bp.buf1)
	<-ret
	return nil
}

func copyBuffer(dst io.Writer, src io.Reader, buf []byte) {
	var (
		nr, nw int
		er, ew error
	)
	for er == nil {
		nr, er = src.Read(buf)
		if nr > 0 {
			nw, ew = dst.Write(buf[:nr])
			if nw < 0 || nr < nw || ew != nil || nr != nw {
				return // Refer io.CopyBuffer
			}
		}
	}
}

var (
	httpEstablished   = []byte("HTTP/1.1 200 Connection Established\r\n\r\n")
	socks5Established = []byte{socksVer5, 0, 0, 1, 0, 0, 0, 0, 0, 0}
)

const (
	socksVer5  = 5
	aTypIPV4   = 1
	aTypDomain = 3
	aTypIPV6   = 4
)

func dialSocks5(w io.Writer, r io.Reader, buf []byte, n int) (net.Conn, error) {
	_, err := io.ReadFull(r, buf[:n+2])
	if err != nil {
		return nil, err
	}

	_, err = w.Write(socks5Established[:2])
	if err != nil {
		return nil, err
	}

	_, err = io.ReadFull(r, buf[:5])
	if err != nil {
		return nil, err
	}
	if buf[0] != socksVer5 {
		return nil, fmt.Errorf("invalid ver %d", buf[0])
	}
	if buf[1] != 1 { // cmd connect = 1
		return nil, fmt.Errorf("invalid cmd %d", buf[1])
	}

	switch buf[3] {
	case aTypDomain:
		// VER CMD RSV TYPE LEN DST.ADDR DST.PORT
		//  1   1   1   1    1     LEN      2
		n = int(buf[4]) + 7
	case aTypIPV4:
		// VER CMD RSV TYPE DST.ADDR DST.PORT
		//  1   1   1   1   ipv4Len     2
		n = net.IPv4len + 6
	case aTypIPV6:
		// VER CMD RSV TYPE DST.ADDR DST.PORT
		//  1   1   1   1   ipv6Len     2
		n = net.IPv6len + 6
	default:
		return nil, errors.New("socks addr type not supported")
	}

	_, err = io.ReadFull(r, buf[5:n])
	if err != nil {
		return nil, err
	}

	var addr string
	switch buf[3] {
	case aTypDomain:
		n = 5 + int(buf[4])
		addr = string(buf[5:n])
	case aTypIPV4:
		n = 4 + net.IPv4len
		addr = net.IP(buf[4:n]).String()
	case aTypIPV6:
		n = 4 + net.IPv6len
		addr = net.IP(buf[4:n]).String()
	}

	addr = net.JoinHostPort(addr, strconv.FormatUint(uint64(buf[n])<<8|uint64(buf[n+1]), 10))
	return net.Dial(netTCP, addr)
}

测试命令如下:

# http CONNECT proxy method
curl -v -x http://127.0.0.1:1080 https://cn.bing.com https://www.baidu.com https://www.sina.com
# http direct proxy method
curl -v -x http://127.0.0.1:1080 cn.bing.com www.baidu.com www.sina.com
# socks5 proxy method
curl -v -x socks5://127.0.0.1:1080 https://cn.bing.com https://www.baidu.com https://www.sina.com
# socks5h proxy method
curl -v -x socks5h://127.0.0.1:1080 https://cn.bing.com https://www.baidu.com https://www.sina.com

标签:http,err,nil,ip,代理服务器,com,net,buf,addr
From: https://www.cnblogs.com/janbar/p/18365730

相关文章