This commit is contained in:
65
pkg/proxy/dialer.go
Normal file
65
pkg/proxy/dialer.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/gotunnel/pkg/protocol"
|
||||
"github.com/hashicorp/yamux"
|
||||
)
|
||||
|
||||
// TunnelDialer 通过隧道连接的拨号器
|
||||
type TunnelDialer struct {
|
||||
session *yamux.Session
|
||||
}
|
||||
|
||||
// NewTunnelDialer 创建隧道拨号器
|
||||
func NewTunnelDialer(session *yamux.Session) *TunnelDialer {
|
||||
return &TunnelDialer{session: session}
|
||||
}
|
||||
|
||||
// Dial 通过隧道建立连接
|
||||
func (d *TunnelDialer) Dial(network, address string) (net.Conn, error) {
|
||||
stream, err := d.session.Open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 发送代理连接请求
|
||||
req := protocol.ProxyConnectRequest{Target: address}
|
||||
msg, err := protocol.NewMessage(protocol.MsgTypeProxyConnect, req)
|
||||
if err != nil {
|
||||
stream.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := protocol.WriteMessage(stream, msg); err != nil {
|
||||
stream.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 读取连接结果
|
||||
respMsg, err := protocol.ReadMessage(stream)
|
||||
if err != nil {
|
||||
stream.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if respMsg.Type != protocol.MsgTypeProxyResult {
|
||||
stream.Close()
|
||||
return nil, errors.New("unexpected response type")
|
||||
}
|
||||
|
||||
var result protocol.ProxyConnectResult
|
||||
if err := respMsg.ParsePayload(&result); err != nil {
|
||||
stream.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !result.Success {
|
||||
stream.Close()
|
||||
return nil, errors.New(result.Message)
|
||||
}
|
||||
|
||||
return stream, nil
|
||||
}
|
||||
88
pkg/proxy/http.go
Normal file
88
pkg/proxy/http.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HTTPServer HTTP 代理服务
|
||||
type HTTPServer struct {
|
||||
dialer Dialer
|
||||
}
|
||||
|
||||
// NewHTTPServer 创建 HTTP 代理服务
|
||||
func NewHTTPServer(dialer Dialer) *HTTPServer {
|
||||
return &HTTPServer{dialer: dialer}
|
||||
}
|
||||
|
||||
// HandleConn 处理 HTTP 代理连接
|
||||
func (h *HTTPServer) HandleConn(conn net.Conn) error {
|
||||
defer conn.Close()
|
||||
|
||||
reader := bufio.NewReader(conn)
|
||||
req, err := http.ReadRequest(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if req.Method == http.MethodConnect {
|
||||
return h.handleConnect(conn, req)
|
||||
}
|
||||
return h.handleHTTP(conn, req, reader)
|
||||
}
|
||||
|
||||
// handleConnect 处理 CONNECT 方法 (HTTPS)
|
||||
func (h *HTTPServer) handleConnect(conn net.Conn, req *http.Request) error {
|
||||
target := req.Host
|
||||
if !strings.Contains(target, ":") {
|
||||
target = target + ":443"
|
||||
}
|
||||
|
||||
remote, err := h.dialer.Dial("tcp", target)
|
||||
if err != nil {
|
||||
conn.Write([]byte("HTTP/1.1 502 Bad Gateway\r\n\r\n"))
|
||||
return err
|
||||
}
|
||||
defer remote.Close()
|
||||
|
||||
conn.Write([]byte("HTTP/1.1 200 Connection Established\r\n\r\n"))
|
||||
|
||||
go io.Copy(remote, conn)
|
||||
io.Copy(conn, remote)
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleHTTP 处理普通 HTTP 请求
|
||||
func (h *HTTPServer) handleHTTP(conn net.Conn, req *http.Request, reader *bufio.Reader) error {
|
||||
target := req.Host
|
||||
if !strings.Contains(target, ":") {
|
||||
target = target + ":80"
|
||||
}
|
||||
|
||||
remote, err := h.dialer.Dial("tcp", target)
|
||||
if err != nil {
|
||||
conn.Write([]byte("HTTP/1.1 502 Bad Gateway\r\n\r\n"))
|
||||
return err
|
||||
}
|
||||
defer remote.Close()
|
||||
|
||||
// 修改请求路径为相对路径
|
||||
req.URL.Scheme = ""
|
||||
req.URL.Host = ""
|
||||
req.RequestURI = req.URL.Path
|
||||
if req.URL.RawQuery != "" {
|
||||
req.RequestURI += "?" + req.URL.RawQuery
|
||||
}
|
||||
|
||||
// 发送请求到目标
|
||||
if err := req.Write(remote); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 转发响应
|
||||
_, err = io.Copy(conn, remote)
|
||||
return err
|
||||
}
|
||||
62
pkg/proxy/server.go
Normal file
62
pkg/proxy/server.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Server 代理服务器
|
||||
type Server struct {
|
||||
socks5 *SOCKS5Server
|
||||
http *HTTPServer
|
||||
listener net.Listener
|
||||
typ string
|
||||
}
|
||||
|
||||
// NewServer 创建代理服务器
|
||||
func NewServer(typ string, dialer Dialer) *Server {
|
||||
return &Server{
|
||||
socks5: NewSOCKS5Server(dialer),
|
||||
http: NewHTTPServer(dialer),
|
||||
typ: typ,
|
||||
}
|
||||
}
|
||||
|
||||
// Run 启动代理服务
|
||||
func (s *Server) Run(addr string) error {
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.listener = ln
|
||||
log.Printf("[Proxy] %s listening on %s", s.typ, addr)
|
||||
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go s.HandleConn(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) HandleConn(conn net.Conn) {
|
||||
var err error
|
||||
switch s.typ {
|
||||
case "socks5":
|
||||
err = s.socks5.HandleConn(conn)
|
||||
case "http":
|
||||
err = s.http.HandleConn(conn)
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("[Proxy] Error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Close 关闭服务
|
||||
func (s *Server) Close() error {
|
||||
if s.listener != nil {
|
||||
return s.listener.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
145
pkg/proxy/socks5.go
Normal file
145
pkg/proxy/socks5.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
socks5Version = 0x05
|
||||
noAuth = 0x00
|
||||
cmdConnect = 0x01
|
||||
atypIPv4 = 0x01
|
||||
atypDomain = 0x03
|
||||
atypIPv6 = 0x04
|
||||
)
|
||||
|
||||
// SOCKS5Server SOCKS5 代理服务
|
||||
type SOCKS5Server struct {
|
||||
dialer Dialer
|
||||
}
|
||||
|
||||
// Dialer 连接拨号器接口
|
||||
type Dialer interface {
|
||||
Dial(network, address string) (net.Conn, error)
|
||||
}
|
||||
|
||||
// NewSOCKS5Server 创建 SOCKS5 服务
|
||||
func NewSOCKS5Server(dialer Dialer) *SOCKS5Server {
|
||||
return &SOCKS5Server{dialer: dialer}
|
||||
}
|
||||
|
||||
// HandleConn 处理 SOCKS5 连接
|
||||
func (s *SOCKS5Server) HandleConn(conn net.Conn) error {
|
||||
defer conn.Close()
|
||||
|
||||
// 握手阶段
|
||||
if err := s.handshake(conn); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 获取请求
|
||||
target, err := s.readRequest(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 连接目标
|
||||
remote, err := s.dialer.Dial("tcp", target)
|
||||
if err != nil {
|
||||
s.sendReply(conn, 0x05) // Connection refused
|
||||
return err
|
||||
}
|
||||
defer remote.Close()
|
||||
|
||||
// 发送成功响应
|
||||
if err := s.sendReply(conn, 0x00); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 双向转发
|
||||
go io.Copy(remote, conn)
|
||||
io.Copy(conn, remote)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handshake 处理握手
|
||||
func (s *SOCKS5Server) handshake(conn net.Conn) error {
|
||||
buf := make([]byte, 2)
|
||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||
return err
|
||||
}
|
||||
if buf[0] != socks5Version {
|
||||
return errors.New("unsupported SOCKS version")
|
||||
}
|
||||
|
||||
nmethods := int(buf[1])
|
||||
methods := make([]byte, nmethods)
|
||||
if _, err := io.ReadFull(conn, methods); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 响应:使用无认证
|
||||
_, err := conn.Write([]byte{socks5Version, noAuth})
|
||||
return err
|
||||
}
|
||||
|
||||
// readRequest 读取请求
|
||||
func (s *SOCKS5Server) readRequest(conn net.Conn) (string, error) {
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if buf[0] != socks5Version || buf[1] != cmdConnect {
|
||||
return "", errors.New("unsupported command")
|
||||
}
|
||||
|
||||
var host string
|
||||
switch buf[3] {
|
||||
case atypIPv4:
|
||||
ip := make([]byte, 4)
|
||||
if _, err := io.ReadFull(conn, ip); err != nil {
|
||||
return "", err
|
||||
}
|
||||
host = net.IP(ip).String()
|
||||
case atypDomain:
|
||||
lenBuf := make([]byte, 1)
|
||||
if _, err := io.ReadFull(conn, lenBuf); err != nil {
|
||||
return "", err
|
||||
}
|
||||
domain := make([]byte, lenBuf[0])
|
||||
if _, err := io.ReadFull(conn, domain); err != nil {
|
||||
return "", err
|
||||
}
|
||||
host = string(domain)
|
||||
case atypIPv6:
|
||||
ip := make([]byte, 16)
|
||||
if _, err := io.ReadFull(conn, ip); err != nil {
|
||||
return "", err
|
||||
}
|
||||
host = net.IP(ip).String()
|
||||
default:
|
||||
return "", errors.New("unsupported address type")
|
||||
}
|
||||
|
||||
portBuf := make([]byte, 2)
|
||||
if _, err := io.ReadFull(conn, portBuf); err != nil {
|
||||
return "", err
|
||||
}
|
||||
port := binary.BigEndian.Uint16(portBuf)
|
||||
|
||||
return fmt.Sprintf("%s:%d", host, port), nil
|
||||
}
|
||||
|
||||
// sendReply 发送响应
|
||||
func (s *SOCKS5Server) sendReply(conn net.Conn, rep byte) error {
|
||||
// VER REP RSV ATYP BND.ADDR BND.PORT
|
||||
reply := []byte{socks5Version, rep, 0x00, atypIPv4, 0, 0, 0, 0, 0, 0}
|
||||
_, err := conn.Write(reply)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user