update: use raw TCP for HTTP server

This commit is contained in:
2025-04-02 23:27:39 +07:00
parent 58f15d5a67
commit 221adf9581
8 changed files with 232 additions and 545 deletions

View File

@ -1,179 +0,0 @@
package httpServer
import (
"bufio"
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"strings"
"tunnel_pls/session"
"tunnel_pls/utils"
indexView "tunnel_pls/view/index"
)
type RouteHandler http.HandlerFunc
// Simple Router Struct
type Router struct {
routes map[string]map[string]http.Handler
}
// NewRouter initializes a new router
func NewRouter() *Router {
return &Router{
routes: make(map[string]map[string]http.Handler),
}
}
// Handle registers a route with an http.Handler
func (r *Router) Handle(method, path string, handler http.Handler) {
if _, exists := r.routes[method]; !exists {
r.routes[method] = make(map[string]http.Handler)
}
r.routes[method][path] = handler
}
// HandleFunc registers a route with a function
func (r *Router) HandleFunc(method, path string, handlerFunc func(http.ResponseWriter, *http.Request)) {
r.Handle(method, path, http.HandlerFunc(handlerFunc))
}
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if methodRoutes, exists := r.routes[req.Method]; exists {
if handler, exists := methodRoutes[req.URL.Path]; exists {
handler.ServeHTTP(w, req)
return
}
}
http.Error(w, "404 Not Found", http.StatusNotFound)
}
type tcpResponseWriter struct {
conn net.Conn
header http.Header
status int
}
func (w *tcpResponseWriter) Header() http.Header {
return w.header
}
func (w *tcpResponseWriter) WriteHeader(statusCode int) {
w.status = statusCode
}
func (w *tcpResponseWriter) Write(data []byte) (int, error) {
fmt.Println("here")
resp := fmt.Sprintf("HTTP/1.1 %d %s\r\n", w.status, http.StatusText(w.status))
for k, v := range w.header {
resp += fmt.Sprintf("%s: %s\r\n", k, v[0])
}
resp += "\r\n" + string(data)
return w.conn.Write([]byte(resp))
}
var router = NewRouter()
func Listen() {
server, err := net.Listen("tcp", ":80")
if err != nil {
log.Fatal(err)
return
}
router.HandleFunc("GET", "/", func(w http.ResponseWriter, r *http.Request) {
indexView.Main("Main Page", utils.Getenv("domain")).Render(r.Context(), w)
return
})
router.HandleFunc("GET", "/public/output.css", func(w http.ResponseWriter, r *http.Request) {
open, err := os.Open("public/output.css")
if err != nil {
return
}
data, _ := io.ReadAll(open)
fmt.Fprintf(w, string(data))
})
//fileserver := http.FileServer(http.Dir("./public"))
//router.Handle("/public/", http.StripPrefix("/public", fileserver))
defer server.Close()
log.Println("Listening on :80")
for {
conn, err := server.Accept()
if err != nil {
log.Fatal(err)
return
}
go handleRequest(conn)
}
}
func handleRequest(conn net.Conn) {
defer conn.Close()
var rawRequest string
reader := bufio.NewReader(conn)
r, err := http.ReadRequest(reader)
if err != nil {
fmt.Println("Error reading request:", err)
return
}
writer := &tcpResponseWriter{
conn: conn,
header: make(http.Header),
status: http.StatusOK,
}
if r.Host == utils.Getenv("domain") {
router.ServeHTTP(writer, r)
return
}
if utils.Getenv("tls_enabled") == "false" {
http.Redirect(writer, r, fmt.Sprintf("https://%s%s", r.Host, r.URL.RequestURI()), http.StatusFound)
return
}
slug := strings.Split(r.Host, ".")[0]
if slug == "" {
fmt.Println("Error parsing slug: ", r.Host)
return
}
sshSession, ok := session.Clients[slug]
if !ok {
fmt.Println("Error finding ssh session: ", slug)
return
}
rawRequest += fmt.Sprintf("%s %s %s\r\n", r.Method, r.URL.RequestURI(), r.Proto)
rawRequest += fmt.Sprintf("Host: %s\r\n", r.Host)
for k, v := range r.Header {
rawRequest += fmt.Sprintf("%s: %s\r\n", k, v[0])
}
rawRequest += "\r\n"
if r.Body != nil {
body, err := io.ReadAll(r.Body)
if err != nil {
log.Println("Error reading request body:", err)
} else {
rawRequest += string(body)
}
}
payload := []byte(rawRequest)
host, originPort := session.ParseAddr(conn.RemoteAddr().String())
sshSession.GetForwardedConnection(conn, host, sshSession.Connection, payload, originPort, 80, r.RequestURI, r.Method, r.Proto)
}

