func newConnectionSvc(cfg *config.Wrapper, myID protocol.DeviceID, mdl *model.Model, tlsCfg *tls.Config, discoverer discover.Finder, relaySvc *relay.Svc) *connectionSvc { svc := &connectionSvc{ Supervisor: suture.NewSimple("connectionSvc"), cfg: cfg, myID: myID, model: mdl, tlsCfg: tlsCfg, discoverer: discoverer, relaySvc: relaySvc, conns: make(chan model.IntermediateConnection), connType: make(map[protocol.DeviceID]model.ConnectionType), relaysEnabled: cfg.Options().RelaysEnabled, lastRelayCheck: make(map[protocol.DeviceID]time.Time), } cfg.Subscribe(svc) // There are several moving parts here; one routine per listening address // to handle incoming connections, one routine to periodically attempt // outgoing connections, one routine to the the common handling // regardless of whether the connection was incoming or outgoing. // Furthermore, a relay service which handles incoming requests to connect // via the relays. // // TODO: Clean shutdown, and/or handling config changes on the fly. We // partly do this now - new devices and addresses will be picked up, but // not new listen addresses and we don't support disconnecting devices // that are removed and so on... svc.Add(serviceFunc(svc.connect)) for _, addr := range svc.cfg.Options().ListenAddress { uri, err := url.Parse(addr) if err != nil { l.Infoln("Failed to parse listen address:", addr, err) continue } listener, ok := listeners[uri.Scheme] if !ok { l.Infoln("Unknown listen address scheme:", uri.String()) continue } if debugNet { l.Debugln("listening on", uri.String()) } svc.Add(serviceFunc(func() { listener(uri, svc.tlsCfg, svc.conns) })) } svc.Add(serviceFunc(svc.handle)) if svc.relaySvc != nil { svc.Add(serviceFunc(svc.acceptRelayConns)) } return svc }
func NewService(cfg *config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder, bepProtocolName string, tlsDefaultCommonName string, lans []*net.IPNet) *Service { service := &Service{ Supervisor: suture.New("connections.Service", suture.Spec{ Log: func(line string) { l.Infoln(line) }, }), cfg: cfg, myID: myID, model: mdl, tlsCfg: tlsCfg, discoverer: discoverer, conns: make(chan internalConn), bepProtocolName: bepProtocolName, tlsDefaultCommonName: tlsDefaultCommonName, lans: lans, limiter: newLimiter(cfg), natService: nat.NewService(myID, cfg), listenersMut: sync.NewRWMutex(), listeners: make(map[string]genericListener), listenerTokens: make(map[string]suture.ServiceToken), // A listener can fail twice, rapidly. Any more than that and it // will be put on suspension for ten minutes. Restarts and changes // due to config are done by removing and adding services, so are // not subject to these limitations. listenerSupervisor: suture.New("c.S.listenerSupervisor", suture.Spec{ Log: func(line string) { l.Infoln(line) }, FailureThreshold: 2, FailureBackoff: 600 * time.Second, }), curConMut: sync.NewMutex(), currentConnection: make(map[protocol.DeviceID]completeConn), } cfg.Subscribe(service) // There are several moving parts here; one routine per listening address // (handled in configuration changing) to handle incoming connections, // one routine to periodically attempt outgoing connections, one routine to // the the common handling regardless of whether the connection was // incoming or outgoing. service.Add(serviceFunc(service.connect)) service.Add(serviceFunc(service.handle)) service.Add(service.listenerSupervisor) raw := cfg.RawCopy() // Actually starts the listeners and NAT service service.CommitConfiguration(raw, raw) return service }
func newLimiter(cfg *config.Wrapper) *limiter { l := &limiter{ write: rate.NewLimiter(rate.Inf, limiterBurstSize), read: rate.NewLimiter(rate.Inf, limiterBurstSize), } cfg.Subscribe(l) prev := config.Configuration{Options: config.OptionsConfiguration{MaxRecvKbps: -1, MaxSendKbps: -1}} l.CommitConfiguration(prev, cfg.RawCopy()) return l }
func NewService(cfg *config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder, bepProtocolName string, tlsDefaultCommonName string, lans []*net.IPNet) *Service { service := &Service{ Supervisor: suture.NewSimple("connections.Service"), cfg: cfg, myID: myID, model: mdl, tlsCfg: tlsCfg, discoverer: discoverer, conns: make(chan IntermediateConnection), bepProtocolName: bepProtocolName, tlsDefaultCommonName: tlsDefaultCommonName, lans: lans, natService: nat.NewService(myID, cfg), listenersMut: sync.NewRWMutex(), listeners: make(map[string]genericListener), listenerTokens: make(map[string]suture.ServiceToken), curConMut: sync.NewMutex(), currentConnection: make(map[protocol.DeviceID]Connection), } cfg.Subscribe(service) // The rate variables are in KiB/s in the UI (despite the camel casing // of the name). We multiply by 1024 here to get B/s. options := service.cfg.Options() if options.MaxSendKbps > 0 { service.writeRateLimit = ratelimit.NewBucketWithRate(float64(1024*options.MaxSendKbps), int64(5*1024*options.MaxSendKbps)) } if options.MaxRecvKbps > 0 { service.readRateLimit = ratelimit.NewBucketWithRate(float64(1024*options.MaxRecvKbps), int64(5*1024*options.MaxRecvKbps)) } // There are several moving parts here; one routine per listening address // (handled in configuration changing) to handle incoming connections, // one routine to periodically attempt outgoing connections, one routine to // the the common handling regardless of whether the connection was // incoming or outgoing. service.Add(serviceFunc(service.connect)) service.Add(serviceFunc(service.handle)) raw := cfg.Raw() // Actually starts the listeners and NAT service service.CommitConfiguration(raw, raw) return service }
func newUsageReportingManager(m *model.Model, cfg *config.Wrapper) *usageReportingManager { mgr := &usageReportingManager{ model: m, } // Start UR if it's enabled. mgr.CommitConfiguration(config.Configuration{}, cfg.Raw()) // Listen to future config changes so that we can start and stop as // appropriate. cfg.Subscribe(mgr) return mgr }
func setupGUI(mainSvc *suture.Supervisor, cfg *config.Wrapper, m *model.Model, apiSub *events.BufferedSubscription, discoverer *discover.CachingMux, relaySvc *relay.Svc, errors, systemLog *logger.Recorder) { guiCfg := cfg.GUI() if !guiCfg.Enabled { return } if guiCfg.Address == "" { return } addr, err := net.ResolveTCPAddr("tcp", guiCfg.Address) if err != nil { l.Fatalf("Cannot start GUI on %q: %v", guiCfg.Address, err) } else { var hostOpen, hostShow string switch { case addr.IP == nil: hostOpen = "localhost" hostShow = "0.0.0.0" case addr.IP.IsUnspecified(): hostOpen = "localhost" hostShow = addr.IP.String() default: hostOpen = addr.IP.String() hostShow = hostOpen } var proto = "http" if guiCfg.UseTLS { proto = "https" } urlShow := fmt.Sprintf("%s://%s/", proto, net.JoinHostPort(hostShow, strconv.Itoa(addr.Port))) l.Infoln("Starting web GUI on", urlShow) api, err := newAPISvc(myID, cfg, guiAssets, m, apiSub, discoverer, relaySvc, errors, systemLog) if err != nil { l.Fatalln("Cannot start GUI:", err) } cfg.Subscribe(api) mainSvc.Add(api) if cfg.Options().StartBrowser && !noBrowser && !stRestarting { urlOpen := fmt.Sprintf("%s://%s/", proto, net.JoinHostPort(hostOpen, strconv.Itoa(addr.Port))) // Can potentially block if the utility we are invoking doesn't // fork, and just execs, hence keep it in it's own routine. go openURL(urlOpen) } } }
// NewProgressEmitter creates a new progress emitter which emits // DownloadProgress events every interval. func NewProgressEmitter(cfg *config.Wrapper) *ProgressEmitter { t := &ProgressEmitter{ stop: make(chan struct{}), registry: make(map[string]*sharedPullerState), last: make(map[string]map[string]*pullerProgress), timer: time.NewTimer(time.Millisecond), mut: sync.NewMutex(), } t.CommitConfiguration(config.Configuration{}, cfg.Raw()) cfg.Subscribe(t) return t }
// NewProgressEmitter creates a new progress emitter which emits // DownloadProgress events every interval. func NewProgressEmitter(cfg *config.Wrapper) *ProgressEmitter { t := &ProgressEmitter{ stop: make(chan struct{}), registry: make(map[string]*sharedPullerState), timer: time.NewTimer(time.Millisecond), sentDownloadStates: make(map[protocol.DeviceID]*sentDownloadState), connections: make(map[string][]protocol.Connection), mut: sync.NewMutex(), } t.CommitConfiguration(config.Configuration{}, cfg.RawCopy()) cfg.Subscribe(t) return t }
func NewBlockFinder(db *leveldb.DB, cfg *config.Wrapper) *BlockFinder { if blockFinder != nil { return blockFinder } f := &BlockFinder{ db: db, mut: sync.NewRWMutex(), } f.CommitConfiguration(config.Configuration{}, cfg.Raw()) cfg.Subscribe(f) return f }
func NewSvc(cfg *config.Wrapper, tlsCfg *tls.Config) *Svc { conns := make(chan *tls.Conn) svc := &Svc{ Supervisor: suture.New("Svc", suture.Spec{ Log: func(log string) { if debug { l.Debugln(log) } }, FailureBackoff: 5 * time.Minute, FailureDecay: float64((10 * time.Minute) / time.Second), FailureThreshold: 5, }), cfg: cfg, tlsCfg: tlsCfg, tokens: make(map[string]suture.ServiceToken), clients: make(map[string]*client.ProtocolClient), mut: sync.NewRWMutex(), invitations: make(chan protocol.SessionInvitation), conns: conns, } rcfg := cfg.Raw() svc.CommitConfiguration(rcfg, rcfg) cfg.Subscribe(svc) receiver := &invitationReceiver{ tlsCfg: tlsCfg, conns: conns, invitations: svc.invitations, stop: make(chan struct{}), } eventBc := &eventBroadcaster{ svc: svc, } svc.Add(receiver) svc.Add(eventBc) return svc }
func setupGUI(mainSvc *suture.Supervisor, cfg *config.Wrapper, m *model.Model, apiSub *events.BufferedSubscription, discoverer *discover.CachingMux, relaySvc *relay.Svc, errors, systemLog *logger.Recorder) { guiCfg := cfg.GUI() if !guiCfg.Enabled { return } api, err := newAPISvc(myID, cfg, guiAssets, m, apiSub, discoverer, relaySvc, errors, systemLog) if err != nil { l.Fatalln("Cannot start GUI:", err) } cfg.Subscribe(api) mainSvc.Add(api) if cfg.Options().StartBrowser && !noBrowser && !stRestarting { // Can potentially block if the utility we are invoking doesn't // fork, and just execs, hence keep it in it's own routine. go openURL(guiCfg.URL()) } }
func setupGUI(mainService *suture.Supervisor, cfg *config.Wrapper, m *model.Model, apiSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService *connections.Service, errors, systemLog logger.Recorder, runtimeOptions RuntimeOptions) { guiCfg := cfg.GUI() if !guiCfg.Enabled { return } if guiCfg.InsecureAdminAccess { l.Warnln("Insecure admin access is enabled.") } api := newAPIService(myID, cfg, locations[locHTTPSCertFile], locations[locHTTPSKeyFile], runtimeOptions.assetDir, m, apiSub, discoverer, connectionsService, errors, systemLog) cfg.Subscribe(api) mainService.Add(api) if cfg.Options().StartBrowser && !runtimeOptions.noBrowser && !runtimeOptions.stRestarting { // Can potentially block if the utility we are invoking doesn't // fork, and just execs, hence keep it in it's own routine. go openURL(guiCfg.URL()) } }
func NewConnectionService(cfg *config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder, upnpService *upnp.Service, relayService relay.Service, bepProtocolName string, tlsDefaultCommonName string, lans []*net.IPNet) *Service { service := &Service{ Supervisor: suture.NewSimple("connections.Service"), cfg: cfg, myID: myID, model: mdl, tlsCfg: tlsCfg, discoverer: discoverer, upnpService: upnpService, relayService: relayService, conns: make(chan model.IntermediateConnection), bepProtocolName: bepProtocolName, tlsDefaultCommonName: tlsDefaultCommonName, lans: lans, connType: make(map[protocol.DeviceID]model.ConnectionType), relaysEnabled: cfg.Options().RelaysEnabled, lastRelayCheck: make(map[protocol.DeviceID]time.Time), } cfg.Subscribe(service) // The rate variables are in KiB/s in the UI (despite the camel casing // of the name). We multiply by 1024 here to get B/s. if service.cfg.Options().MaxSendKbps > 0 { service.writeRateLimit = ratelimit.NewBucketWithRate(float64(1024*service.cfg.Options().MaxSendKbps), int64(5*1024*service.cfg.Options().MaxSendKbps)) } if service.cfg.Options().MaxRecvKbps > 0 { service.readRateLimit = ratelimit.NewBucketWithRate(float64(1024*service.cfg.Options().MaxRecvKbps), int64(5*1024*service.cfg.Options().MaxRecvKbps)) } // There are several moving parts here; one routine per listening address // to handle incoming connections, one routine to periodically attempt // outgoing connections, one routine to the the common handling // regardless of whether the connection was incoming or outgoing. // Furthermore, a relay service which handles incoming requests to connect // via the relays. // // TODO: Clean shutdown, and/or handling config changes on the fly. We // partly do this now - new devices and addresses will be picked up, but // not new listen addresses and we don't support disconnecting devices // that are removed and so on... service.Add(serviceFunc(service.connect)) for _, addr := range service.cfg.Options().ListenAddress { uri, err := url.Parse(addr) if err != nil { l.Infoln("Failed to parse listen address:", addr, err) continue } listener, ok := listeners[uri.Scheme] if !ok { l.Infoln("Unknown listen address scheme:", uri.String()) continue } l.Debugln("listening on", uri) service.Add(serviceFunc(func() { listener(uri, service.tlsCfg, service.conns) })) } service.Add(serviceFunc(service.handle)) if service.relayService != nil { service.Add(serviceFunc(service.acceptRelayConns)) } return service }