frames.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. package wire
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "sync"
  7. )
  8. type SNACError struct {
  9. Code uint16
  10. TLVRestBlock
  11. }
  12. const (
  13. FLAPFrameSignon uint8 = 0x01
  14. FLAPFrameData uint8 = 0x02
  15. FLAPFrameError uint8 = 0x03
  16. FLAPFrameSignoff uint8 = 0x04
  17. FLAPFrameKeepAlive uint8 = 0x05
  18. )
  19. type FLAPFrame struct {
  20. StartMarker uint8
  21. FrameType uint8
  22. Sequence uint16
  23. Payload []byte `oscar:"len_prefix=uint16"`
  24. }
  25. // FLAPFrameDisconnect is the last FLAP frame sent to a client before
  26. // disconnection. It differs from FLAPFrame in that there is no payload length
  27. // prefix at the end, which causes pre-multi-conn Windows AIM clients to
  28. // improperly handle server disconnections, as when the regular FLAPFrame type
  29. // is used.
  30. type FLAPFrameDisconnect struct {
  31. StartMarker uint8
  32. FrameType uint8
  33. Sequence uint16
  34. }
  35. type SNACFrame struct {
  36. FoodGroup uint16
  37. SubGroup uint16
  38. Flags uint16
  39. RequestID uint32
  40. }
  41. // ReqIDFromServer is the SNAC frame Request ID value that indicates the SNAC
  42. // is initiated by the server. Some clients, such as the Java AIM 1.1.19,
  43. // completely fail to process some server SNACs if the high bit is not set on
  44. // request ID.
  45. const ReqIDFromServer = 1 << 31
  46. type FLAPSignonFrame struct {
  47. FLAPVersion uint32
  48. TLVRestBlock
  49. }
  50. type SNACMessage struct {
  51. Frame SNACFrame
  52. Body any
  53. }
  54. // NewFlapClient creates a new FLAP client instance. startSeq is the initial
  55. // sequence value, which is typically 0. r receives FLAP messages, w writes
  56. // FLAP messages.
  57. func NewFlapClient(startSeq uint32, r io.Reader, w io.Writer) *FlapClient {
  58. return &FlapClient{
  59. sequence: startSeq,
  60. r: r,
  61. w: w,
  62. mutex: sync.Mutex{},
  63. }
  64. }
  65. // FlapClient sends and receive FLAP frames to and from the server. It ensures
  66. // that the message sequence numbers are properly incremented after sending
  67. // each successive message. It is not safe to use with multiple goroutines
  68. // without synchronization.
  69. type FlapClient struct {
  70. sequence uint32
  71. r io.Reader
  72. w io.Writer
  73. mutex sync.Mutex
  74. }
  75. // Fixes a race condition caused by testify. Yup...
  76. // https://github.com/stretchr/testify/issues/625
  77. func (f *FlapClient) String() string {
  78. return ""
  79. }
  80. // SendSignonFrame sends a signon FLAP frame containing a list of TLVs to
  81. // authenticate or initiate a session.
  82. func (f *FlapClient) SendSignonFrame(tlvs []TLV) error {
  83. signonFrame := FLAPSignonFrame{
  84. FLAPVersion: 1,
  85. }
  86. if len(tlvs) > 0 {
  87. signonFrame.AppendList(tlvs)
  88. }
  89. buf := &bytes.Buffer{}
  90. if err := MarshalBE(signonFrame, buf); err != nil {
  91. return err
  92. }
  93. f.mutex.Lock()
  94. defer f.mutex.Unlock()
  95. flap := FLAPFrame{
  96. StartMarker: 42,
  97. FrameType: FLAPFrameSignon,
  98. Sequence: uint16(f.sequence),
  99. Payload: buf.Bytes(),
  100. }
  101. if err := MarshalBE(flap, f.w); err != nil {
  102. return err
  103. }
  104. f.sequence++
  105. return nil
  106. }
  107. // ReceiveSignonFrame receives a signon FLAP response message.
  108. func (f *FlapClient) ReceiveSignonFrame() (FLAPSignonFrame, error) {
  109. flap := FLAPFrame{}
  110. if err := UnmarshalBE(&flap, f.r); err != nil {
  111. return FLAPSignonFrame{}, err
  112. }
  113. signonFrame := FLAPSignonFrame{}
  114. if err := UnmarshalBE(&signonFrame, bytes.NewBuffer(flap.Payload)); err != nil {
  115. return FLAPSignonFrame{}, err
  116. }
  117. return signonFrame, nil
  118. }
  119. // ReceiveFLAP receives a FLAP frame and body. It only returns a body if the
  120. // FLAP frame is a data frame.
  121. func (f *FlapClient) ReceiveFLAP() (FLAPFrame, error) {
  122. flap := FLAPFrame{}
  123. err := UnmarshalBE(&flap, f.r)
  124. if err != nil {
  125. err = fmt.Errorf("unable to unmarshal FLAP frame: %w", err)
  126. }
  127. return flap, err
  128. }
  129. // OldSignoff sends a signoff FLAP frame for legacy clients that do not
  130. // support multi-connection (Windows AIM 1.x–4.1).
  131. //
  132. // When these clients receive this frame, they display a "connection lost"
  133. // message and close the session. Unlike normal FLAP frames, this variant
  134. // omits the payload size field. If the size field were present, the client
  135. // would hang without displaying any message upon server disconnection.
  136. func (f *FlapClient) OldSignoff() error {
  137. f.mutex.Lock()
  138. defer f.mutex.Unlock()
  139. flap := FLAPFrameDisconnect{
  140. StartMarker: 42,
  141. FrameType: FLAPFrameSignoff,
  142. Sequence: uint16(f.sequence),
  143. }
  144. return MarshalBE(flap, f.w)
  145. }
  146. // NewSignoff sends a signoff FLAP frame for multi-connection clients.
  147. //
  148. // The frame includes a TLV block with additional metadata such as error codes.
  149. // Client behavior depends on the version:
  150. // - AIM 4.3–5.x: the client minimizes and enters a "signed off" state.
  151. // - AIM 6.x–7.x: the client closes and displays a disconnection error.
  152. func (f *FlapClient) NewSignoff(tlvs TLVRestBlock) error {
  153. tlvBuf := &bytes.Buffer{}
  154. if err := MarshalBE(tlvs, tlvBuf); err != nil {
  155. return err
  156. }
  157. f.mutex.Lock()
  158. defer f.mutex.Unlock()
  159. flap := FLAPFrame{
  160. StartMarker: 42,
  161. FrameType: FLAPFrameSignoff,
  162. Sequence: uint16(f.sequence),
  163. Payload: tlvBuf.Bytes(),
  164. }
  165. if err := MarshalBE(flap, f.w); err != nil {
  166. return err
  167. }
  168. f.sequence++
  169. return nil
  170. }
  171. // SendSNAC sends a SNAC message wrapped in a FLAP frame.
  172. func (f *FlapClient) SendSNAC(frame SNACFrame, body any) error {
  173. snacBuf := &bytes.Buffer{}
  174. if err := MarshalBE(frame, snacBuf); err != nil {
  175. return err
  176. }
  177. if err := MarshalBE(body, snacBuf); err != nil {
  178. return err
  179. }
  180. f.mutex.Lock()
  181. defer f.mutex.Unlock()
  182. flap := FLAPFrame{
  183. StartMarker: 42,
  184. FrameType: FLAPFrameData,
  185. Sequence: uint16(f.sequence),
  186. Payload: snacBuf.Bytes(),
  187. }
  188. if err := MarshalBE(flap, f.w); err != nil {
  189. return err
  190. }
  191. f.sequence++
  192. return nil
  193. }
  194. func (f *FlapClient) SendDataFrame(payload []byte) error {
  195. f.mutex.Lock()
  196. defer f.mutex.Unlock()
  197. flap := FLAPFrame{
  198. StartMarker: 42,
  199. FrameType: FLAPFrameData,
  200. Sequence: uint16(f.sequence),
  201. Payload: payload,
  202. }
  203. if err := MarshalBE(flap, f.w); err != nil {
  204. return err
  205. }
  206. f.sequence++
  207. return nil
  208. }
  209. func (f *FlapClient) SendKeepAliveFrame() error {
  210. f.mutex.Lock()
  211. defer f.mutex.Unlock()
  212. flap := FLAPFrame{
  213. StartMarker: 42,
  214. FrameType: FLAPFrameKeepAlive,
  215. Sequence: uint16(f.sequence),
  216. }
  217. if err := MarshalBE(flap, f.w); err != nil {
  218. return err
  219. }
  220. f.sequence++
  221. return nil
  222. }
  223. // ReceiveSNAC receives a SNAC message wrapped in a FLAP frame.
  224. func (f *FlapClient) ReceiveSNAC(frame *SNACFrame, body any) error {
  225. flap := FLAPFrame{}
  226. if err := UnmarshalBE(&flap, f.r); err != nil {
  227. return err
  228. }
  229. buf := bytes.NewBuffer(flap.Payload)
  230. if err := UnmarshalBE(frame, buf); err != nil {
  231. return err
  232. }
  233. return UnmarshalBE(body, buf)
  234. }