View File

@ -1,180 +0,0 @@
package httpServer
import (
"crypto/tls"
"fmt"
"golang.org/x/net/http2"
"io"
"log"
"net/http"
"os"
"strings"
"tunnel_pls/session"
"tunnel_pls/utils"
indexView "tunnel_pls/view/index"
)
func ListenTLS(config *tls.Config) {
server := &http.Server{
Addr: ":443",
TLSConfig: config,
Handler: http.HandlerFunc(handleRequestTLS),
}
http2.ConfigureServer(server, &http2.Server{})
fmt.Println("Listening on :8443 (HTTP/2 over TLS)")
log.Fatal(server.ListenAndServeTLS("", ""))
}
func handleRequestTLS(w http.ResponseWriter, r *http.Request) {
_, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to read request", http.StatusInternalServerError)
return
}
defer r.Body.Close()
var rawRequest string
if r.Host == utils.Getenv("domain") {
TLSRouter().ServeHTTP(w, r)
return
}
slug := strings.Split(r.Host, ".")[0]
if slug == "" {
fmt.Println("Error parsing slug: ", r.Host)
return
}
sshSession, ok := session.Clients[slug]
if !ok {
fmt.Println("Error finding ssh session: ", slug)
http.Error(w, "No tunnel found on this address", http.StatusNotFound)
return
}
rawRequest += fmt.Sprintf("%s %s %s\r\n", r.Method, r.URL.RequestURI(), "HTTP/1.1")
rawRequest += fmt.Sprintf("Host: %s\r\n", r.Host)
for k, v := range r.Header {
rawRequest += fmt.Sprintf("%s: %s\r\n", k, v[0])
}
rawRequest += "\r\n"
if r.Body != nil {
body, err := io.ReadAll(r.Body)
if err != nil {
log.Println("Error reading request body:", err)
} else {
rawRequest += string(body)
}
}
payload := []byte(rawRequest)
host, originPort := session.ParseAddr(r.RemoteAddr)
response := sshSession.GetForwardedConnectionTLS(host, sshSession.Connection, payload, originPort, 80, r.RequestURI, r.Method, r.Proto)
forbiddenHeaders := map[string]bool{
"connection": true,
"transfer-encoding": true,
"upgrade": true,
"keep-alive": true,
}
for k, v := range response.Header {
k = strings.ToLower(k)
if forbiddenHeaders[k] {
continue
}
if k == ":status" || k == ":method" || k == ":path" || k == ":authority" {
continue
}
w.Header().Set(k, v[0])
}
io.Copy(w, response.Body)
return
}
func TLSRouter() *http.ServeMux {
handler := http.NewServeMux()
handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
indexView.Main("Main Page", utils.Getenv("domain")).Render(r.Context(), w)
return
})
handler.HandleFunc("/public/output.css", func(w http.ResponseWriter, r *http.Request) {
open, err := os.Open("public/output.css")
if err != nil {
return
}
defer open.Close()
w.Header().Set("Content-Type", "text/css; charset=utf-8")
io.Copy(w, open)
return
})
return handler
}
//func handleRequestTLS(conn net.Conn) {
// defer conn.Close()
// var rawRequest string
//
// reader := bufio.NewReader(conn)
// r, err := http.ReadRequest(reader)
// if err != nil {
// fmt.Println("Error reading request:", err)
// return
// }
//
// writer := &tcpResponseWriter{
// conn: conn,
// header: make(http.Header),
// status: http.StatusOK,
// }
//
// if r.Host == utils.Getenv("domain") {
// router.ServeHTTP(writer, r)
// return
// }
//
// slug := strings.Split(r.Host, ".")[0]
// if slug == "" {
// fmt.Println("Error parsing slug: ", r.Host)
// return
// }
//
// sshSession, ok := session.Clients[slug]
// if !ok {
// fmt.Println("Error finding ssh session: ", slug)
// return
// }
//
// rawRequest += fmt.Sprintf("%s %s %s\r\n", r.Method, r.URL.RequestURI(), r.Proto)
// rawRequest += fmt.Sprintf("Host: %s\r\n", r.Host)
//
// for k, v := range r.Header {
// rawRequest += fmt.Sprintf("%s: %s\r\n", k, v[0])
// }
// rawRequest += "\r\n"
//
// if r.Body != nil {
// body, err := io.ReadAll(r.Body)
// if err != nil {
// log.Println("Error reading request body:", err)
// } else {
// rawRequest += string(body)
// }
// }
//
// payload := []byte(rawRequest)
//
// host, originPort := session.ParseAddr(conn.RemoteAddr().String())
// sshSession.GetForwardedConnection(conn, host, sshSession.Connection, payload, originPort, 80, r.RequestURI, r.Method, r.Proto)
//}

