Example #1
0
func autoUpdate(s mvc.State, token string) {
	up, err := update.New().VerifySignatureWithPEM([]byte(publicKey))
	if err != nil {
		log.Error("Failed to create update with signature: %v", err)
		return
	}

	update := func() (tryAgain bool) {
		log.Info("Checking for update")
		params := check.Params{
			AppId:      appId,
			AppVersion: version.MajorMinor(),
			UserId:     token,
		}

		result, err := params.CheckForUpdate(updateEndpoint, up)
		if err == check.NoUpdateAvailable {
			log.Info("No update available")
			return true
		} else if err != nil {
			log.Error("Error while checking for update: %v", err)
			return true
		}

		if result.Initiative == check.INITIATIVE_AUTO {
			if err := up.CanUpdate(); err != nil {
				log.Error("Can't update: insufficient permissions: %v", err)
				// tell the user to update manually
				s.SetUpdateStatus(mvc.UpdateAvailable)
			} else {
				applyUpdate(s, result)
			}
		} else if result.Initiative == check.INITIATIVE_MANUAL {
			// this is the way the server tells us to update manually
			log.Info("Server wants us to update manually")
			s.SetUpdateStatus(mvc.UpdateAvailable)
		} else {
			log.Info("Update available, but ignoring")
		}

		// stop trying after a single download attempt
		// XXX: improve this so the we can:
		// 1. safely update multiple times
		// 2. only retry after temporary errors
		return false
	}

	// try to update immediately and then at a set interval
	for {
		if tryAgain := update(); !tryAgain {
			break
		}

		time.Sleep(updateCheckInterval)
	}
}
Example #2
0
func NewWebView(ctl *ui.Controller, state ui.State, port int) *WebView {
	w := &WebView{}

	switch p := state.GetProtocol().(type) {
	case *proto.Http:
		NewWebHttpView(ctl, p)
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/http/in", 302)
	})

	http.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) {
		parts := strings.Split(r.URL.Path, "/")
		name := parts[len(parts)-1]
		fn, ok := static.AssetMap[name]
		if !ok {
			http.NotFound(w, r)
			return
		}
		w.Write(fn())
	})

	log.Info("Serving web interface on localhost:%d", port)
	go http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
	return w
}
Example #3
0
func init() {
	metrics.windowsCounter = gometrics.NewCounter()
	metrics.linuxCounter = gometrics.NewCounter()
	metrics.osxCounter = gometrics.NewCounter()
	metrics.otherCounter = gometrics.NewCounter()
	/*
	   metrics.bytesInCount = gometrics.NewCounter()
	   metrics.bytesOutCount = gometrics.NewCounter()
	*/

	/*
	   metrics.tunnelGauge = gometrics.NewGauge()
	   metrics.tcpTunnelGauge = gometrics.NewGauge()
	   metrics.requestGauge = gometrics.NewGauge()
	*/

	metrics.tunnelMeter = gometrics.NewMeter()
	metrics.tcpTunnelMeter = gometrics.NewMeter()
	metrics.requestMeter = gometrics.NewMeter()
	metrics.lostHeartbeatMeter = gometrics.NewMeter()

	metrics.requestTimer = gometrics.NewTimer()

	go func() {
		time.Sleep(reportInterval)
		log.Info("Server metrics: %s", MetricsJson())
	}()
}
Example #4
0
// Listen for incoming control and proxy connections
// We listen for incoming control and proxy connections on the same port
// for ease of deployment. The hope is that by running on port 443, using
// TLS and running all connections over the same port, we can bust through
// restrictive firewalls.
func tunnelListener(addr string, tlsConfig *tls.Config) {
	// listen for incoming connections
	listener, err := conn.Listen(addr, "tun", tlsConfig)
	if err != nil {
		panic(err)
	}

	log.Info("Listening for control and proxy connections on %s", listener.Addr.String())
	for c := range listener.Conns {
		go func(tunnelConn conn.Conn) {
			tunnelConn.SetReadDeadline(time.Now().Add(connReadTimeout))
			var rawMsg msg.Message
			if rawMsg, err = msg.ReadMsg(tunnelConn); err != nil {
				tunnelConn.Error("Failed to read message: %v", err)
				tunnelConn.Close()
				return
			}

			// don't timeout after the initial read, tunnel heartbeating will kill
			// dead connections
			tunnelConn.SetReadDeadline(time.Time{})

			switch m := rawMsg.(type) {
			case *msg.Auth:
				NewControl(tunnelConn, m)

			case *msg.RegProxy:
				NewProxy(tunnelConn, m)

			default:
				tunnelConn.Close()
			}
		}(c)
	}
}
Example #5
0
// Listen for incoming control and proxy connections
// We listen for incoming control and proxy connections on the same port
// for ease of deployment. The hope is that by running on port 443, using
// TLS and running all connections over the same port, we can bust through
// restrictive firewalls.
func tunnelListener(addr string, tlsConfig *tls.Config, secret string) {
	// listen for incoming connections
	listener, err := conn.Listen(addr, "tun", tlsConfig)
	if err != nil {
		panic(err)
	}

	log.Info("Listening for control and proxy connections on %s", listener.Addr.String())
	if secret != "" {
		log.Info("Clients should use '%s' as secret", secret)
	}

	for c := range listener.Conns {
		go func(tunnelConn conn.Conn) {
			// don't crash on panics
			defer func() {
				if r := recover(); r != nil {
					tunnelConn.Info("tunnelListener failed with error %v: %s", r, debug.Stack())
				}
			}()

			tunnelConn.SetReadDeadline(time.Now().Add(connReadTimeout))
			var rawMsg msg.Message
			if rawMsg, err = msg.ReadMsg(tunnelConn); err != nil {
				tunnelConn.Warn("Failed to read message: %v", err)
				tunnelConn.Close()
				return
			}

			// don't timeout after the initial read, tunnel heartbeating will kill
			// dead connections
			tunnelConn.SetReadDeadline(time.Time{})

			switch m := rawMsg.(type) {
			case *msg.Auth:
				NewControl(tunnelConn, m, secret)

			case *msg.RegProxy:
				NewProxy(tunnelConn, m)

			default:
				tunnelConn.Close()
			}
		}(c)
	}
}
Example #6
0
/**
 * Listens for new http connections from the public internet
 */
