frames.go 6.9 KB

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