BIN
main

Binary file not shown.

View File

@ -1,3 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

63
server/http.go Normal file
View File

@ -0,0 +1,63 @@
package server
import (
"bufio"
"errors"
"fmt"
"log"
"net"
"net/http"
"strings"
"tunnel_pls/session"
)
func NewHTTPServer() error {
listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:80"))
if err != nil {
return errors.New("Error listening: " + err.Error())
}
go func() {
for {
conn, err := listener.Accept()
if err != nil {
if errors.Is(err, net.ErrClosed) {
return
}
log.Printf("Error accepting connection: %v", err)
continue
}
go Handler(conn)
}
}()
return nil
}
func Handler(conn net.Conn) {
reader := bufio.NewReader(conn)
request, err := http.ReadRequest(reader)
if err != nil {
fmt.Println("Error reading request:", err)
return
}
host := strings.Split(request.Host, ".")
if len(host) < 1 {
conn.Write([]byte("HTTP/1.1 400 Bad Request\r\n\r\n"))
conn.Close()
return
}
slug := host[0]
sshSession, ok := session.Clients[slug]
if !ok {
conn.Write([]byte("HTTP/1.1 400 Bad Request\r\n\r\n"))
conn.Close()
return
}
request.Header.Set("Connection", "keep-alive")
request.Header.Set("Keep-Alive", "timeout=60")
go sshSession.HandleForwardedConnectionHTTP(conn, sshSession.Connection, request)
}

View File

