// Register a new tunnel on this control connection func (c *Control) registerTunnel(rawTunnelReq *msg.ReqTunnel) { for _, proto := range strings.Split(rawTunnelReq.Protocol, "+") { tunnelReq := *rawTunnelReq tunnelReq.Protocol = proto c.conn.Debug("Registering new tunnel") t, err := NewTunnel(&tunnelReq, c) if err != nil { c.out <- &msg.NewTunnel{Error: err.Error()} if len(c.tunnels) == 0 { c.shutdown.Begin() } // we're done return } // add it to the list of tunnels c.tunnels = append(c.tunnels, t) // acknowledge success c.out <- &msg.NewTunnel{ Url: t.url, Protocol: proto, ReqId: rawTunnelReq.ReqId, } rawTunnelReq.Hostname = strings.Replace(t.url, proto+"://", "", 1) } }
// Register a new tunnel on this control connection func (c *Control) registerTunnel(rawTunnelReq *msg.ReqTunnel) { for _, proto := range strings.Split(rawTunnelReq.Protocol, "+") { tunnelReq := *rawTunnelReq tunnelReq.Protocol = proto c.conn.Debug("Registering new tunnel") t, err := NewTunnel(&tunnelReq, c) if err != nil { ack := &msg.NewTunnel{Error: err.Error()} if len(c.tunnels) == 0 { // you can't fail your first tunnel registration // terminate the control connection c.stop <- ack } else { // inform client of failure c.out <- ack } // we're done return } // add it to the list of tunnels c.tunnels = append(c.tunnels, t) // acknowledge success c.out <- &msg.NewTunnel{ Url: t.url, Protocol: proto, ReqId: rawTunnelReq.ReqId, } rawTunnelReq.Hostname = strings.Replace(t.url, proto+"://", "", 1) } }
// Create a new tunnel from a registration message received // on a control channel func NewTunnel(m *msg.ReqTunnel, ctl *Control) (t *Tunnel, err error) { t = &Tunnel{ req: m, start: time.Now(), ctl: ctl, Logger: log.NewPrefixLogger(), } proto := t.req.Protocol switch proto { case "tcp": bindTcp := func(port int) error { if t.listener, err = net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: port}); err != nil { err = t.ctl.conn.Error("Error binding TCP listener: %v", err) return err } // create the url addr := t.listener.Addr().(*net.TCPAddr) t.url = fmt.Sprintf("tcp://%s:%d", opts.domain, addr.Port) // register it if err = tunnelRegistry.RegisterAndCache(t.url, t); err != nil { // This should never be possible because the OS will // only assign available ports to us. t.listener.Close() err = fmt.Errorf("TCP listener bound, but failed to register %s", t.url) return err } go t.listenTcp(t.listener) return nil } // use the custom remote port you asked for if t.req.RemotePort != 0 { bindTcp(int(t.req.RemotePort)) return } // try to return to you the same port you had before cachedUrl := tunnelRegistry.GetCachedRegistration(t) if cachedUrl != "" { var port int parts := strings.Split(cachedUrl, ":") portPart := parts[len(parts)-1] port, err = strconv.Atoi(portPart) if err != nil { t.ctl.conn.Error("Failed to parse cached url port as integer: %s", portPart) } else { // we have a valid, cached port, let's try to bind with it if bindTcp(port) != nil { t.ctl.conn.Warn("Failed to get custom port %d: %v, trying a random one", port, err) } else { // success, we're done return } } } // Bind for TCP connections bindTcp(0) return case "http", "https": l, ok := listeners[proto] if !ok { err = fmt.Errorf("Not listeneing for %s connections", proto) return } if err = registerVhost(t, proto, l.Addr.(*net.TCPAddr).Port); err != nil { return } default: err = fmt.Errorf("Protocol %s is not supported", proto) return } // pre-encode the http basic auth for fast comparisons later if m.HttpAuth != "" { m.HttpAuth = "Basic " + base64.StdEncoding.EncodeToString([]byte(m.HttpAuth)) } t.AddLogPrefix(t.Id()) t.Info("Registered new tunnel on: %s", t.ctl.conn.Id()) metrics.OpenTunnel(t) return }