2017-12-07 14 views
2

私は、同じポート上でSSHとHTTPSの両方の接続を提供できるようにします。これを行うには、クライアントから送信された最初の数バイトを調べ、「SSH」で始まる場合は接続を一方向に処理しますが、SSH以外の場合はGo HTTPサーバーで処理します。Goの同じリスナーからのSSHとHTTP(S)トラフィックをどのように処理できますか?

しかし、httpパッケージはnet.Listenerでのみ動作します。リスナーからの接続を受け入れて最初のバイトを調べると、net.Connをhttpに送信するのは遅すぎます。

どうすればこの問題を解決できますか?

答えて

4

SSH接続の場合は1つ、その他の接続の場合は1つのカスタムリスナーを2つ作成します。その後、生のリスナーからの接続を受け入れ、最初のバイトを確認し、適切なリスナーに接続を送信します。

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 
} 

リスナー:

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) 
} 
関連する問題