Official cwtch.im peer and server implementations. https://cwtch.im
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

152 lines
5.0 KiB

  1. package server
  2. import (
  3. "crypto/ed25519"
  4. "cwtch.im/cwtch/model"
  5. "cwtch.im/cwtch/server/metrics"
  6. "cwtch.im/cwtch/server/storage"
  7. "cwtch.im/tapir"
  8. "cwtch.im/tapir/applications"
  9. tor2 "cwtch.im/tapir/networks/tor"
  10. "cwtch.im/tapir/persistence"
  11. "cwtch.im/tapir/primitives"
  12. "cwtch.im/tapir/primitives/privacypass"
  13. "fmt"
  14. "git.openprivacy.ca/openprivacy/connectivity"
  15. "git.openprivacy.ca/openprivacy/connectivity/tor"
  16. "git.openprivacy.ca/openprivacy/log"
  17. "path"
  18. "sync"
  19. )
  20. // Server encapsulates a complete, compliant Cwtch server.
  21. type Server struct {
  22. service tapir.Service
  23. config Config
  24. metricsPack metrics.Monitors
  25. tokenTapirService tapir.Service
  26. tokenServer *privacypass.TokenServer
  27. tokenService primitives.Identity
  28. tokenServicePrivKey ed25519.PrivateKey
  29. tokenServiceStopped bool
  30. onionServiceStopped bool
  31. running bool
  32. existingMessageCount int
  33. lock sync.RWMutex
  34. }
  35. // Setup initialized a server from a given configuration
  36. func (s *Server) Setup(serverConfig Config) {
  37. s.config = serverConfig
  38. bs := new(persistence.BoltPersistence)
  39. bs.Open(path.Join(serverConfig.ConfigDir, "tokens.db"))
  40. s.tokenServer = privacypass.NewTokenServerFromStore(bs)
  41. s.tokenService = s.config.TokenServiceIdentity()
  42. s.tokenServicePrivKey = s.config.TokenServerPrivateKey
  43. }
  44. // Identity returns the main onion identity of the server
  45. func (s *Server) Identity() primitives.Identity {
  46. return s.config.Identity()
  47. }
  48. // Run starts a server with the given privateKey
  49. func (s *Server) Run(acn connectivity.ACN) error {
  50. addressIdentity := tor.GetTorV3Hostname(s.config.PublicKey)
  51. identity := primitives.InitializeIdentity("", &s.config.PrivateKey, &s.config.PublicKey)
  52. var service tapir.Service
  53. service = new(tor2.BaseOnionService)
  54. service.Init(acn, s.config.PrivateKey, &identity)
  55. s.service = service
  56. log.Infof("cwtch server running on cwtch:%s\n", addressIdentity+".onion:")
  57. s.metricsPack.Start(service, s.config.ConfigDir, s.config.ServerReporting.LogMetricsToFile)
  58. ms := new(storage.MessageStore)
  59. err := ms.Init(s.config.ConfigDir, s.config.MaxBufferLines, s.metricsPack.MessageCounter)
  60. if err != nil {
  61. return err
  62. }
  63. // Needed because we only collect metrics on a per-session basis
  64. // TODO fix metrics so they persist across sessions?
  65. s.existingMessageCount = len(ms.FetchMessages())
  66. s.tokenTapirService = new(tor2.BaseOnionService)
  67. s.tokenTapirService.Init(acn, s.tokenServicePrivKey, &s.tokenService)
  68. tokenApplication := new(applications.TokenApplication)
  69. tokenApplication.TokenService = s.tokenServer
  70. powTokenApp := new(applications.ApplicationChain).
  71. ChainApplication(new(applications.ProofOfWorkApplication), applications.SuccessfulProofOfWorkCapability).
  72. ChainApplication(tokenApplication, applications.HasTokensCapability)
  73. go func() {
  74. s.tokenTapirService.Listen(powTokenApp)
  75. s.tokenServiceStopped = true
  76. }()
  77. go func() {
  78. s.service.Listen(NewTokenBoardServer(ms, s.tokenServer))
  79. s.onionServiceStopped = true
  80. }()
  81. s.lock.Lock()
  82. s.running = true
  83. s.lock.Unlock()
  84. return nil
  85. }
  86. // KeyBundle provides the signed keybundle of the server
  87. func (s *Server) KeyBundle() *model.KeyBundle {
  88. kb := model.NewKeyBundle()
  89. identity := s.config.Identity()
  90. kb.Keys[model.KeyTypeServerOnion] = model.Key(identity.Hostname())
  91. kb.Keys[model.KeyTypeTokenOnion] = model.Key(s.tokenService.Hostname())
  92. kb.Keys[model.KeyTypePrivacyPass] = model.Key(s.tokenServer.Y.String())
  93. kb.Sign(identity)
  94. return kb
  95. }
  96. // CheckStatus returns true if the server is running and/or an error if any part of the server needs to be restarted.
  97. func (s *Server) CheckStatus() (bool, error) {
  98. s.lock.RLock()
  99. defer s.lock.RUnlock()
  100. if s.onionServiceStopped == true || s.tokenServiceStopped == true {
  101. return s.running, fmt.Errorf("one of more server components are down: onion:%v token service: %v", s.onionServiceStopped, s.tokenServiceStopped)
  102. }
  103. return s.running, nil
  104. }
  105. // Shutdown kills the app closing all connections and freeing all goroutines
  106. func (s *Server) Shutdown() {
  107. s.lock.Lock()
  108. defer s.lock.Unlock()
  109. s.service.Shutdown()
  110. s.tokenTapirService.Shutdown()
  111. s.metricsPack.Stop()
  112. s.running = true
  113. }
  114. // Statistics is an encapsulation of information about the server that an operator might want to know at a glance.
  115. type Statistics struct {
  116. TotalMessages int
  117. }
  118. // GetStatistics is a stub method for providing some high level information about
  119. // the server operation to bundling applications (e.g. the UI)
  120. func (s *Server) GetStatistics() Statistics {
  121. // TODO Statistics from Metrics is very awkward. Metrics needs an overhaul to make safe
  122. total := s.existingMessageCount
  123. if s.metricsPack.TotalMessageCounter != nil {
  124. total += s.metricsPack.TotalMessageCounter.Count()
  125. }
  126. return Statistics{
  127. TotalMessages: total,
  128. }
  129. }
  130. // ConfigureAutostart sets whether this server should autostart (in the Cwtch UI/bundling application)
  131. func (s *Server) ConfigureAutostart(autostart bool) {
  132. s.config.AutoStart = autostart
  133. s.config.Save(s.config.ConfigDir, s.config.FilePath)
  134. }