oscar_conn.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. package toc
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "log/slog"
  8. "net"
  9. "sync"
  10. "github.com/mk6i/open-oscar-server/wire"
  11. )
  12. // errSignedOff indicates that the OSCAR server refused or ended a connection
  13. // with a signoff frame.
  14. var errSignedOff = errors.New("signed off by OSCAR server")
  15. // oscarConn is a TOC user's connection to an OSCAR service, either BOS or a
  16. // chat room.
  17. type oscarConn interface {
  18. // SendSNAC sends a SNAC to the OSCAR server.
  19. SendSNAC(frame wire.SNACFrame, body any) error
  20. // Messages returns the SNACs received from the server, replies and
  21. // server-initiated messages alike, in arrival order.
  22. Messages() <-chan wire.SNACMessage
  23. // Closed returns a channel that is closed when the connection closes.
  24. Closed() <-chan struct{}
  25. // Close closes the connection.
  26. Close()
  27. }
  28. // snacQueueLimit is how many received SNACs a connection holds for the TOC
  29. // consumer, the same as an OSCAR session's message queue.
  30. const snacQueueLimit = 1000
  31. // flapConn is an oscarConn over a FLAP connection to the OSCAR server.
  32. type flapConn struct {
  33. conn net.Conn
  34. flapc *wire.FlapClient
  35. logger *slog.Logger
  36. // msgs holds SNACs received from the server until the TOC consumer takes
  37. // them. read never waits on it, so it keeps reading from the server even
  38. // while the consumer is busy; otherwise the server's writes would block,
  39. // and with them its reads of the SNACs TOC sends.
  40. msgs chan wire.SNACMessage
  41. closed chan struct{}
  42. closeOnce sync.Once
  43. }
  44. // newFLAPConn wraps a FLAP connection that has finished signing on to an
  45. // OSCAR service and starts reading SNACs from it.
  46. func newFLAPConn(conn net.Conn, flapc *wire.FlapClient, logger *slog.Logger) *flapConn {
  47. c := &flapConn{
  48. conn: conn,
  49. flapc: flapc,
  50. logger: logger,
  51. msgs: make(chan wire.SNACMessage, snacQueueLimit),
  52. closed: make(chan struct{}),
  53. }
  54. go c.read()
  55. return c
  56. }
  57. func (c *flapConn) read() {
  58. defer c.Close()
  59. for {
  60. frame, err := c.flapc.ReceiveFLAP()
  61. if err != nil {
  62. return
  63. }
  64. switch frame.FrameType {
  65. case wire.FLAPFrameData:
  66. msg, err := decodeSNAC(frame.Payload)
  67. if err != nil {
  68. c.logger.Debug("unable to decode SNAC from OSCAR server", "err", err.Error())
  69. continue
  70. }
  71. select {
  72. case c.msgs <- msg:
  73. default:
  74. // like the OSCAR server, disconnect a client that falls too far behind
  75. c.logger.Warn("TOC client isn't keeping up with the OSCAR server, disconnecting")
  76. return
  77. }
  78. case wire.FLAPFrameSignoff:
  79. return
  80. }
  81. }
  82. }
  83. func (c *flapConn) SendSNAC(frame wire.SNACFrame, body any) error {
  84. return c.flapc.SendSNAC(frame, body)
  85. }
  86. func (c *flapConn) Messages() <-chan wire.SNACMessage {
  87. return c.msgs
  88. }
  89. func (c *flapConn) Closed() <-chan struct{} {
  90. return c.closed
  91. }
  92. func (c *flapConn) Close() {
  93. c.closeOnce.Do(func() {
  94. close(c.closed)
  95. _ = c.conn.Close()
  96. })
  97. }
  98. // clientAddrConn is the server end of an in-memory OSCAR connection. It
  99. // reports the TOC client's address as its remote address, so that the OSCAR
  100. // server sees where the user really connects from.
  101. type clientAddrConn struct {
  102. net.Conn
  103. clientAddr net.Addr
  104. }
  105. func (c clientAddrConn) RemoteAddr() net.Addr {
  106. return c.clientAddr
  107. }
  108. // dialOSCAR opens an in-memory connection to the OSCAR server on behalf of
  109. // the TOC client at clientAddr.
  110. func (s OSCARProxy) dialOSCAR(clientAddr net.Addr) net.Conn {
  111. clientEnd, serverEnd := net.Pipe()
  112. go s.OSCARServer.ServeConn(clientAddrConn{Conn: serverEnd, clientAddr: clientAddr}, s.OSCAREndpoint)
  113. return clientEnd
  114. }
  115. // authenticate signs on to the OSCAR auth service with the given signon TLVs
  116. // and returns the server's reply TLVs, which hold either a login cookie or an
  117. // error code.
  118. func (s OSCARProxy) authenticate(clientAddr net.Addr, signonTLVs []wire.TLV) (wire.TLVRestBlock, error) {
  119. conn := s.dialOSCAR(clientAddr)
  120. defer conn.Close()
  121. flapc := wire.NewFlapClient(0, conn, conn)
  122. if _, err := flapc.ReceiveSignonFrame(); err != nil {
  123. return wire.TLVRestBlock{}, fmt.Errorf("ReceiveSignonFrame: %w", err)
  124. }
  125. if err := flapc.SendSignonFrame(signonTLVs); err != nil {
  126. return wire.TLVRestBlock{}, fmt.Errorf("SendSignonFrame: %w", err)
  127. }
  128. frame, err := flapc.ReceiveFLAP()
  129. if err != nil {
  130. return wire.TLVRestBlock{}, fmt.Errorf("ReceiveFLAP: %w", err)
  131. }
  132. switch frame.FrameType {
  133. case wire.FLAPFrameSignoff:
  134. block := wire.TLVRestBlock{}
  135. if err := wire.UnmarshalBE(&block, bytes.NewReader(frame.Payload)); err != nil {
  136. return wire.TLVRestBlock{}, fmt.Errorf("UnmarshalBE: %w", err)
  137. }
  138. return block, nil
  139. case wire.FLAPFrameData:
  140. // the server answers with a BUCP login response when it rate limits
  141. // an address that recently signed on with BUCP
  142. msg, err := decodeSNAC(frame.Payload)
  143. if err != nil {
  144. return wire.TLVRestBlock{}, fmt.Errorf("decodeSNAC: %w", err)
  145. }
  146. reply, ok := msg.Body.(wire.SNAC_0x17_0x03_BUCPLoginResponse)
  147. if !ok {
  148. return wire.TLVRestBlock{}, fmt.Errorf("unexpected login reply %s",
  149. wire.SubGroupName(msg.Frame.FoodGroup, msg.Frame.SubGroup))
  150. }
  151. return reply.TLVRestBlock, nil
  152. default:
  153. return wire.TLVRestBlock{}, fmt.Errorf("unexpected login reply frame type %d", frame.FrameType)
  154. }
  155. }
  156. // connectService connects to the OSCAR service that loginCookie grants access
  157. // to, either BOS or a chat room, and waits for the server to report the
  158. // service online.
  159. func (s OSCARProxy) connectService(clientAddr net.Addr, loginCookie []byte) (*flapConn, error) {
  160. conn := s.dialOSCAR(clientAddr)
  161. flapc := wire.NewFlapClient(0, conn, conn)
  162. if err := func() error {
  163. if _, err := flapc.ReceiveSignonFrame(); err != nil {
  164. return fmt.Errorf("ReceiveSignonFrame: %w", err)
  165. }
  166. if err := flapc.SendSignonFrame([]wire.TLV{wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, loginCookie)}); err != nil {
  167. return fmt.Errorf("SendSignonFrame: %w", err)
  168. }
  169. frame, err := flapc.ReceiveFLAP()
  170. if err != nil {
  171. return fmt.Errorf("ReceiveFLAP: %w", err)
  172. }
  173. if frame.FrameType == wire.FLAPFrameSignoff {
  174. return errSignedOff
  175. }
  176. msg, err := decodeSNAC(frame.Payload)
  177. if err != nil {
  178. return fmt.Errorf("decodeSNAC: %w", err)
  179. }
  180. if _, ok := msg.Body.(wire.SNAC_0x01_0x03_OServiceHostOnline); !ok {
  181. return fmt.Errorf("expected host online, got %s",
  182. wire.SubGroupName(msg.Frame.FoodGroup, msg.Frame.SubGroup))
  183. }
  184. return nil
  185. }(); err != nil {
  186. _ = conn.Close()
  187. return nil, err
  188. }
  189. return newFLAPConn(conn, flapc, s.Logger), nil
  190. }
  191. // snacDecoders unmarshal the bodies of the SNACs a TOC connection handles,
  192. // keyed by food group and subgroup.
  193. var snacDecoders = map[[2]uint16]func(r io.Reader) (any, error){
  194. {wire.OService, wire.OServiceHostOnline}: decodeBody[wire.SNAC_0x01_0x03_OServiceHostOnline],
  195. {wire.OService, wire.OServiceServiceResponse}: decodeBody[wire.SNAC_0x01_0x05_OServiceServiceResponse],
  196. {wire.OService, wire.OServiceRateParamChange}: decodeBody[wire.SNAC_0x01_0x0A_OServiceRateParamsChange],
  197. {wire.OService, wire.OServiceUserInfoUpdate}: decodeBody[wire.SNAC_0x01_0x0F_OServiceUserInfoUpdate],
  198. {wire.OService, wire.OServiceEvilNotification}: decodeBody[wire.SNAC_0x01_0x10_OServiceEvilNotification],
  199. {wire.Locate, wire.LocateUserInfoReply}: decodeBody[wire.SNAC_0x02_0x06_LocateUserInfoReply],
  200. {wire.Buddy, wire.BuddyArrived}: decodeBody[wire.SNAC_0x03_0x0B_BuddyArrived],
  201. {wire.Buddy, wire.BuddyDeparted}: decodeBody[wire.SNAC_0x03_0x0C_BuddyDeparted],
  202. {wire.ICBM, wire.ICBMChannelMsgToClient}: decodeBody[wire.SNAC_0x04_0x07_ICBMChannelMsgToClient],
  203. {wire.ICBM, wire.ICBMEvilReply}: decodeBody[wire.SNAC_0x04_0x09_ICBMEvilReply],
  204. {wire.ICBM, wire.ICBMClientEvent}: decodeBody[wire.SNAC_0x04_0x14_ICBMClientEvent],
  205. {wire.Admin, wire.AdminInfoChangeReply}: decodeBody[wire.SNAC_0x07_0x05_AdminChangeReply],
  206. {wire.ChatNav, wire.ChatNavNavInfo}: decodeBody[wire.SNAC_0x0D_0x09_ChatNavNavInfo],
  207. {wire.Chat, wire.ChatUsersJoined}: decodeBody[wire.SNAC_0x0E_0x03_ChatUsersJoined],
  208. {wire.Chat, wire.ChatUsersLeft}: decodeBody[wire.SNAC_0x0E_0x04_ChatUsersLeft],
  209. {wire.Chat, wire.ChatChannelMsgToClient}: decodeBody[wire.SNAC_0x0E_0x06_ChatChannelMsgToClient],
  210. {wire.Feedbag, wire.FeedbagReply}: decodeBody[wire.SNAC_0x13_0x06_FeedbagReply],
  211. {wire.Feedbag, wire.FeedbagInsertItem}: decodeBody[wire.SNAC_0x13_0x08_FeedbagInsertItem],
  212. {wire.Feedbag, wire.FeedbagUpdateItem}: decodeBody[wire.SNAC_0x13_0x09_FeedbagUpdateItem],
  213. {wire.Feedbag, wire.FeedbagDeleteItem}: decodeBody[wire.SNAC_0x13_0x0A_FeedbagDeleteItem],
  214. {wire.Feedbag, wire.FeedbagStatus}: decodeBody[wire.SNAC_0x13_0x0E_FeedbagStatus],
  215. {wire.BUCP, wire.BUCPLoginResponse}: decodeBody[wire.SNAC_0x17_0x03_BUCPLoginResponse],
  216. }
  217. // snacErrorSubGroup is the subgroup of the error SNAC in every food group.
  218. const snacErrorSubGroup = 0x0001
  219. // decodeSNAC unmarshals a SNAC received from the OSCAR server. A SNAC that
  220. // TOC doesn't handle is returned with a nil body.
  221. func decodeSNAC(payload []byte) (wire.SNACMessage, error) {
  222. r := bytes.NewReader(payload)
  223. msg := wire.SNACMessage{}
  224. if err := wire.UnmarshalBE(&msg.Frame, r); err != nil {
  225. return msg, fmt.Errorf("unmarshal SNAC frame: %w", err)
  226. }
  227. decode := snacDecoders[[2]uint16{msg.Frame.FoodGroup, msg.Frame.SubGroup}]
  228. if msg.Frame.SubGroup == snacErrorSubGroup {
  229. decode = decodeBody[wire.SNACError]
  230. }
  231. if decode == nil {
  232. return msg, nil
  233. }
  234. body, err := decode(r)
  235. if err != nil {
  236. return msg, fmt.Errorf("unmarshal %s: %w",
  237. wire.SubGroupName(msg.Frame.FoodGroup, msg.Frame.SubGroup), err)
  238. }
  239. msg.Body = body
  240. return msg, nil
  241. }
  242. func decodeBody[T any](r io.Reader) (any, error) {
  243. var body T
  244. err := wire.UnmarshalBE(&body, r)
  245. return body, err
  246. }