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.

169 lines
4.8KB

  1. package connections
  2. import (
  3. "crypto/rand"
  4. "cwtch.im/cwtch/event"
  5. "cwtch.im/cwtch/protocol"
  6. "cwtch.im/cwtch/protocol/connections/fetch"
  7. "cwtch.im/cwtch/protocol/connections/listen"
  8. "cwtch.im/cwtch/protocol/connections/send"
  9. "errors"
  10. "git.openprivacy.ca/openprivacy/libricochet-go"
  11. "git.openprivacy.ca/openprivacy/libricochet-go/channels"
  12. "git.openprivacy.ca/openprivacy/libricochet-go/connection"
  13. "git.openprivacy.ca/openprivacy/libricochet-go/identity"
  14. "git.openprivacy.ca/openprivacy/log"
  15. "golang.org/x/crypto/ed25519"
  16. "sync"
  17. "time"
  18. )
  19. // PeerServerConnection encapsulates a single Peer->Server connection
  20. type PeerServerConnection struct {
  21. connection.AutoConnectionHandler
  22. Server string
  23. stateMutex sync.Mutex
  24. state ConnectionState
  25. connection *connection.Connection
  26. protocolEngine Engine
  27. GroupMessageHandler func(string, *protocol.GroupMessage)
  28. }
  29. // NewPeerServerConnection creates a new Peer->Server outbound connection
  30. func NewPeerServerConnection(engine Engine, serverhostname string) *PeerServerConnection {
  31. psc := new(PeerServerConnection)
  32. psc.protocolEngine = engine
  33. psc.Server = serverhostname
  34. psc.setState(DISCONNECTED)
  35. psc.Init()
  36. return psc
  37. }
  38. // GetState returns the current connection state
  39. func (psc *PeerServerConnection) GetState() ConnectionState {
  40. psc.stateMutex.Lock()
  41. defer psc.stateMutex.Unlock()
  42. return psc.state
  43. }
  44. func (psc *PeerServerConnection) setState(state ConnectionState) {
  45. log.Debugf("Setting State to %v for %v\n", ConnectionStateName[state], psc.Server)
  46. psc.stateMutex.Lock()
  47. defer psc.stateMutex.Unlock()
  48. psc.state = state
  49. psc.protocolEngine.EventManager().Publish(event.NewEvent(event.ServerStateChange, map[event.Field]string{
  50. event.GroupServer: string(psc.Server),
  51. event.ConnectionState: ConnectionStateName[state],
  52. }))
  53. }
  54. // WaitTilSynced waits until the underlying connection is authenticated
  55. func (psc *PeerServerConnection) WaitTilSynced() {
  56. for {
  57. if psc.GetState() == SYNCED {
  58. break
  59. }
  60. time.Sleep(time.Second * 1)
  61. }
  62. }
  63. // Run manages the setup and teardown of a peer server connection
  64. func (psc *PeerServerConnection) Run() error {
  65. log.Infof("Connecting to %v", psc.Server)
  66. psc.setState(CONNECTING)
  67. rc, err := goricochet.Open(psc.protocolEngine.ACN(), psc.Server)
  68. if err == nil {
  69. psc.connection = rc
  70. if psc.GetState() == KILLED {
  71. return nil
  72. }
  73. psc.setState(CONNECTED)
  74. pub, priv, err := ed25519.GenerateKey(rand.Reader)
  75. if err == nil {
  76. _, err := connection.HandleOutboundConnection(psc.connection).ProcessAuthAsV3Client(identity.InitializeV3("cwtchpeer", &priv, &pub))
  77. if err == nil {
  78. if psc.GetState() == KILLED {
  79. return nil
  80. }
  81. psc.setState(AUTHENTICATED)
  82. go func() {
  83. psc.connection.Do(func() error {
  84. psc.connection.RequestOpenChannel("im.cwtch.server.fetch", &fetch.CwtchPeerFetchChannel{Handler: psc})
  85. return nil
  86. })
  87. psc.connection.Do(func() error {
  88. psc.connection.RequestOpenChannel("im.cwtch.server.listen", &listen.CwtchPeerListenChannel{Handler: psc})
  89. return nil
  90. })
  91. }()
  92. psc.connection.Process(psc)
  93. }
  94. }
  95. }
  96. psc.setState(FAILED)
  97. return err
  98. }
  99. // Break makes Run() return and prevents processing, but doesn't close the connection.
  100. func (psc *PeerServerConnection) Break() error {
  101. return psc.connection.Break()
  102. }
  103. // SendGroupMessage sends the given protocol message to the Server.
  104. func (psc *PeerServerConnection) SendGroupMessage(gm *protocol.GroupMessage) error {
  105. if psc.state != SYNCED {
  106. return errors.New("peer is not yet connected & authenticated & synced to server cannot send message")
  107. }
  108. err := psc.connection.Do(func() error {
  109. psc.connection.RequestOpenChannel("im.cwtch.server.send", &send.CwtchPeerSendChannel{})
  110. return nil
  111. })
  112. errCount := 0
  113. for errCount < 5 {
  114. time.Sleep(time.Second * time.Duration(errCount+1)) // back off retry
  115. err = psc.connection.Do(func() error {
  116. channel := psc.connection.Channel("im.cwtch.server.send", channels.Outbound)
  117. if channel == nil {
  118. return errors.New("no channel found")
  119. }
  120. sendchannel, ok := channel.Handler.(*send.CwtchPeerSendChannel)
  121. if ok {
  122. return sendchannel.SendGroupMessage(gm)
  123. }
  124. return errors.New("channel is not a peer send channel (this should definitely not happen)")
  125. })
  126. if err != nil {
  127. errCount++
  128. } else {
  129. return nil
  130. }
  131. }
  132. return err
  133. }
  134. // Close shuts down the connection (freeing the handler goroutines)
  135. func (psc *PeerServerConnection) Close() {
  136. psc.setState(KILLED)
  137. if psc.connection != nil {
  138. psc.connection.Close()
  139. }
  140. }
  141. // HandleGroupMessage passes the given group message back to the profile.
  142. func (psc *PeerServerConnection) HandleGroupMessage(gm *protocol.GroupMessage) {
  143. psc.GroupMessageHandler(psc.Server, gm)
  144. }
  145. // HandleFetchDone calls the supplied callback for when a fetch connection is closed
  146. func (psc *PeerServerConnection) HandleFetchDone() {
  147. psc.setState(SYNCED)
  148. }