func newTunnel(m *msg.RegMsg, ctl *Control) (t *Tunnel) { t = &Tunnel{ regMsg: m, start: time.Now(), ctl: ctl, proxies: make(chan conn.Conn), Logger: log.NewPrefixLogger(), } switch t.regMsg.Protocol { case "tcp": var err error t.listener, err = net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: 0}) if err != nil { t.ctl.conn.Error("Failed to create tunnel. Error binding TCP listener: %v", err) t.ctl.stop <- &msg.RegAckMsg{Error: "Internal server error"} } go t.listenTcp(t.listener) default: } if err := tunnels.Add(t); err != nil { t.ctl.stop <- &msg.RegAckMsg{Error: fmt.Sprint(err)} return } if m.Version != version.Proto { t.ctl.stop <- &msg.RegAckMsg{Error: fmt.Sprintf("Incompatible versions. Server %s, client %s. Download a new version at http://ngrok.com", version.MajorMinor(), m.Version)} } // pre-encode the http basic auth for fast comparisons later if m.HttpAuth != "" { m.HttpAuth = "Basic " + base64.StdEncoding.EncodeToString([]byte(m.HttpAuth)) } t.ctl.conn.AddLogPrefix(t.Id()) t.AddLogPrefix(t.Id()) t.Info("Registered new tunnel") t.ctl.out <- &msg.RegAckMsg{ Url: t.url, ProxyAddr: fmt.Sprintf("%s", proxyAddr), Version: version.Proto, MmVersion: version.MajorMinor(), } metrics.OpenTunnel(t) return }
func newTunnel(m *msg.RegMsg, ctl *Control) (t *Tunnel) { t = &Tunnel{ regMsg: m, start: time.Now(), ctl: ctl, proxies: make(chan conn.Conn), Logger: log.NewPrefixLogger(), } failReg := func(err error) { t.ctl.stop <- &msg.RegAckMsg{Error: err.Error()} } var err error switch t.regMsg.Protocol { case "tcp": var port int = 0 // try to return to you the same port you had before cachedUrl := tunnels.GetCachedRegistration(t) if cachedUrl != "" { 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) // continue with zero port = 0 } } // Bind for TCP connections t.listener, err = net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: port}) // If we failed with a custom port, try with a random one if err != nil && port != 0 { t.ctl.conn.Warn("Failed to get custom port %d: %v, trying a random one", port, err) t.listener, err = net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: 0}) } // we tried to bind with a random port and failed (no more ports available?) if err != nil { failReg(t.ctl.conn.Error("Error binding TCP listener: %v", err)) return } // create the url addr := t.listener.Addr().(*net.TCPAddr) t.url = fmt.Sprintf("tcp://%s:%d", domain, addr.Port) // register it if err = tunnels.RegisterAndCache(t.url, t); err != nil { // This should never be possible because the OS will // only assign available ports to us. t.listener.Close() failReg(fmt.Errorf("TCP listener bound, but failed to register %s", t.url)) return } go t.listenTcp(t.listener) case "http": if strings.TrimSpace(t.regMsg.Hostname) != "" { t.url = fmt.Sprintf("http://%s", t.regMsg.Hostname) } else if strings.TrimSpace(t.regMsg.Subdomain) != "" { t.url = fmt.Sprintf("http://%s.%s", t.regMsg.Subdomain, domain) } if t.url != "" { if err := tunnels.Register(t.url, t); err != nil { failReg(err) return } } else { t.url, err = tunnels.RegisterRepeat(func() string { return fmt.Sprintf("http://%x.%s", rand.Int31(), domain) }, t) if err != nil { failReg(err) return } } } if m.Version != version.Proto { failReg(fmt.Errorf("Incompatible versions. Server %s, client %s. Download a new version at http://ngrok.com", version.MajorMinor(), m.Version)) return } // pre-encode the http basic auth for fast comparisons later if m.HttpAuth != "" { m.HttpAuth = "Basic " + base64.StdEncoding.EncodeToString([]byte(m.HttpAuth)) } t.ctl.conn.AddLogPrefix(t.Id()) t.AddLogPrefix(t.Id()) t.Info("Registered new tunnel") t.ctl.out <- &msg.RegAckMsg{ Url: t.url, ProxyAddr: fmt.Sprintf("%s", proxyAddr), Version: version.Proto, MmVersion: version.MajorMinor(), } metrics.OpenTunnel(t) return }
func newTunnel(m *msg.RegMsg, ctl *Control) (t *Tunnel) { t = &Tunnel{ regMsg: m, start: time.Now(), ctl: ctl, proxies: make(chan conn.Conn), Logger: log.NewPrefixLogger(), } failReg := func(err error) { t.ctl.stop <- &msg.RegAckMsg{Error: err.Error()} } switch t.regMsg.Protocol { case "tcp": var err error t.listener, err = net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: 0}) if err != nil { t.ctl.conn.Error("Failed to create tunnel. Error binding TCP listener: %v", err) t.ctl.stop <- &msg.RegAckMsg{Error: "Internal server error"} } addr := t.listener.Addr().(*net.TCPAddr) t.url = fmt.Sprintf("tcp://%s:%d", domain, addr.Port) if err = tunnels.Register(t.url, t); err != nil { // This should never be possible because the OS will only assign // available ports to us. t.Error("TCP listener bound, but failed to register: %s", err.Error()) t.listener.Close() failReg(err) return } go t.listenTcp(t.listener) case "http": if strings.TrimSpace(t.regMsg.Hostname) != "" { t.url = fmt.Sprintf("http://%s", t.regMsg.Hostname) } else if strings.TrimSpace(t.regMsg.Subdomain) != "" { t.url = fmt.Sprintf("http://%s.%s", t.regMsg.Subdomain, domain) } if t.url != "" { if err := tunnels.Register(t.url, t); err != nil { failReg(err) return } } else { t.url = tunnels.RegisterRepeat(func() string { return fmt.Sprintf("http://%x.%s", rand.Int31(), domain) }, t) } } if m.Version != version.Proto { failReg(fmt.Errorf("Incompatible versions. Server %s, client %s. Download a new version at http://ngrok.com", version.MajorMinor(), m.Version)) return } // pre-encode the http basic auth for fast comparisons later if m.HttpAuth != "" { m.HttpAuth = "Basic " + base64.StdEncoding.EncodeToString([]byte(m.HttpAuth)) } t.ctl.conn.AddLogPrefix(t.Id()) t.AddLogPrefix(t.Id()) t.Info("Registered new tunnel") t.ctl.out <- &msg.RegAckMsg{ Url: t.url, ProxyAddr: fmt.Sprintf("%s", proxyAddr), Version: version.Proto, MmVersion: version.MajorMinor(), } metrics.OpenTunnel(t) return }