// Handles a new http connection from the public internet func httpHandler(tcpConn net.Conn, proto string) { // wrap up the connection for logging conn := conn.NewHttp(tcpConn, "pub") defer conn.Close() defer func() { // recover from failures if r := recover(); r != nil { conn.Warn("httpHandler failed with error %v", r) } }() // Make sure we detect dead connections while we decide how to multiplex conn.SetDeadline(time.Now().Add(connReadTimeout)) // read out the http request req, err := conn.ReadRequest() if err != nil { conn.Warn("Failed to read valid %s request: %v", proto, err) conn.Write([]byte(BadRequest)) return } // read out the Host header from the request host := strings.ToLower(req.Host) conn.Debug("Found hostname %s in request", host) // multiplex to find the right backend host tunnel := tunnelRegistry.Get(fmt.Sprintf("%s://%s", proto, host)) if tunnel == nil { conn.Info("No tunnel found for hostname %s", host) conn.Write([]byte(fmt.Sprintf(NotFound, len(host)+18, host))) return } // If the client specified http auth and it doesn't match this request's auth // then fail the request with 401 Not Authorized and request the client reissue the // request with basic authdeny the request if tunnel.req.HttpAuth != "" && req.Header.Get("Authorization") != tunnel.req.HttpAuth { conn.Info("Authentication failed: %s", req.Header.Get("Authorization")) conn.Write([]byte(NotAuthorized)) return } // dead connections will now be handled by tunnel heartbeating and the client conn.SetDeadline(time.Time{}) // let the tunnel handle the connection now tunnel.HandlePublicConnection(conn) }
/** * Listens for new public tcp connections from the internet. */ func (t *Tunnel) listenTcp(listener *net.TCPListener) { for { defer func() { if r := recover(); r != nil { log.Warn("listenTcp failed with error %v", r) } }() // accept public connections tcpConn, err := listener.AcceptTCP() if err != nil { // not an error, we're shutting down this tunnel if atomic.LoadInt32(&t.closing) == 1 { return } t.Error("Failed to accept new TCP connection: %v", err) continue } conn := conn.Wrap(tcpConn, "pub") conn.AddLogPrefix(t.Id()) conn.Info("New connection from %v", conn.RemoteAddr()) go t.HandlePublicConnection(conn) } }
/** * Handles a new http connection from the public internet */ func httpHandler(tcpConn net.Conn) { // wrap up the connection for logging conn := conn.NewHttp(tcpConn, "pub") defer conn.Close() defer func() { // recover from failures if r := recover(); r != nil { conn.Warn("httpHandler failed with error %v", r) } }() // read out the http request req, err := conn.ReadRequest() if err != nil { conn.Warn("Failed to read valid http request: %v", err) conn.Write([]byte(BadRequest)) return } // read out the Host header from the request host := strings.ToLower(req.Host) conn.Debug("Found hostname %s in request", host) // multiplex to find the right backend host tunnel := tunnels.Get("http://" + host) if tunnel == nil { conn.Info("No tunnel found for hostname %s", host) conn.Write([]byte(fmt.Sprintf(NotFound, len(host)+18, host))) return } // If the client specified http auth and it doesn't match this request's auth // then fail the request with 401 Not Authorized and request the client reissue the // request with basic authdeny the request if tunnel.regMsg.HttpAuth != "" && req.Header.Get("Authorization") != tunnel.regMsg.HttpAuth { conn.Info("Authentication failed: %s", req.Header.Get("Authorization")) conn.Write([]byte(NotAuthorized)) return } tunnel.HandlePublicConnection(conn) }
/** * Handles a new http connection from the public internet */ func httpHandler(tcpConn net.Conn) { // wrap up the connection for logging conn := conn.NewHttp(tcpConn, "pub") defer conn.Close() defer func() { // recover from failures if r := recover(); r != nil { conn.Warn("httpHandler failed with error %v", r) } }() // read out the http request req, err := conn.ReadRequest() if err != nil { conn.Warn("Failed to read valid http request: %v", err) conn.Write([]byte(BadRequest)) return } // multiplex to find the right backend host conn.Debug("Found hostname %s in request", req.Host) tunnel := tunnels.Get("http://" + req.Host) if tunnel == nil { conn.Info("No tunnel found for hostname %s", req.Host) conn.Write([]byte(fmt.Sprintf(NotFound, len(req.Host)+18, req.Host))) return } // satisfy auth, if necessary conn.Debug("From client: %s", req.Header.Get("Authorization")) conn.Debug("To match: %s", tunnel.regMsg.HttpAuth) if req.Header.Get("Authorization") != tunnel.regMsg.HttpAuth { conn.Info("Authentication failed") conn.Write([]byte(NotAuthorized)) return } tunnel.HandlePublicConnection(conn) }
/** * Listens for new public tcp connections from the internet. */ func (t *Tunnel) listenTcp(listener *net.TCPListener) { for { defer func() { if r := recover(); r != nil { log.Warn("listenTcp failed with error %v", r) } }() // accept public connections tcpConn, err := listener.AcceptTCP() if err != nil { panic(err) } conn := conn.Wrap(tcpConn, "pub") conn.AddLogPrefix(t.Id()) conn.Info("New connection from %v", conn.RemoteAddr()) go t.HandlePublicConnection(conn) } }
/** * Listens for new proxy connections from tunnel clients */ func proxyListener(addr *net.TCPAddr, domain string) { listener, err := conn.Listen(addr, "pxy", tls.Config) if err != nil { panic(err) } // set global proxy addr variable proxyAddr = fmt.Sprintf("%s:%d", domain, listener.Port) log.Info("Listening for proxy connection on %d", listener.Port) for proxyConn := range listener.Conns { go func(conn conn.Conn) { // fail gracefully if the proxy connection dies defer func() { if r := recover(); r != nil { conn.Warn("Failed with error: %v", r) conn.Close() } }() // read the proxy register message var regPxy msg.RegProxyMsg if err = msg.ReadMsgInto(conn, ®Pxy); err != nil { panic(err) } // look up the tunnel for this proxy conn.Info("Registering new proxy for %s", regPxy.Url) tunnel := tunnels.Get(regPxy.Url) if tunnel == nil { panic("No tunnel found for: " + regPxy.Url) } // register the proxy connection with the tunnel tunnel.RegisterProxy(conn) }(proxyConn) } }
/** * Establishes and manages a tunnel control connection with the server */ func control(s *State, ctl *ui.Controller) { defer func() { if r := recover(); r != nil { log.Error("control recovering from failure %v", r) } }() // establish control channel conn, err := conn.Dial(s.opts.server, "ctl", tlsConfig) if err != nil { panic(err) } defer conn.Close() // register with the server err = msg.WriteMsg(conn, &msg.RegMsg{ Protocol: s.opts.protocol, OS: runtime.GOOS, HttpAuth: s.opts.httpAuth, Hostname: s.opts.hostname, Subdomain: s.opts.subdomain, ClientId: s.id, Version: version.Proto, MmVersion: version.MajorMinor(), User: s.opts.authtoken, }) if err != nil { panic(err) } // wait for the server to ack our register var regAck msg.RegAckMsg if err = msg.ReadMsgInto(conn, ®Ack); err != nil { panic(err) } if regAck.Error != "" { emsg := fmt.Sprintf("Server failed to allocate tunnel: %s", regAck.Error) ctl.Cmds <- ui.CmdQuit{Message: emsg} return } // update UI state s.publicUrl = regAck.Url conn.Info("Tunnel established at %v", s.GetPublicUrl()) s.status = "online" s.serverVersion = regAck.MmVersion ctl.Update(s) SaveAuthToken(s.opts.authtoken) // start the heartbeat lastPong := time.Now().UnixNano() go heartbeat(&lastPong, conn) // main control loop for { var m msg.Message if m, err = msg.ReadMsg(conn); err != nil { panic(err) } switch m.(type) { case *msg.ReqProxyMsg: go proxy(regAck.ProxyAddr, s, ctl) case *msg.PongMsg: atomic.StoreInt64(&lastPong, time.Now().UnixNano()) } } }