Golang中如何从TLS net.Conn获取主机服务器名称
Golang中如何从TLS net.Conn获取主机服务器名称
我有一个使用通配符域名 *.myserver.com 的 Go TLS 服务器,这样远程客户端就可以通过 foo.myserver.com 或 bar.myserver.com 来调用它。有没有办法可以从 Listener.Accept() 返回的 net.Conn 中找出客户端使用了哪个主机名?
到目前为止,我发现如果我使用服务器 tls.Config 上的 GetConfigForClient,那么该函数确实能获取到服务器名称:
tlsConfig := &tls.Config{
GetConfigForClient: getConfigForClient,
}
func getConfigForClient(hi *tls.ClientHelloInfo) (*tls.Config, error) {
// 确实会打印完整名称:foo.myserver.com
log.Println("Server.getConfigForClient hostName:", hi.ServerName)
. . .
}
但我找不到方法将此信息获取或传递给监听器处理程序。
感谢任何帮助。
更多关于Golang中如何从TLS net.Conn获取主机服务器名称的实战教程也可以访问 https://www.itying.com/category-94-b0.html
在 ClientHelloInfo 中,两者不是都有吗?
在 getConfigForClient() 内部,你可以读取两者并存储在一个全局映射中。
更多关于Golang中如何从TLS net.Conn获取主机服务器名称的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
您可以将这些信息存储在数据结构中,然后在处理程序中进行查找。ClientHelloInfo 也包含 net.Conn,因此您或许可以使用一个以 net.Conn 为键的映射。
问题在于,net.Conn 是一个接口,而 getConfigForClient 中 ClientHelloInfo 提供的实例与 net.Listener.Accept() 返回的并非同一个东西。
这正是我所期望的情况,但我目前还没找到方法。我查阅的资料都无法同时访问主机名和 net.Conn。
// 代码示例:假设这里有一段Go代码
func example() {
// 代码内容保持不变
}
我们目前通过使用一个键为 net.Conn.RemoteAddr().String() 的映射来解决问题,该键代表远程客户端的IP地址和端口。这似乎工作得不错,但在所有情况下它都会是唯一的吗?
func main() {
fmt.Println("hello world")
}
在Go中,可以通过类型断言将net.Conn转换为tls.Conn,然后获取ConnectionState来访问客户端Hello中的服务器名称。以下是示例代码:
package main
import (
"crypto/tls"
"fmt"
"net"
)
func handleConnection(conn net.Conn) {
defer conn.Close()
// 类型断言为tls.Conn
if tlsConn, ok := conn.(*tls.Conn); ok {
// 获取TLS连接状态
state := tlsConn.ConnectionState()
// 从ClientHelloInfo中获取服务器名称
serverName := state.ServerName
fmt.Printf("Client requested server name: %s\n", serverName)
// 继续处理连接...
// 例如:读取/写入数据
} else {
fmt.Println("Not a TLS connection")
}
}
func main() {
cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
panic(err)
}
config := &tls.Config{
Certificates: []tls.Certificate{cert},
}
listener, err := tls.Listen("tcp", ":443", config)
if err != nil {
panic(err)
}
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
fmt.Printf("Accept error: %v\n", err)
continue
}
go handleConnection(conn)
}
}
或者,如果你需要在GetConfigForClient中传递信息,可以通过自定义上下文实现:
package main
import (
"context"
"crypto/tls"
"fmt"
"net"
)
type connKey struct{}
func getConfigForClient(hi *tls.ClientHelloInfo) (*tls.Config, error) {
// 创建带服务器名称的上下文
ctx := context.WithValue(hi.Context(), connKey{}, hi.ServerName)
hi.Context = func() context.Context { return ctx }
return &tls.Config{
Certificates: []tls.Certificate{yourCertificate},
}, nil
}
func handleConnection(conn net.Conn) {
defer conn.Close()
if tlsConn, ok := conn.(*tls.Conn); ok {
// 从连接状态获取上下文
state := tlsConn.ConnectionState()
if serverName, ok := state.Context.Value(connKey{}).(string); ok {
fmt.Printf("Client requested server name: %s\n", serverName)
}
}
}
func main() {
config := &tls.Config{
GetConfigForClient: getConfigForClient,
}
listener, err := tls.Listen("tcp", ":443", config)
if err != nil {
panic(err)
}
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
fmt.Printf("Accept error: %v\n", err)
continue
}
go handleConnection(conn)
}
}
第一个示例直接从ConnectionState.ServerName获取服务器名称,这是最直接的方法。第二个示例展示了如何在GetConfigForClient中通过上下文传递信息。

