Last active
December 27, 2022 17:15
-
-
Save AlperRehaYAZGAN/b6ebadb1c05aae851d61ba6cf1df3c66 to your computer and use it in GitHub Desktop.
Golang TCP Proxy with sni support
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"crypto/tls" | |
"fmt" | |
"io" | |
"log" | |
"net" | |
"strings" | |
) | |
func InitTCPSniProxy() { | |
// Create a listener for incoming connections. | |
listener, err := net.Listen("tcp", ":8888") | |
if err != nil { | |
log.Fatal(err) | |
} | |
defer listener.Close() | |
// Accept incoming connections and handle them concurrently. | |
for { | |
conn, err := listener.Accept() | |
if err != nil { | |
log.Println(err) | |
continue | |
} | |
go handleConnection(conn) | |
} | |
} | |
func handleConnection(conn net.Conn) { | |
// Create an SSL/TLS-encrypted socket for the client. | |
tlsConn := tls.Server(conn, &tls.Config{ | |
// Set the SNI callback to determine the target host and port. | |
GetConfigForClient: func(clientHello *tls.ClientHelloInfo) (*tls.Config, error) { | |
// Retrieve the SNI value from the client's SSL/TLS handshake. | |
sni := clientHello.ServerName | |
log.Printf("SNI: %s\n", sni) | |
// Determine the target host and port based on the SNI value. | |
var target string | |
if strings.HasSuffix(sni, "db.mydomain.com") { | |
target = "192.168.1.2:3306" | |
} else if strings.HasSuffix(sni, "redis.mydomain.com") { | |
target = "192.168.1.3:6379" | |
} else { | |
// Return an error if the SNI value is not recognized. | |
return nil, fmt.Errorf("unrecognized SNI: %s", sni) | |
} | |
// Return a new tls.Config with the target host and port. | |
return &tls.Config{ | |
ServerName: target, | |
}, nil | |
}, | |
}) | |
defer tlsConn.Close() | |
// Connect to the target host and port. | |
target, err := net.Dial("tcp", tlsConn.ConnectionState().ServerName) | |
if err != nil { | |
log.Println(err) | |
return | |
} | |
defer target.Close() | |
// Transfer data between the client and target. | |
done := make(chan struct{}) | |
go func() { | |
_, err := io.Copy(target, tlsConn) | |
if err != nil { | |
log.Println(err) | |
} | |
target.Close() | |
done <- struct{}{} | |
}() | |
go func() { | |
_, err := io.Copy(tlsConn, target) | |
if err != nil { | |
log.Println(err) | |
} | |
tlsConn.Close() | |
done <- struct{}{} | |
}() | |
<-done | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment