diff --git a/admin_listener.go b/admin_listener.go deleted file mode 100644 index 224767b..0000000 --- a/admin_listener.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "fmt" - "net" -) - -type AdminListener struct { - connChan chan (net.Conn) -} - -func NewAdminListener() *AdminListener { - connChan := make(chan (net.Conn)) - return &AdminListener{connChan} -} - -// implement net.Listener -func (l *AdminListener) Accept() (net.Conn, error) { - // TODO: error conditions? - conn := <-l.connChan - return conn, nil -} -func (l *AdminListener) Close() error { - // TODO - fmt.Println("AdminListener Close") - return nil -} -func (l *AdminListener) Addr() net.Addr { - // TODO - fmt.Println("AdminListener Addr") - return nil -} diff --git a/boringproxy.go b/boringproxy.go index 1a94398..3cf0f7e 100644 --- a/boringproxy.go +++ b/boringproxy.go @@ -8,9 +8,7 @@ import ( "io" "io/ioutil" "log" - "net" "net/http" - "sync" ) type BoringProxyConfig struct { @@ -29,11 +27,9 @@ type BoringProxy struct { config *BoringProxyConfig auth *Auth tunMan *TunnelManager - adminListener *AdminListener - certConfig *certmagic.Config } -func NewBoringProxy() *BoringProxy { +func Listen() { config := &BoringProxyConfig{} @@ -54,7 +50,6 @@ func NewBoringProxy() *BoringProxy { certConfig := certmagic.NewDefault() tunMan := NewTunnelManager(certConfig) - adminListener := NewAdminListener() err = certConfig.ManageSync([]string{config.AdminDomain}) if err != nil { @@ -64,106 +59,78 @@ func NewBoringProxy() *BoringProxy { auth := NewAuth() - p := &BoringProxy{config, auth, tunMan, adminListener, certConfig} - - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - p.handleAdminRequest(w, r) - }) + p := &BoringProxy{config, auth, tunMan} api := NewApi(config, auth, tunMan) http.Handle("/api/", http.StripPrefix("/api", api)) - go http.Serve(adminListener, nil) - - log.Println("BoringProxy ready") - - return p -} - -func (p *BoringProxy) Run() { - - listener, err := net.Listen("tcp", ":443") + tlsConfig := &tls.Config{ + GetCertificate: certConfig.GetCertificate, + NextProtos: []string{"h2"}, + } + tlsListener, err := tls.Listen("tcp", ":443", tlsConfig) if err != nil { log.Fatal(err) } - for { - conn, err := listener.Accept() - if err != nil { - log.Print(err) - continue - } - go p.handleConnection(conn) - } -} - -func (p *BoringProxy) handleConnection(clientConn net.Conn) { - // TODO: does this need to be closed manually, or is it handled when decryptedConn is closed? - //defer clientConn.Close() - - log.Println("handleConnection") - - var serverName string - - decryptedConn := tls.Server(clientConn, &tls.Config{ - GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { - - serverName = clientHello.ServerName - - return p.certConfig.GetCertificate(clientHello) - }, + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + if r.Host == config.AdminDomain { + p.handleAdminRequest(w, r) + } else { + p.proxyRequest(w, r) + } }) - //defer decryptedConn.Close() - // Need to manually do handshake to ensure serverName is populated by this point. Usually Handshake() - // is automatically called on first read/write - decryptedConn.Handshake() + log.Println("BoringProxy ready") - if serverName == p.config.AdminDomain { - p.handleAdminConnection(decryptedConn) - } else { - p.handleTunnelConnection(decryptedConn, serverName) - } + http.Serve(tlsListener, nil) } -func (p *BoringProxy) handleAdminConnection(decryptedConn net.Conn) { - p.adminListener.connChan <- decryptedConn -} +func (p *BoringProxy) proxyRequest(w http.ResponseWriter, r *http.Request) { -func (p *BoringProxy) handleTunnelConnection(decryptedConn net.Conn, serverName string) { - - defer decryptedConn.Close() - - port, err := p.tunMan.GetPort(serverName) + port, err := p.tunMan.GetPort(r.Host) if err != nil { log.Print(err) - errMessage := fmt.Sprintf("HTTP/1.1 500 Internal server error\n\nNo tunnel attached to %s", serverName) - decryptedConn.Write([]byte(errMessage)) + errMessage := fmt.Sprintf("No tunnel attached to %s", r.Host) + w.WriteHeader(500) + io.WriteString(w, errMessage) return } - upstreamAddr := fmt.Sprintf("127.0.0.1:%d", port) + httpClient := &http.Client{} - upstreamConn, err := net.Dial("tcp", upstreamAddr) - if err != nil { + downstreamReqHeaders := r.Header.Clone() + + upstreamAddr := fmt.Sprintf("localhost:%d", port) + upstreamUrl := fmt.Sprintf("http://%s%s", upstreamAddr, r.URL.RequestURI()) + + upstreamReq, err := http.NewRequest(r.Method, upstreamUrl, r.Body) + if err != nil { log.Print(err) + errMessage := fmt.Sprintf("%s", err) + w.WriteHeader(500) + io.WriteString(w, errMessage) return - } - defer upstreamConn.Close() + } - var wg sync.WaitGroup - wg.Add(2) + upstreamReq.Header = downstreamReqHeaders - go func() { - io.Copy(decryptedConn, upstreamConn) - //decryptedConn.(*net.TCPConn).CloseWrite() - wg.Done() - }() - go func() { - io.Copy(upstreamConn, decryptedConn) - //upstreamConn.(*net.TCPConn).CloseWrite() - wg.Done() - }() + upstreamRes, err := httpClient.Do(upstreamReq) + if err != nil { + log.Print(err) + errMessage := fmt.Sprintf("%s", err) + w.WriteHeader(502) + io.WriteString(w, errMessage) + return + } + defer upstreamRes.Body.Close() - wg.Wait() + downstreamResHeaders := w.Header() + + for k, v := range upstreamRes.Header { + downstreamResHeaders[k] = v + } + + w.WriteHeader(upstreamRes.StatusCode) + io.Copy(w, upstreamRes.Body) } diff --git a/main.go b/main.go index 766ac94..8fc2560 100644 --- a/main.go +++ b/main.go @@ -18,8 +18,7 @@ func main() { switch command { case "server": log.Println("Starting up") - proxy := NewBoringProxy() - proxy.Run() + Listen() case "client": client := NewBoringProxyClient() diff --git a/ssh_server.go b/ssh_server.go deleted file mode 100644 index 3a4e3c4..0000000 --- a/ssh_server.go +++ /dev/null @@ -1,93 +0,0 @@ -package main - -import ( - "fmt" - "golang.org/x/crypto/ssh" - "io/ioutil" - "log" - "net" -) - -type SshServer struct { - config *ssh.ServerConfig - listener net.Listener -} - -func NewSshServer() *SshServer { - config := &ssh.ServerConfig{} - - privateBytes, err := ioutil.ReadFile("id_rsa_boringproxy") - if err != nil { - log.Fatal("Failed to load private key: ", err) - } - - private, err := ssh.ParsePrivateKey(privateBytes) - if err != nil { - log.Fatal("Failed to parse private key: ", err) - } - - config.AddHostKey(private) - - listener, err := net.Listen("tcp", "0.0.0.0:2022") - if err != nil { - log.Fatal("failed to listen for connection: ", err) - } - - server := &SshServer{config, listener} - - go server.acceptAll() - - return server -} - -func (s *SshServer) acceptAll() { - for { - nConn, err := s.listener.Accept() - if err != nil { - log.Print("failed to accept incoming connection: ", err) - continue - } - - go s.handleServerConn(nConn) - } -} - -func (s *SshServer) handleServerConn(nConn net.Conn) { - - var password string - - s.config.PasswordCallback = func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { - password = string(pass) - if c.User() == "user" && string(pass) == "yolo" { - return nil, nil - } - return nil, fmt.Errorf("password rejected for %q", c.User()) - } - - conn, chans, reqs, err := ssh.NewServerConn(nConn, s.config) - if err != nil { - log.Print("failed to handshake: ", err) - return - } - - fmt.Println(password) - - go ssh.DiscardRequests(reqs) - - go func() { - for newChannel := range chans { - newChannel.Reject(ssh.ResourceShortage, "too bad") - } - }() - - ch, cReqs, err := conn.OpenChannel("boringproxy-tunnel", []byte{25, 25}) - if err != nil { - log.Print(err) - return - } - - go ssh.DiscardRequests(cReqs) - - ch.Write([]byte("Hi there")) - ch.Close() -}