在 parseHeaders 方法中,如果使用下面的方法拿请求头的数据( req, err := http.ReadRequest(reader)),就会导致一直请求中,但是如果把两个变量写死就不会出现问题
reader := bufio.NewReader(conn)
req, err := http.ReadRequest(reader)
if err != nil {
return "", "", err
}
realIP := req.Header.Get("X-Real-IP")
serverAddr := req.Header.Get("X-Server-Addr")
PS:透传的 table 、回环啥的都配置好了
完整代码如下:
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"strconv"
"syscall"
)
func main() {
listen, err := net.Listen("tcp", "0.0.0.0:9920")
if err != nil {
log.Fatal(err)
}
log.Printf("waiting for incomming connection...")
defer listen.Close()
for {
conn, err := listen.Accept()
if err != nil {
log.Println(err)
continue
}
go clientHandler(conn)
}
}
func clientHandler(client net.Conn) {
log.Printf("proxy %s accepted", client.RemoteAddr().String())
defer client.Close()
//获取需要透代的客户端IP和服务端IP
clientIP, serverAddr, err := parseHeaders(client)
if err != nil {
log.Println(err)
return
}
log.Printf("clientIP: %s serverAddr: %s", clientIP, serverAddr)
//建立透明连接
server, err := createTransparentSocket(clientIP, serverAddr)
if err != nil {
log.Println(err)
return
}
defer server.Close()
streamCopy := func(dst io.Writer, src io.Reader, direction string) {
n, _ := read2Write(dst, src)
log.Printf("%s %d bytes copied", direction, n)
client.Close()
server.Close()
}
go streamCopy(client, server, "server to client")
streamCopy(server, client, "client to server")
}
func createTransparentSocket(clientIP, serverAddr string) (net.Conn, error) {
var socketFD int
var file *os.File
var server net.Conn
var errorPrefix string
var err error
// Create transparent socket
if socketFD, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP); err != nil {
errorPrefix = "syscall.socket()"
goto ReturnError
}
// Set the send timeout, here set to 3 seconds
if err = syscall.SetsockoptTimeval(socketFD, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &syscall.Timeval{Sec: 3}); err != nil {
errorPrefix = "syscall.SetsockoptTimeval()"
goto CloseSocket
}
// Set the transparent transmission flag, allowing binding to IP addresses already in use by other processes
if err = syscall.SetsockoptInt(socketFD, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
errorPrefix = "syscall.SetsockoptInt()"
goto CloseSocket
}
// Use client's IP address as socket local address
if err = syscall.Bind(socketFD, parseClientIP(clientIP)); err != nil {
errorPrefix = "syscall.Bind()"
goto CloseSocket
}
// Connect to server
if err = syscall.Connect(socketFD, parseServerAddr(serverAddr)); err != nil && err.Error() != "operation now in progress" {
errorPrefix = "syscall.Connect()"
goto CloseSocket
}
file = os.NewFile(uintptr(socketFD), serverAddr)
server, err = net.FileConn(file)
if err == nil {
// net.FileConn() has already duplicated this file descriptor
syscall.Close(socketFD)
return server, nil
}
errorPrefix = "net.FileConn()"
CloseSocket:
syscall.Close(socketFD)
ReturnError:
return nil, fmt.Errorf("%s: %s", errorPrefix, err)
}
func read2Write(dst io.Writer, src io.Reader) (written int64, err error) {
if wt, ok := src.(io.WriterTo); ok {
return wt.WriteTo(dst)
}
if rt, ok := dst.(io.ReaderFrom); ok {
return rt.ReadFrom(src)
}
buf := make([]byte, 4096)
return io.CopyBuffer(dst, src, buf)
}
func parseHeaders(conn net.Conn) (string, string, error) {
reader := bufio.NewReader(conn)
req, err := http.ReadRequest(reader)
if err != nil {
return "", "", err
}
realIP := req.Header.Get("X-Real-IP")
serverAddr := req.Header.Get("X-Server-Addr")
//realIP := "192.168.0.126"
//serverAddr := "192.168.0.135:8000"
if realIP == "" || serverAddr == "" {
return "", "", fmt.Errorf("missing required headers")
}
return realIP, serverAddr, nil
}
func parseClientIP(clientIP string) syscall.Sockaddr {
ip := net.ParseIP(clientIP).To4()
return &syscall.SockaddrInet4{
// 客户端端口随机
Port: 0,
Addr: [4]byte{ip[0], ip[1], ip[2], ip[3]},
}
}
func parseServerAddr(serverAddr string) syscall.Sockaddr {
host, port, _ := net.SplitHostPort(serverAddr)
ip := net.ParseIP(host).To4()
destinationPort, _ := strconv.Atoi(port)
return &syscall.SockaddrInet4{
Port: destinationPort,
Addr: [4]byte{ip[0], ip[1], ip[2], ip[3]},
}
}