connection.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package server
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "io"
  7. "log/slog"
  8. "github.com/mkaminski/goaim/oscar"
  9. "github.com/mkaminski/goaim/state"
  10. )
  11. var (
  12. ErrUnsupportedSubGroup = errors.New("unimplemented subgroup, your client version may be unsupported")
  13. )
  14. type (
  15. incomingMessage struct {
  16. flap oscar.FLAPFrame
  17. payload *bytes.Buffer
  18. }
  19. alertHandler func(ctx context.Context, msg oscar.SNACMessage, w io.Writer, u *uint32) error
  20. clientReqHandler func(ctx context.Context, r io.Reader, w io.Writer, u *uint32) error
  21. )
  22. func sendSNAC(frame oscar.SNACFrame, body any, sequence *uint32, w io.Writer) error {
  23. snacBuf := &bytes.Buffer{}
  24. if err := oscar.Marshal(frame, snacBuf); err != nil {
  25. return err
  26. }
  27. if err := oscar.Marshal(body, snacBuf); err != nil {
  28. return err
  29. }
  30. flap := oscar.FLAPFrame{
  31. StartMarker: 42,
  32. FrameType: oscar.FLAPFrameData,
  33. Sequence: uint16(*sequence),
  34. PayloadLength: uint16(snacBuf.Len()),
  35. }
  36. if err := oscar.Marshal(flap, w); err != nil {
  37. return err
  38. }
  39. expectLen := snacBuf.Len()
  40. c, err := w.Write(snacBuf.Bytes())
  41. if err != nil {
  42. return err
  43. }
  44. if c != expectLen {
  45. panic("did not write the expected # of bytes")
  46. }
  47. *sequence++
  48. return nil
  49. }
  50. func sendInvalidSNACErr(frameIn oscar.SNACFrame, w io.Writer, sequence *uint32) error {
  51. frameOut := oscar.SNACFrame{
  52. FoodGroup: frameIn.FoodGroup,
  53. SubGroup: 0x01, // error subgroup for all SNACs
  54. RequestID: frameIn.RequestID,
  55. }
  56. bodyOut := oscar.SNACError{
  57. Code: oscar.ErrorCodeInvalidSnac,
  58. }
  59. return sendSNAC(frameOut, bodyOut, sequence, w)
  60. }
  61. func consumeFLAPFrames(r io.Reader, msgCh chan incomingMessage, errCh chan error) {
  62. defer close(msgCh)
  63. defer close(errCh)
  64. for {
  65. in := incomingMessage{}
  66. if err := oscar.Unmarshal(&in.flap, r); err != nil {
  67. errCh <- err
  68. return
  69. }
  70. if in.flap.FrameType == oscar.FLAPFrameData {
  71. buf := make([]byte, in.flap.PayloadLength)
  72. if _, err := r.Read(buf); err != nil {
  73. errCh <- err
  74. return
  75. }
  76. in.payload = bytes.NewBuffer(buf)
  77. }
  78. msgCh <- in
  79. }
  80. }
  81. func dispatchIncomingMessages(ctx context.Context, sess *state.Session, seq uint32, rw io.ReadWriter, logger *slog.Logger, fn clientReqHandler, alertHandler alertHandler) {
  82. // buffered so that the go routine has room to exit
  83. msgCh := make(chan incomingMessage, 1)
  84. readErrCh := make(chan error, 1)
  85. go consumeFLAPFrames(rw, msgCh, readErrCh)
  86. defer func() {
  87. logger.InfoContext(ctx, "user disconnected")
  88. }()
  89. for {
  90. select {
  91. case m := <-msgCh:
  92. switch m.flap.FrameType {
  93. case oscar.FLAPFrameData:
  94. // route a client request to the appropriate service handler. the
  95. // handler may write a response to the client connection.
  96. if err := fn(ctx, m.payload, rw, &seq); err != nil {
  97. return
  98. }
  99. case oscar.FLAPFrameSignon:
  100. logger.ErrorContext(ctx, "shouldn't get FLAPFrameSignon", "flap", m.flap)
  101. case oscar.FLAPFrameError:
  102. logger.ErrorContext(ctx, "got FLAPFrameError", "flap", m.flap)
  103. return
  104. case oscar.FLAPFrameSignoff:
  105. logger.InfoContext(ctx, "got FLAPFrameSignoff", "flap", m.flap)
  106. return
  107. case oscar.FLAPFrameKeepAlive:
  108. logger.DebugContext(ctx, "keepalive heartbeat")
  109. default:
  110. logger.ErrorContext(ctx, "got unknown FLAP frame type", "flap", m.flap)
  111. return
  112. }
  113. case m := <-sess.RecvMessage():
  114. // forward a notification sent from another client to this client
  115. if err := alertHandler(ctx, m, rw, &seq); err != nil {
  116. logRequestError(ctx, logger, m.Frame, err)
  117. return
  118. }
  119. logRequest(ctx, logger, m.Frame, m.Body)
  120. case <-sess.Closed():
  121. // gracefully disconnect so that the client does not try to
  122. // reconnect when the connection closes.
  123. flap := oscar.FLAPFrame{
  124. StartMarker: 42,
  125. FrameType: oscar.FLAPFrameSignoff,
  126. Sequence: uint16(seq),
  127. PayloadLength: uint16(0),
  128. }
  129. if err := oscar.Marshal(flap, rw); err != nil {
  130. logger.ErrorContext(ctx, "unable to gracefully disconnect user", "err", err)
  131. }
  132. return
  133. case err := <-readErrCh:
  134. if !errors.Is(io.EOF, err) {
  135. logger.ErrorContext(ctx, "client disconnected with error", "err", err)
  136. }
  137. return
  138. }
  139. }
  140. }