func httpListener(addr *net.TCPAddr) {
	// bind/listen for incoming connections
	listener, err := conn.Listen(addr, "pub", nil)
	if err != nil {
		panic(err)
	}

	log.Info("Listening for public http connections on %v", listener.Port)
	for conn := range listener.Conns {
		go httpHandler(conn)
	}
}
Example #7
0
/**
 * Listens for new control connections from tunnel clients
 */
func controlListener(addr *net.TCPAddr, domain string) {
	// listen for incoming connections
	listener, err := conn.Listen(addr, "ctl", tls.Config)
	if err != nil {
		panic(err)
	}

	log.Info("Listening for control connections on %d", listener.Port)
	for c := range listener.Conns {
		NewControl(c)
	}
}
Example #8
0
func NewWebView(ctl *ui.Controller, state ui.State, port int) *WebView {
	v := &WebView{
		wsMessages: util.NewBroadcast(),
	}

	switch p := state.GetProtocol().(type) {
	case *proto.Http:
		NewWebHttpView(v, ctl, p)
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/http/in", 302)
	})

	http.HandleFunc("/_ws", func(w http.ResponseWriter, r *http.Request) {
		conn, err := websocket.Upgrade(w, r.Header, nil, 1024, 1024)

		if err != nil {
			http.Error(w, "Failed websocket upgrade", 400)
			log.Warn("Failed websocket upgrade: %v", err)
			return
		}

		msgs := v.wsMessages.Reg()
		defer v.wsMessages.UnReg(msgs)
		for m := range msgs {
			err := conn.WriteMessage(websocket.OpText, m.([]byte))
			if err != nil {
				// connection is closed
				break
			}
		}
	})

	http.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) {
		parts := strings.Split(r.URL.Path, "/")
		name := parts[len(parts)-1]
		fn, ok := static.AssetMap[name]
		if !ok {
			http.NotFound(w, r)
			return
		}
		w.Write(fn())
	})

	log.Info("Serving web interface on localhost:%d", port)
	go http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
	return v
}
Example #9
0
func applyUpdate(s mvc.State, result *check.Result) {
	err, errRecover := result.Update()
	if err == nil {
		log.Info("Update ready!")
		s.SetUpdateStatus(mvc.UpdateReady)
		return
	}

	log.Error("Error while updating ngrok: %v", err)
	if errRecover != nil {
		log.Error("Error while recovering from failed ngrok update, your binary may be missing: %v", errRecover.Error())
	}

	// tell the user to update manually
	s.SetUpdateStatus(mvc.UpdateAvailable)
}
func progressWatcher(s mvc.State, progress chan int, complete chan int) {
	for {
		select {
		case pct, ok := <-progress:
			if !ok {
				close(complete)
				return
			} else if pct == 100 {
				s.SetUpdateStatus(mvc.UpdateInstalling)
				close(complete)
				return
			} else {
				if pct%25 == 0 {
					log.Info("Downloading update %d%% complete", pct)
				}
				s.SetUpdateStatus(mvc.UpdateStatus(pct))
			}
		}
	}
}
Example #11
0
File: http.go Project: 0x19/ngrok
// Listens for new http(s) connections from the public internet
func startHttpListener(addr string, tlsCfg *tls.Config) (listener *conn.Listener) {
	// bind/listen for incoming connections
	var err error
	if listener, err = conn.Listen(addr, "pub", tlsCfg); err != nil {
		panic(err)
	}

	proto := "http"
	if tlsCfg != nil {
		proto = "https"
	}

	log.Info("Listening for public %s connections on %v", proto, listener.Addr.String())
	go func() {
		for conn := range listener.Conns {
			go httpHandler(conn, proto)
		}
	}()

	return
}
Example #12
0
func reconnectingControl(s *State, ctl *ui.Controller) {
	// how long we should wait before we reconnect
	maxWait := 30 * time.Second
	wait := 1 * time.Second

	for {
		control(s, ctl)

		if s.status == "online" {
			wait = 1 * time.Second
		}

		log.Info("Waiting %d seconds before reconnecting", int(wait.Seconds()))
		time.Sleep(wait)
		// exponentially increase wait time
		wait = 2 * wait
		wait = time.Duration(math.Min(float64(wait), float64(maxWait)))
		s.status = "reconnecting"
		ctl.Update(s)
	}
}
Example #13
0
func progressWatcher(s *State, ctl *ui.Controller, progress chan int, complete chan int) {
	for {
		select {
		case pct, ok := <-progress:
			if !ok {
				close(complete)
				return
			} else if pct == 100 {
				s.update = ui.UpdateInstalling
				ctl.Update(s)
				close(complete)
				return
			} else {
				if pct%25 == 0 {
					log.Info("Downloading update %d%% complete", pct)
				}
				s.update = ui.UpdateStatus(pct)
				ctl.Update(s)
			}
		}
	}
}
Example #14
0
func (c *ClientModel) Run() {
	// how long we should wait before we reconnect
	maxWait := 30 * time.Second
	wait := 1 * time.Second

	for {
		// run the control channel
		c.control()

		// control oonly returns when a failure has occurred, so we're going to try to reconnect
		if c.connStatus == mvc.ConnOnline {
			wait = 1 * time.Second
		}

		log.Info("Waiting %d seconds before reconnecting", int(wait.Seconds()))
		time.Sleep(wait)
		// exponentially increase wait time
		wait = 2 * wait
		wait = time.Duration(math.Min(float64(wait), float64(maxWait)))
		c.connStatus = mvc.ConnReconnecting
		c.update()
	}
}
Example #15
0
/**
 * 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, &regPxy); 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)
	}
}
Example #16
0
func autoUpdate(s *State, ctl *ui.Controller, token string) {
	update := func() (updateSuccessful bool) {
		params := make(url.Values)
		params.Add("version", version.MajorMinor())
		params.Add("os", runtime.GOOS)
		params.Add("arch", runtime.GOARCH)

		download := update.NewDownload()
		downloadComplete := make(chan int)
		go func() {
			for {
				select {
				case progress, ok := <-download.Progress:
					if !ok {
						close(downloadComplete)
						return
					} else if progress == 100 {
						s.update = ui.UpdateInstalling
						ctl.Update(s)
						close(downloadComplete)
						return
					} else {
						if progress%25 == 0 {
							log.Info("Downloading update %d%% complete", progress)
						}
						s.update = ui.UpdateStatus(progress)
						ctl.Update(s)
					}
				}
			}
		}()

		log.Info("Checking for update")
		err := download.UpdateFromUrl(updateEndpoint + "?" + params.Encode())
		<-downloadComplete
		if err != nil {
			log.Error("Error while updating ngrok: %v", err)
			if download.Available {
				s.update = ui.UpdateError
			} else {
				s.update = ui.UpdateNone
			}

			// record the error to ngrok.com's servers for debugging purposes
			params.Add("error", err.Error())
			params.Add("user", token)
			resp, err := http.PostForm("https://dl.ngrok.com/update/error", params)
			if err != nil {
				log.Error("Error while reporting update error")
			}
			resp.Body.Close()
		} else {
			if download.Available {
				log.Info("Marked update ready")
				s.update = ui.UpdateReady
				updateSuccessful = true
			} else {
				log.Info("No update available at this time")
				s.update = ui.UpdateNone
			}
		}

		ctl.Update(s)
		return
	}

	// try to update immediately and then at a set interval
	update()
	for _ = range time.Tick(updateCheckInterval) {
		if update() {
			// stop trying to update if the update function is successful
			// XXX: improve this by trying to download versions newer than
			// the last one we downloaded
			return
		}
	}
}
Example #17
0
func LoadConfiguration(opts *Options) (config *Configuration, err error) {
	configPath := opts.config
	if configPath == "" {
		configPath = defaultPath()
	}

	log.Info("Reading configuration file %s", configPath)
	configBuf, err := ioutil.ReadFile(configPath)
	if err != nil {
		// failure to read a configuration file is only a fatal error if
		// the user specified one explicitly
		if opts.config != "" {
			err = fmt.Errorf("Failed to read configuration file %s: %v", configPath, err)
			return
		}
	}

	// deserialize/parse the config
	config = new(Configuration)
	if err = goyaml.Unmarshal(configBuf, &config); err != nil {
		err = fmt.Errorf("Error parsing configuration file %s: %v", configPath, err)
		return
	}

	// try to parse the old .ngrok format for backwards compatibility
	matched := false
	content := strings.TrimSpace(string(configBuf))
	if matched, err = regexp.MatchString("^[0-9a-zA-Z_\\-!]+$", content); err != nil {
		return
	} else if matched {
		config = &Configuration{AuthToken: content}
	}

	// set configuration defaults
	if config.ServerAddr == "" {
		config.ServerAddr = defaultServerAddr
	}

	if config.InspectAddr == "" {
		config.InspectAddr = "127.0.0.1:4040"
	}

	if config.HttpProxy == "" {
		config.HttpProxy = os.Getenv("http_proxy")
	}

	// validate and normalize configuration
	if config.InspectAddr != "disabled" {
		if config.InspectAddr, err = normalizeAddress(config.InspectAddr, "inspect_addr"); err != nil {
			return
		}
	}

	if config.ServerAddr, err = normalizeAddress(config.ServerAddr, "server_addr"); err != nil {
		return
	}

	if config.HttpProxy != "" {
		var proxyUrl *url.URL
		if proxyUrl, err = url.Parse(config.HttpProxy); err != nil {
			return
		} else {
			if proxyUrl.Scheme != "http" && proxyUrl.Scheme != "https" {
				err = fmt.Errorf("Proxy url scheme must be 'http' or 'https', got %v", proxyUrl.Scheme)
				return
			}
		}
	}

	for name, t := range config.Tunnels {
		if t == nil || t.Protocols == nil || len(t.Protocols) == 0 {
			err = fmt.Errorf("Tunnel %s does not specify any protocols to tunnel.", name)
			return
		}

		for k, addr := range t.Protocols {
			tunnelName := fmt.Sprintf("for tunnel %s[%s]", name, k)
			if t.Protocols[k], err = normalizeAddress(addr, tunnelName); err != nil {
				return
			}

			if err = validateProtocol(k, tunnelName); err != nil {
				return
			}
		}

		// use the name of the tunnel as the subdomain if none is specified
		if t.Hostname == "" && t.Subdomain == "" {
			// XXX: a crude heuristic, really we should be checking if the last part
			// is a TLD
			if len(strings.Split(name, ".")) > 1 {
				t.Hostname = name
			} else {
				t.Subdomain = name
			}
		}
	}

	// override configuration with command-line options
	config.LogTo = opts.logto
	config.Path = configPath
	if opts.authtoken != "" {
		config.AuthToken = opts.authtoken
	}

	switch opts.command {
	// start a single tunnel, the default, simple ngrok behavior
	case "default":
		config.Tunnels = make(map[string]*TunnelConfiguration)
		config.Tunnels["default"] = &TunnelConfiguration{
			Subdomain: opts.subdomain,
			Hostname:  opts.hostname,
			HttpAuth:  opts.httpauth,
			Protocols: make(map[string]string),
		}

		for _, proto := range strings.Split(opts.protocol, "+") {
			if err = validateProtocol(proto, "default"); err != nil {
				return
			}

			if config.Tunnels["default"].Protocols[proto], err = normalizeAddress(opts.args[0], ""); err != nil {
				return
			}
		}

	// start tunnels
	case "start":
		if len(opts.args) == 0 {
			err = fmt.Errorf("You must specify at least one tunnel to start")
			return
		}

		requestedTunnels := make(map[string]bool)
		for _, arg := range opts.args {
			requestedTunnels[arg] = true

			if _, ok := config.Tunnels[arg]; !ok {
				err = fmt.Errorf("Requested to start tunnel %s which is not defined in the config file.", arg)
				return
			}
		}

		for name, _ := range config.Tunnels {
			if !requestedTunnels[name] {
				delete(config.Tunnels, name)
			}
		}

	default:
		err = fmt.Errorf("Unknown command: %s", opts.command)
		return
	}

	return
}
Example #18
0
func NewControl(ctlConn conn.Conn, authMsg *msg.Auth) {
	var err error

	// create the object
	c := &Control{
		auth:            authMsg,
		conn:            ctlConn,
		out:             make(chan msg.Message),
		in:              make(chan msg.Message),
		proxies:         make(chan conn.Conn, 10),
		lastPing:        time.Now(),
		writerShutdown:  util.NewShutdown(),
		readerShutdown:  util.NewShutdown(),
		managerShutdown: util.NewShutdown(),
		shutdown:        util.NewShutdown(),
	}

	failAuth := func(e error) {
		_ = msg.WriteMsg(ctlConn, &msg.AuthResp{Error: e.Error()})
		ctlConn.Close()
	}
	failAuth2 := func(e string) {
		_ = msg.WriteMsg(ctlConn, e)
		ctlConn.Close()
	}

	// check auth_token
	if authMsg.User == "" {
		failAuth2("Need auth_token")
		return
	}
	val, err := client.HGet("ngrok", authMsg.User).Result()
	if err != nil {
		log.Info(err.Error())
		failAuth2("Auth ERROR:" + err.Error())
		return
	}
	c.UserName = val
	log.Info("New Client auth ok --> " + val)

	// register the clientid
	c.id = authMsg.ClientId
	if c.id == "" {
		// it's a new session, assign an ID
		if c.id, err = util.SecureRandId(16); err != nil {
			failAuth(err)
			return
		}
	}

	// set logging prefix
	ctlConn.SetType("ctl")
	ctlConn.AddLogPrefix(c.id)

	if authMsg.Version != version.Proto {
		failAuth(fmt.Errorf("Incompatible versions. Server %s, client %s. Download a new version at http://ngrok.com", version.MajorMinor(), authMsg.Version))
		return
	}

	// register the control
	if replaced := controlRegistry.Add(c.id, c); replaced != nil {
		replaced.shutdown.WaitComplete()
	}

	// start the writer first so that the following messages get sent
	go c.writer()

	// Respond to authentication
	c.out <- &msg.AuthResp{
		Version:   version.Proto,
		MmVersion: version.MajorMinor(),
		ClientId:  c.id,
	}

	// As a performance optimization, ask for a proxy connection up front
	c.out <- &msg.ReqProxy{}

	// manage the connection
	go c.manager()
	go c.reader()
	go c.stopper()
}
func autoUpdate(s mvc.State, token string) {
	tryAgain := true

	params := make(url.Values)
	params.Add("version", version.MajorMinor())
	params.Add("os", runtime.GOOS)
	params.Add("arch", runtime.GOARCH)
	params.Add("user", token)

	updateUrl := updateEndpoint + "?" + params.Encode()
	checkUrl := checkEndpoint + "?" + params.Encode()

	update := func() {
		log.Info("Checking for update")
		available, err := update.NewDownload(checkUrl).Check()
		if err != nil {
			log.Error("Error while checking for update: %v", err)
			return
		}

		if !available {
			log.Info("No update available")
			return
		}

		// stop trying after a single download attempt
		// XXX: improve this so the we can:
		// 1. safely update multiple times
		// 2. only retry after a network connection failure
		tryAgain = false

		download := update.NewDownload(updateUrl)
		downloadComplete := make(chan int)

		go progressWatcher(s, download.Progress, downloadComplete)

		log.Info("Trying to update . . .")
		err, errRecover := download.GetAndUpdate()
		<-downloadComplete

		if err != nil {
			// log error to console
			log.Error("Error while updating ngrok: %v", err)
			if errRecover != nil {
				log.Error("Error while recovering from failed ngrok update, your binary may be missing: %v", errRecover.Error())
				params.Add("errorRecover", errRecover.Error())
			}

			// log error to ngrok.com's servers for debugging purposes
			params.Add("error", err.Error())
			resp, reportErr := http.PostForm("https://dl.ngrok.com/update/error", params)
			if err != nil {
				log.Error("Error while reporting update error: %v, %v", err, reportErr)
			}
			resp.Body.Close()

			// tell the user to update manually
			s.SetUpdateStatus(mvc.UpdateAvailable)
		} else {
			if !download.Available {
				// this is the way the server tells us to update manually
				log.Info("Server wants us to update manually")
				s.SetUpdateStatus(mvc.UpdateAvailable)
			} else {
				// tell the user the update is ready
				log.Info("Update ready!")
				s.SetUpdateStatus(mvc.UpdateReady)
			}
		}

		return
	}

	// try to update immediately and then at a set interval
	update()
	for _ = range time.Tick(updateCheckInterval) {
		if !tryAgain {
			break
		}
		update()
	}
}