package server import ( "crypto/ed25519" "cwtch.im/cwtch/model" "cwtch.im/cwtch/server/metrics" "cwtch.im/cwtch/server/storage" "cwtch.im/tapir" "cwtch.im/tapir/applications" tor2 "cwtch.im/tapir/networks/tor" "cwtch.im/tapir/persistence" "cwtch.im/tapir/primitives" "cwtch.im/tapir/primitives/privacypass" "git.openprivacy.ca/openprivacy/connectivity" "git.openprivacy.ca/openprivacy/connectivity/tor" "git.openprivacy.ca/openprivacy/log" "os" "time" ) // Server encapsulates a complete, compliant Cwtch server. type Server struct { service tapir.Service config Config metricsPack metrics.Monitors closed bool tokenTapirService tapir.Service tokenServer *privacypass.TokenServer tokenService primitives.Identity tokenServicePrivKey ed25519.PrivateKey } // Setup initialized a server from a given configuration func (s *Server) Setup(serverConfig Config) { s.config = serverConfig bs := new(persistence.BoltPersistence) bs.Open("./tokens.db") s.tokenServer = privacypass.NewTokenServerFromStore(bs) s.tokenService = s.config.TokenServiceIdentity() s.tokenServicePrivKey = s.config.TokenServerPrivateKey } // Run starts a server with the given privateKey // TODO: surface errors // TODO: handle HUP/KILL signals to exit and close Tor gracefully // TODO: handle user input to exit func (s *Server) Run(acn connectivity.ACN) { s.closed = false addressIdentity := tor.GetTorV3Hostname(s.config.PublicKey) //tokenService := privacypass.NewTokenServer() identity := primitives.InitializeIdentity("", &s.config.PrivateKey, &s.config.PublicKey) var service tapir.Service service = new(tor2.BaseOnionService) service.Init(acn, s.config.PrivateKey, &identity) s.service = service log.Infof("cwtch server running on cwtch:%s\n", addressIdentity+".onion:") s.metricsPack.Start(service, s.config.ConfigDir, s.config.ServerReporting.LogMetricsToFile) ms := new(storage.MessageStore) err := ms.Init(s.config.ConfigDir, s.config.MaxBufferLines, s.metricsPack.MessageCounter) if err != nil { log.Errorln(err) acn.Close() os.Exit(1) } go func() { s.tokenTapirService = new(tor2.BaseOnionService) s.tokenTapirService.Init(acn, s.tokenServicePrivKey, &s.tokenService) tokenApplication := new(applications.TokenApplication) tokenApplication.TokenService = s.tokenServer powTokenApp := new(applications.ApplicationChain). ChainApplication(new(applications.ProofOfWorkApplication), applications.SuccessfulProofOfWorkCapability). ChainApplication(tokenApplication, applications.HasTokensCapability) s.tokenTapirService.Listen(powTokenApp) }() for true { s.service.Listen(NewTokenBoardServer(ms, s.tokenServer)) if s.closed { return } time.Sleep(5 * time.Second) } } // KeyBundle provides the signed keybundle of the server func (s *Server) KeyBundle() *model.KeyBundle { kb := model.NewKeyBundle() identity := s.config.Identity() kb.Keys[model.KeyTypeServerOnion] = model.Key(identity.Hostname()) kb.Keys[model.KeyTypeTokenOnion] = model.Key(s.tokenService.Hostname()) kb.Keys[model.KeyTypePrivacyPass] = model.Key(s.tokenServer.Y.String()) kb.Sign(identity) return kb } // Shutdown kills the app closing all connections and freeing all goroutines func (s *Server) Shutdown() { s.closed = true s.service.Shutdown() s.tokenTapirService.Shutdown() s.metricsPack.Stop() }