Golang中如何从TLS net.Conn获取主机服务器名称

Golang中如何从TLS net.Conn获取主机服务器名称 我有一个使用通配符域名 *.myserver.com 的 Go TLS 服务器,这样远程客户端就可以通过 foo.myserver.combar.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

6 回复

ClientHelloInfo 中,两者不是都有吗?

getConfigForClient() 内部,你可以读取两者并存储在一个全局映射中。

更多关于Golang中如何从TLS net.Conn获取主机服务器名称的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


您可以将这些信息存储在数据结构中,然后在处理程序中进行查找。ClientHelloInfo 也包含 net.Conn,因此您或许可以使用一个以 net.Conn 为键的映射。

问题在于,net.Conn 是一个接口,而 getConfigForClientClientHelloInfo 提供的实例与 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中通过上下文传递信息。

回到顶部