connection.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. package oscar
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "log/slog"
  9. "github.com/mk6i/retro-aim-server/config"
  10. "github.com/mk6i/retro-aim-server/server/oscar/middleware"
  11. "github.com/mk6i/retro-aim-server/state"
  12. "github.com/mk6i/retro-aim-server/wire"
  13. )
  14. type incomingMessage struct {
  15. flap wire.FLAPFrame
  16. payload *bytes.Buffer
  17. }
  18. type flapClient struct {
  19. sequence uint32
  20. w io.Writer
  21. r io.Reader
  22. }
  23. func (f *flapClient) SignonHandshake() (wire.FLAPSignonFrame, error) {
  24. // send FLAPFrameSignon to client
  25. flap := wire.FLAPFrame{
  26. StartMarker: 42,
  27. FrameType: wire.FLAPFrameSignon,
  28. Sequence: uint16(f.sequence),
  29. PayloadLength: 4, // size of FLAPSignonFrame
  30. }
  31. if err := wire.Marshal(flap, f.w); err != nil {
  32. return wire.FLAPSignonFrame{}, err
  33. }
  34. flapSignonFrameOut := wire.FLAPSignonFrame{
  35. FLAPVersion: 1,
  36. }
  37. if err := wire.Marshal(flapSignonFrameOut, f.w); err != nil {
  38. return wire.FLAPSignonFrame{}, err
  39. }
  40. // receive FLAPFrameSignon from client
  41. flap = wire.FLAPFrame{}
  42. if err := wire.Unmarshal(&flap, f.r); err != nil {
  43. return wire.FLAPSignonFrame{}, err
  44. }
  45. buf, err := flap.ReadBody(f.r)
  46. if err != nil {
  47. return wire.FLAPSignonFrame{}, err
  48. }
  49. flapSignonFrameIn := wire.FLAPSignonFrame{}
  50. if err := wire.Unmarshal(&flapSignonFrameIn, buf); err != nil {
  51. return wire.FLAPSignonFrame{}, err
  52. }
  53. f.sequence++
  54. return flapSignonFrameIn, nil
  55. }
  56. // SendSignoffFrame sends a sign-off FLAP frame with attached TLVs as the last
  57. // request sent in the FLAP auth flow. This is unrelated to the Disconnect()
  58. // method, which sends a sign-off frame to terminate a BOS connection.
  59. // todo: combine this method with Disconnect()
  60. func (f *flapClient) SendSignoffFrame(tlvs wire.TLVRestBlock) error {
  61. tlvBuf := &bytes.Buffer{}
  62. if err := wire.Marshal(tlvs, tlvBuf); err != nil {
  63. return err
  64. }
  65. flap := wire.FLAPFrame{
  66. StartMarker: 42,
  67. FrameType: wire.FLAPFrameSignoff,
  68. Sequence: uint16(f.sequence),
  69. PayloadLength: uint16(tlvBuf.Len()),
  70. }
  71. if err := wire.Marshal(flap, f.w); err != nil {
  72. return err
  73. }
  74. expectLen := tlvBuf.Len()
  75. c, err := f.w.Write(tlvBuf.Bytes())
  76. if err != nil {
  77. return err
  78. }
  79. if c != expectLen {
  80. panic("did not write the expected # of bytes")
  81. }
  82. f.sequence++
  83. return nil
  84. }
  85. func (f *flapClient) SendSNAC(frame wire.SNACFrame, body any) error {
  86. snacBuf := &bytes.Buffer{}
  87. if err := wire.Marshal(frame, snacBuf); err != nil {
  88. return err
  89. }
  90. if err := wire.Marshal(body, snacBuf); err != nil {
  91. return err
  92. }
  93. flap := wire.FLAPFrame{
  94. StartMarker: 42,
  95. FrameType: wire.FLAPFrameData,
  96. Sequence: uint16(f.sequence),
  97. PayloadLength: uint16(snacBuf.Len()),
  98. }
  99. if err := wire.Marshal(flap, f.w); err != nil {
  100. return err
  101. }
  102. expectLen := snacBuf.Len()
  103. c, err := f.w.Write(snacBuf.Bytes())
  104. if err != nil {
  105. return err
  106. }
  107. if c != expectLen {
  108. panic("did not write the expected # of bytes")
  109. }
  110. f.sequence++
  111. return nil
  112. }
  113. func (f *flapClient) ReceiveSNAC(frame *wire.SNACFrame, body any) error {
  114. flap := wire.FLAPFrame{}
  115. if err := wire.Unmarshal(&flap, f.r); err != nil {
  116. return err
  117. }
  118. buf, err := flap.ReadBody(f.r)
  119. if err != nil {
  120. return err
  121. }
  122. if err := wire.Unmarshal(frame, buf); err != nil {
  123. return err
  124. }
  125. return wire.Unmarshal(body, buf)
  126. }
  127. func (f *flapClient) Disconnect() error {
  128. // gracefully disconnect so that the client does not try to
  129. // reconnect when the connection closes.
  130. flap := wire.FLAPFrame{
  131. StartMarker: 42,
  132. FrameType: wire.FLAPFrameSignoff,
  133. Sequence: uint16(f.sequence),
  134. PayloadLength: uint16(0),
  135. }
  136. return wire.Marshal(flap, f.w)
  137. }
  138. func sendInvalidSNACErr(frameIn wire.SNACFrame, rw ResponseWriter) error {
  139. frameOut := wire.SNACFrame{
  140. FoodGroup: frameIn.FoodGroup,
  141. SubGroup: 0x01, // error subgroup for all SNACs
  142. RequestID: frameIn.RequestID,
  143. }
  144. bodyOut := wire.SNACError{
  145. Code: wire.ErrorCodeInvalidSnac,
  146. }
  147. return rw.SendSNAC(frameOut, bodyOut)
  148. }
  149. func consumeFLAPFrames(r io.Reader, msgCh chan incomingMessage, errCh chan error) {
  150. defer close(msgCh)
  151. defer close(errCh)
  152. for {
  153. in := incomingMessage{}
  154. if err := wire.Unmarshal(&in.flap, r); err != nil {
  155. errCh <- err
  156. return
  157. }
  158. if in.flap.PayloadLength > 0 {
  159. buf := make([]byte, in.flap.PayloadLength)
  160. if _, err := io.ReadFull(r, buf); err != nil {
  161. errCh <- err
  162. return
  163. }
  164. in.payload = bytes.NewBuffer(buf)
  165. }
  166. msgCh <- in
  167. }
  168. }
  169. // dispatchIncomingMessages receives incoming messages and sends them to the
  170. // appropriate message handler. Messages from the client are sent to the
  171. // router. Messages relayed from the user session are forwarded to the client.
  172. // This function ensures that the same sequence number is incremented for both
  173. // types of messages. The function terminates upon receiving a connection error
  174. // or when the session closes.
  175. //
  176. // todo: this method has too many params and should be folded into a new type
  177. func dispatchIncomingMessages(ctx context.Context, sess *state.Session, flapc *flapClient, r io.Reader, logger *slog.Logger, router Handler, config config.Config) error {
  178. // buffered so that the go routine has room to exit
  179. msgCh := make(chan incomingMessage, 1)
  180. readErrCh := make(chan error, 1)
  181. go consumeFLAPFrames(r, msgCh, readErrCh)
  182. defer func() {
  183. logger.InfoContext(ctx, "user disconnected")
  184. }()
  185. for {
  186. select {
  187. case m, ok := <-msgCh:
  188. if !ok {
  189. return nil
  190. }
  191. switch m.flap.FrameType {
  192. case wire.FLAPFrameData:
  193. inFrame := wire.SNACFrame{}
  194. if err := wire.Unmarshal(&inFrame, m.payload); err != nil {
  195. return err
  196. }
  197. // route a client request to the appropriate service handler. the
  198. // handler may write a response to the client connection.
  199. if err := router.Handle(ctx, sess, inFrame, m.payload, flapc); err != nil {
  200. middleware.LogRequestError(ctx, logger, inFrame, err)
  201. if errors.Is(err, ErrRouteNotFound) || errors.Is(err, wire.ErrUnsupportedFoodGroup) {
  202. if err1 := sendInvalidSNACErr(inFrame, flapc); err1 != nil {
  203. return errors.Join(err1, err)
  204. }
  205. if config.FailFast {
  206. panic(err.Error())
  207. }
  208. break
  209. }
  210. return err
  211. }
  212. case wire.FLAPFrameSignon:
  213. return fmt.Errorf("shouldn't get FLAPFrameSignon. flap: %v", m.flap)
  214. case wire.FLAPFrameError:
  215. return fmt.Errorf("got FLAPFrameError. flap: %v", m.flap)
  216. case wire.FLAPFrameSignoff:
  217. logger.InfoContext(ctx, "got FLAPFrameSignoff", "flap", m.flap)
  218. return nil
  219. case wire.FLAPFrameKeepAlive:
  220. logger.DebugContext(ctx, "keepalive heartbeat")
  221. default:
  222. return fmt.Errorf("got unknown FLAP frame type. flap: %v", m.flap)
  223. }
  224. case m := <-sess.ReceiveMessage():
  225. // forward a notification sent from another client to this client
  226. if err := flapc.SendSNAC(m.Frame, m.Body); err != nil {
  227. middleware.LogRequestError(ctx, logger, m.Frame, err)
  228. return err
  229. }
  230. middleware.LogRequest(ctx, logger, m.Frame, m.Body)
  231. case <-sess.Closed():
  232. // gracefully disconnect so that the client does not try to
  233. // reconnect when the connection closes.
  234. if err := flapc.Disconnect(); err != nil {
  235. return fmt.Errorf("unable to gracefully disconnect user. %w", err)
  236. }
  237. return nil
  238. case err := <-readErrCh:
  239. if !errors.Is(io.EOF, err) {
  240. logger.ErrorContext(ctx, "client disconnected with error", "err", err)
  241. }
  242. return nil
  243. }
  244. }
  245. }