@ -1,14 +1,11 @@
package server
import (
"crypto/tls"
"fmt"
"golang.org/x/crypto/ssh"
"log"
"net"
"net/http"
httpServer "tunnel_pls/http"
"tunnel_pls/utils"
)
type Server struct {
@ -23,16 +20,12 @@ func NewServer(config ssh.ServerConfig) *Server {
log.Fatalf("failed to listen on port 2200: %v", err)
return nil
}
go httpServer.Listen()
if utils.Getenv("tls_enabled") == "true" {
cert, err := tls.LoadX509KeyPair(utils.Getenv("cert_loc"), utils.Getenv("key_loc"))
go func() {
err := NewHTTPServer()
if err != nil {
log.Fatal("Failed to load key pair:", err)
log.Fatalf("failed to start http server: %v", err)
}
tlsConfig := &tls.Config{Certificates: []tls.Certificate{cert}, NextProtos: []string{"h2"}}
go httpServer.ListenTLS(tlsConfig)
}
}()
return &Server{
Conn: &listener,
Config: &config,

View File

@ -13,10 +13,14 @@ import (
"net/http"
"strconv"
"time"
"tunnel_pls/proto"
"tunnel_pls/utils"
)
type UserConnection struct {
Reader io.Reader
Writer net.Conn
}
func (s *Session) handleGlobalRequest() {
for {
select {
@ -25,81 +29,8 @@ func (s *Session) handleGlobalRequest() {
return
}
if req.Type == "tcpip-forward" {
log.Println("Port forwarding request detected")
reader := bytes.NewReader(req.Payload)
addr, err := readSSHString(reader)
if err != nil {
log.Println("Failed to read address from payload:", err)
req.Reply(false, nil)
continue
}
var portToBind uint32
if err := binary.Read(reader, binary.BigEndian, &portToBind); err != nil {
log.Println("Failed to read port from payload:", err)
req.Reply(false, nil)
continue
}
if portToBind == 80 || portToBind == 443 {
s.TunnelType = HTTP
s.ForwardedPort = uint16(portToBind)
var slug string
for {
slug = utils.GenerateRandomString(32)
if _, ok := Clients[slug]; ok {
continue
}
break
}
Clients[slug] = s
s.Slug = slug
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, uint32(portToBind))
log.Printf("Forwarding approved on port: %d", portToBind)
if utils.Getenv("tls_enabled") == "true" {
s.ConnChannels[0].Write([]byte(fmt.Sprintf("Forwarding your traffic to https://%s.%s \r\n", slug, utils.Getenv("domain"))))
} else {
s.ConnChannels[0].Write([]byte(fmt.Sprintf("Forwarding your traffic to http://%s.%s \r\n", slug, utils.Getenv("domain"))))
}
req.Reply(true, buf.Bytes())
} else {
s.TunnelType = TCP
log.Printf("Requested forwarding on %s:%d", addr, portToBind)
listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", portToBind))
if err != nil {
log.Printf("Failed to bind to port %d: %v", portToBind, err)
req.Reply(false, nil)
continue
}
s.Listener = listener
s.ConnChannels[0].Write([]byte(fmt.Sprintf("Forwarding your traffic to %s:%d \r\n", utils.Getenv("domain"), portToBind)))
go func() {
for {
conn, err := listener.Accept()
if err != nil {
if errors.Is(err, net.ErrClosed) {
return
}
log.Printf("Error accepting connection: %v", err)
continue
}
go s.HandleForwardedConnection(conn, s.Connection, portToBind)
}
}()
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, uint32(portToBind))
log.Printf("Forwarding approved on port: %d", portToBind)
req.Reply(true, buf.Bytes())
}
s.handleTCPIPForward(req)
continue
} else {
req.Reply(false, nil)
}
@ -109,6 +40,88 @@ func (s *Session) handleGlobalRequest() {
}
}
func (s *Session) handleTCPIPForward(req *ssh.Request) {
log.Println("Port forwarding request detected")
reader := bytes.NewReader(req.Payload)
addr, err := readSSHString(reader)
if err != nil {
log.Println("Failed to read address from payload:", err)
req.Reply(false, nil)
return
}
var portToBind uint32
if err := binary.Read(reader, binary.BigEndian, &portToBind); err != nil {
log.Println("Failed to read port from payload:", err)
req.Reply(false, nil)
return
}
if portToBind == 80 || portToBind == 443 {
s.TunnelType = HTTP
s.ForwardedPort = uint16(portToBind)
var slug string
for {
slug = utils.GenerateRandomString(32)
if _, ok := Clients[slug]; ok {
return
}
break
}
Clients[slug] = s
s.Slug = slug
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, uint32(80))
log.Printf("Forwarding approved on port: %d", 80)
if utils.Getenv("tls_enabled") == "true" {
s.ConnChannels[0].Write([]byte(fmt.Sprintf("Forwarding your traffic to https://%s.%s \r\n", slug, utils.Getenv("domain"))))
} else {
s.ConnChannels[0].Write([]byte(fmt.Sprintf("Forwarding your traffic to http://%s.%s \r\n", slug, utils.Getenv("domain"))))
}
req.Reply(true, buf.Bytes())
} else {
s.TunnelType = TCP
log.Printf("Requested forwarding on %s:%d", addr, portToBind)
listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", portToBind))
if err != nil {
log.Printf("Failed to bind to port %d: %v", portToBind, err)
req.Reply(false, nil)
return
}
s.Listener = listener
s.ConnChannels[0].Write([]byte(fmt.Sprintf("Forwarding your traffic to %s:%d \r\n", utils.Getenv("domain"), portToBind)))
go func() {
for {
conn, err := listener.Accept()
if err != nil {
if errors.Is(err, net.ErrClosed) {
return
}
log.Printf("Error accepting connection: %v", err)
continue
}
go s.HandleForwardedConnection(UserConnection{
Reader: nil,
Writer: conn,
}, s.Connection, portToBind)
}
}()
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, uint32(portToBind))
log.Printf("Forwarding approved on port: %d", portToBind)
req.Reply(true, buf.Bytes())
}
}
func (s *Session) HandleSessionChannel(newChannel ssh.NewChannel) {
connection, requests, err := newChannel.Accept()
s.ConnChannels = append(s.ConnChannels, connection)
@ -213,70 +226,53 @@ func (s *Session) HandleSessionChannel(newChannel ssh.NewChannel) {
}()
}
func (s *Session) HandleForwardedConnection(conn net.Conn, sshConn *ssh.ServerConn, port uint32) {
defer conn.Close()
log.Printf("Handling new forwarded connection from %s", conn.RemoteAddr())
host, originPort := ParseAddr(conn.RemoteAddr().String())
func (s *Session) HandleForwardedConnection(conn UserConnection, sshConn *ssh.ServerConn, port uint32) {
defer conn.Writer.Close()
log.Printf("Handling new forwarded connection from %s", conn.Writer.RemoteAddr())
host, originPort := ParseAddr(conn.Writer.RemoteAddr().String())
payload := createForwardedTCPIPPayload(host, originPort, port)
channel, reqs, err := sshConn.OpenChannel("forwarded-tcpip", payload)
if err != nil {
log.Printf("Failed to open forwarded-tcpip channel: %v", err)
return
}
defer channel.Close()
conn.SetReadDeadline(time.Now().Add(2 * time.Second))
connReader := bufio.NewReader(conn)
var isHttp bool
header, err := connReader.Peek(7)
if err != nil {
isHttp = false
} else {
isHttp = proto.IsHttpRequest(header)
}
conn.SetReadDeadline(time.Time{})
go io.Copy(channel, connReader)
reader := bufio.NewReader(channel)
_, err = reader.Peek(1)
if err == io.EOF {
if isHttp {
io.Copy(conn, bytes.NewReader([]byte("HTTP/1.1 502 Bad Gateway\r\nContent-Length: 11\r\nContent-Type: text/plain\r\n\r\nBad Gateway")))
} else {
conn.Write([]byte("Could not forward request to the tunnel addr\r\n"))
}
s.ConnChannels[0].Write([]byte("Could not forward request to the tunnel addr\r\n"))
return
} else {
io.Copy(conn, reader)
}
go func() {
for req := range reqs {
req.Reply(false, nil)
}
}()
}
func (s *Session) GetForwardedConnection(conn net.Conn, host string, sshConn *ssh.ServerConn, payload []byte, originPort, port uint32, path, method, proto string) {
defer conn.Close()
channelPayload := createForwardedTCPIPPayload(host, originPort, port)
channel, reqs, err := sshConn.OpenChannel("forwarded-tcpip", channelPayload)
if err != nil {
log.Printf("Failed to open forwarded-tcpip channel: %v", err)
return
}
defer channel.Close()
if conn.Reader == nil {
conn.Reader = bufio.NewReader(conn.Writer)
}
go io.Copy(channel, conn.Reader)
reader := bufio.NewReader(channel)
_, err = reader.Peek(1)
if err == io.EOF {
fmt.Println("error babi")
}
io.Copy(conn.Writer, reader)
}
connReader := bufio.NewReader(conn)
initalPayload := bytes.NewReader(payload)
io.Copy(channel, initalPayload)
go io.Copy(channel, connReader)
func (s *Session) HandleForwardedConnectionHTTP(conn net.Conn, sshConn *ssh.ServerConn, request *http.Request) {
defer conn.Close()
fmt.Println(request)
channelPayload := createForwardedTCPIPPayload(request.Host, 80, 80)
channel, reqs, err := sshConn.OpenChannel("forwarded-tcpip", channelPayload)
go func() {
for req := range reqs {
req.Reply(false, nil)
}
}()
var requestBuffer bytes.Buffer
if err := request.Write(&requestBuffer); err != nil {
fmt.Println("Error serializing request:", err)
channel.Close()
conn.Close()
return
}
channel.Write(requestBuffer.Bytes())
reader := bufio.NewReader(channel)
_, err = reader.Peek(1)
@ -285,46 +281,42 @@ func (s *Session) GetForwardedConnection(conn net.Conn, host string, sshConn *ss
s.ConnChannels[0].Write([]byte("Could not forward request to the tunnel addr\r\n"))
return
} else {
s.ConnChannels[0].Write([]byte(fmt.Sprintf("\033[32m %s -- [%s] \"%s %s %s\" \r\n \033[0m", host, time.Now().Format("02/Jan/2006 15:04:05"), method, path, proto)))
s.ConnChannels[0].Write([]byte(fmt.Sprintf("\033[32m %s -- [%s] \"%s %s %s\" \r\n \033[0m", request.Host, time.Now().Format("02/Jan/2006 15:04:05"), request.Method, request.RequestURI, request.Proto)))
io.Copy(conn, reader)
}
go func() {
for req := range reqs {
req.Reply(false, nil)
}
}()
}
func (s *Session) GetForwardedConnectionTLS(host string, sshConn *ssh.ServerConn, payload []byte, originPort, port uint32, path, method, proto string) *http.Response {
channelPayload := createForwardedTCPIPPayload(host, originPort, port)
channel, reqs, err := sshConn.OpenChannel("forwarded-tcpip", channelPayload)
if err != nil {
log.Printf("Failed to open forwarded-tcpip channel: %v", err)
return nil
}
defer channel.Close()
initalPayload := bytes.NewReader(payload)
io.Copy(channel, initalPayload)
go func() {
for req := range reqs {
req.Reply(false, nil)
}
}()
reader := bufio.NewReader(channel)
_, err = reader.Peek(1)
if err == io.EOF {
s.ConnChannels[0].Write([]byte("Could not forward request to the tunnel addr\r\n"))
return nil
} else {
s.ConnChannels[0].Write([]byte(fmt.Sprintf("\033[32m %s -- [%s] \"%s %s %s\" \r\n \033[0m", host, time.Now().Format("02/Jan/2006 15:04:05"), method, path, proto)))
response, _ := http.ReadResponse(reader, nil)
return response
}
}
//TODO: Implement HTTPS forwarding
//func (s *Session) GetForwardedConnectionTLS(host string, sshConn *ssh.ServerConn, payload []byte, originPort, port uint32, path, method, proto string) (*http.Response, error) {
// channelPayload := createForwardedTCPIPPayload(host, originPort, port)
// channel, reqs, err := sshConn.OpenChannel("forwarded-tcpip", channelPayload)
// if err != nil {
// return nil, err
// }
// defer channel.Close()
//
// initalPayload := bytes.NewReader(payload)
// io.Copy(channel, initalPayload)
//
// go func() {
// for req := range reqs {
// req.Reply(false, nil)
// }
// }()
//
// reader := bufio.NewReader(channel)
// _, err = reader.Peek(1)
// if err == io.EOF {
// return nil, err
// } else {
// s.ConnChannels[0].Write([]byte(fmt.Sprintf("\033[32m %s -- [%s] \"%s %s %s\" \r\n \033[0m", host, time.Now().Format("02/Jan/2006 15:04:05"), method, path, proto)))
// response, err := http.ReadResponse(reader, nil)
// if err != nil {
// return nil, err
// }
// return response, err
// }
//}
func writeSSHString(buffer *bytes.Buffer, str string) {
binary.Write(buffer, binary.BigEndian, uint32(len(str)))

View File

@ -8,15 +8,16 @@ import (
)
type Session struct {
ID uuid.UUID
Slug string
ConnChannels []ssh.Channel
Connection *ssh.ServerConn
GlobalRequest <-chan *ssh.Request
Listener net.Listener
TunnelType TunnelType
ForwardedPort uint16
Done chan bool
ID uuid.UUID
Slug string
ConnChannels []ssh.Channel
Connection *ssh.ServerConn
GlobalRequest <-chan *ssh.Request
Listener net.Listener
TunnelType TunnelType
ForwardedPort uint16
Done chan bool
ForwardedChannel ssh.Channel
}
type TunnelType string