doukezi4576
2017-12-07 16:20
浏览 89
已采纳

如何在Go中从同一侦听器提供SSH和HTTP(S)通信?

I want to be able to serve both SSH and HTTPS connections on the same port. To do so, I need to examine the first few bytes sent by the client, and if it starts with "SSH", serve the connection one way, but let the Go HTTP server handle it if it isn't SSH.

But the http package will only work with a net.Listener. Once I accept a connection from the listener and examine the first bytes, it's too late to send the net.Conn to http.

How can I accomplish this?

图片转代码服务由CSDN问答提供 功能建议

我希望能够在同一端口上同时提供SSH和HTTPS连接。 为此,我需要检查客户端发送的前几个字节,如果它以“ SSH”开头,则以一种方式提供连接,但是如果不是SSH,请让Go HTTP服务器处理它。

但是http包仅适用于net.Listener。 一旦我接受了来自侦听器的连接并检查了第一个字节,就将net.Conn发送到http已经太晚了。

我该如何完成? < / DIV>

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

1条回答 默认 最新

  • douxunzui1519 2017-12-07 16:20
    已采纳

    Make two custom listeners, one for SSH connections, and one for all other connections. Then accept connections from the raw listener, peek at the first bytes, and send the connection to the appropriate listener.

    l := net.Listen("tcp", ":443")
    sshListener, httpListener := MuxListener(l)
    go sshServer.Serve(sshListener)
    go httpServer.Serve(httpListener)
    

    MuxListener:

    // MuxListener takes a net.Listener and returns two listeners, one that
    // accepts connections that start with "SSH", and another that accepts
    // all others. This allows SSH and HTTPS to be served from the same port.
    func MuxListener(l net.Listener) (ssh net.Listener, other net.Listener) {
        sshListener, otherListener := newListener(l), newListener(l)
        go func() {
            for {
                conn, err := l.Accept()
                if err != nil {
                    log.Println("Error accepting conn:", err)
                    continue
                }
                conn.SetReadDeadline(time.Now().Add(time.Second * 10))
                bconn := bufferedConn{conn, bufio.NewReaderSize(conn, 3)}
                p, err := bconn.Peek(3)
                conn.SetReadDeadline(time.Time{})
                if err != nil {
                    log.Println("Error peeking into conn:", err)
                    continue
                }
                prefix := string(p)
                selectedListener := otherListener
                if prefix == "SSH" {
                    selectedListener = sshListener
                }
                if selectedListener.accept != nil {
                    selectedListener.accept <- bconn
                }
            }
        }()
        return sshListener, otherListener
    }
    

    listener:

    type listener struct {
        accept chan net.Conn
        net.Listener
    }
    
    func newListener(l net.Listener) *listener {
        return &listener{
            make(chan net.Conn),
            l,
        }
    }
    
    func (l *listener) Accept() (net.Conn, error) {
        if l.accept == nil {
            return nil, errors.New("Listener closed")
        }
        return <-l.accept, nil
    }
    
    func (l *listener) Close() error {
        close(l.accept)
        l.accept = nil
        return nil
    }
    

    bufferedConn:

    type bufferedConn struct {
        net.Conn
        r *bufio.Reader
    }
    
    func (b bufferedConn) Peek(n int) ([]byte, error) {
        return b.r.Peek(n)
    }
    
    func (b bufferedConn) Read(p []byte) (int, error) {
        return b.r.Read(p)
    }
    
    点赞 打赏 评论

相关推荐 更多相似问题