4
0

icbm.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. package server
  2. import (
  3. "errors"
  4. "io"
  5. "github.com/mkaminski/goaim/oscar"
  6. )
  7. const (
  8. evilDelta = uint16(100)
  9. evilDeltaAnon = uint16(30)
  10. )
  11. type ICBMHandler interface {
  12. ChannelMsgToHostHandler(sm SessionManager, fm FeedbagManager, sess *Session, snacPayloadIn oscar.SNAC_0x04_0x06_ICBMChannelMsgToHost) (*XMessage, error)
  13. ClientEventHandler(sm SessionManager, fm FeedbagManager, sess *Session, snacPayloadIn oscar.SNAC_0x04_0x14_ICBMClientEvent) error
  14. EvilRequestHandler(sm SessionManager, fm FeedbagManager, sess *Session, snacPayloadIn oscar.SNAC_0x04_0x08_ICBMEvilRequest) (XMessage, error)
  15. ParameterQueryHandler() XMessage
  16. }
  17. func NewICBMRouter() ICBMRouter {
  18. return ICBMRouter{
  19. ICBMHandler: ICBMService{},
  20. }
  21. }
  22. type ICBMRouter struct {
  23. ICBMHandler
  24. }
  25. func (rt *ICBMRouter) RouteICBM(sm SessionManager, fm FeedbagManager, sess *Session, SNACFrame oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
  26. switch SNACFrame.SubGroup {
  27. case oscar.ICBMAddParameters:
  28. inSNAC := oscar.SNAC_0x04_0x02_ICBMAddParameters{}
  29. return oscar.Unmarshal(&inSNAC, r)
  30. case oscar.ICBMParameterQuery:
  31. outSNAC := rt.ParameterQueryHandler()
  32. return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  33. case oscar.ICBMChannelMsgToHost:
  34. inSNAC := oscar.SNAC_0x04_0x06_ICBMChannelMsgToHost{}
  35. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  36. return err
  37. }
  38. outSNAC, err := rt.ChannelMsgToHostHandler(sm, fm, sess, inSNAC)
  39. if err != nil {
  40. return err
  41. }
  42. if outSNAC != nil {
  43. if err := writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w); err != nil {
  44. return err
  45. }
  46. }
  47. return nil
  48. case oscar.ICBMEvilRequest:
  49. inSNAC := oscar.SNAC_0x04_0x08_ICBMEvilRequest{}
  50. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  51. return err
  52. }
  53. outSNAC, err := rt.EvilRequestHandler(sm, fm, sess, inSNAC)
  54. if err != nil {
  55. return err
  56. }
  57. return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  58. case oscar.ICBMClientErr:
  59. inSNAC := oscar.SNAC_0x04_0x0B_ICBMClientErr{}
  60. return oscar.Unmarshal(&inSNAC, r)
  61. case oscar.ICBMClientEvent:
  62. inSNAC := oscar.SNAC_0x04_0x14_ICBMClientEvent{}
  63. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  64. return err
  65. }
  66. return rt.ClientEventHandler(sm, fm, sess, inSNAC)
  67. default:
  68. return ErrUnsupportedSubGroup
  69. }
  70. }
  71. type ICBMService struct {
  72. }
  73. func (s ICBMService) ParameterQueryHandler() XMessage {
  74. return XMessage{
  75. snacFrame: oscar.SnacFrame{
  76. FoodGroup: ICBM,
  77. SubGroup: oscar.ICBMParameterReply,
  78. },
  79. snacOut: oscar.SNAC_0x04_0x05_ICBMParameterReply{
  80. MaxSlots: 100,
  81. ICBMFlags: 3,
  82. MaxIncomingICBMLen: 512,
  83. MaxSourceEvil: 999,
  84. MaxDestinationEvil: 999,
  85. MinInterICBMInterval: 0,
  86. },
  87. }
  88. }
  89. func (s ICBMService) ChannelMsgToHostHandler(sm SessionManager, fm FeedbagManager, sess *Session, snacPayloadIn oscar.SNAC_0x04_0x06_ICBMChannelMsgToHost) (*XMessage, error) {
  90. blocked, err := fm.Blocked(sess.ScreenName, snacPayloadIn.ScreenName)
  91. if err != nil {
  92. return nil, err
  93. }
  94. if blocked != BlockedNo {
  95. code := ErrorCodeNotLoggedOn
  96. if blocked == BlockedA {
  97. code = ErrorCodeInLocalPermitDeny
  98. }
  99. return &XMessage{
  100. snacFrame: oscar.SnacFrame{
  101. FoodGroup: ICBM,
  102. SubGroup: oscar.ICBMErr,
  103. },
  104. snacOut: oscar.SnacError{
  105. Code: code,
  106. },
  107. }, nil
  108. }
  109. recipSess, err := sm.RetrieveByScreenName(snacPayloadIn.ScreenName)
  110. switch {
  111. case errors.Is(err, errSessNotFound):
  112. return &XMessage{
  113. snacFrame: oscar.SnacFrame{
  114. FoodGroup: ICBM,
  115. SubGroup: oscar.ICBMErr,
  116. },
  117. snacOut: oscar.SnacError{
  118. Code: ErrorCodeNotLoggedOn,
  119. },
  120. }, nil
  121. case err != nil:
  122. return nil, err
  123. }
  124. clientIM := oscar.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  125. Cookie: snacPayloadIn.Cookie,
  126. ChannelID: snacPayloadIn.ChannelID,
  127. TLVUserInfo: oscar.TLVUserInfo{
  128. ScreenName: sess.ScreenName,
  129. WarningLevel: sess.GetWarning(),
  130. },
  131. TLVRestBlock: oscar.TLVRestBlock{
  132. TLVList: oscar.TLVList{
  133. {
  134. TType: 0x0B,
  135. Val: []byte{},
  136. },
  137. },
  138. },
  139. }
  140. // copy over TLVs from sender SNAC to recipient SNAC verbatim. this
  141. // includes ICBMTLVTagRequestHostAck, which is ignored by the client, as
  142. // far as I can tell.
  143. clientIM.AddTLVList(snacPayloadIn.TLVRestBlock.TLVList)
  144. sm.SendToScreenName(recipSess.ScreenName, XMessage{
  145. snacFrame: oscar.SnacFrame{
  146. FoodGroup: ICBM,
  147. SubGroup: oscar.ICBMChannelMsgToclient,
  148. },
  149. snacOut: clientIM,
  150. })
  151. if _, requestedConfirmation := snacPayloadIn.TLVRestBlock.GetSlice(oscar.ICBMTLVTagRequestHostAck); !requestedConfirmation {
  152. // don't ack message
  153. return nil, nil
  154. }
  155. // ack message back to sender
  156. return &XMessage{
  157. snacFrame: oscar.SnacFrame{
  158. FoodGroup: ICBM,
  159. SubGroup: oscar.ICBMHostAck,
  160. },
  161. snacOut: oscar.SNAC_0x04_0x0C_ICBMHostAck{
  162. Cookie: snacPayloadIn.Cookie,
  163. ChannelID: snacPayloadIn.ChannelID,
  164. ScreenName: snacPayloadIn.ScreenName,
  165. },
  166. }, nil
  167. }
  168. func (s ICBMService) ClientEventHandler(sm SessionManager, fm FeedbagManager, sess *Session, snacPayloadIn oscar.SNAC_0x04_0x14_ICBMClientEvent) error {
  169. blocked, err := fm.Blocked(sess.ScreenName, snacPayloadIn.ScreenName)
  170. switch {
  171. case err != nil:
  172. return err
  173. case blocked != BlockedNo:
  174. return nil
  175. default:
  176. sm.SendToScreenName(snacPayloadIn.ScreenName, XMessage{
  177. snacFrame: oscar.SnacFrame{
  178. FoodGroup: ICBM,
  179. SubGroup: oscar.ICBMClientEvent,
  180. },
  181. snacOut: oscar.SNAC_0x04_0x14_ICBMClientEvent{
  182. Cookie: snacPayloadIn.Cookie,
  183. ChannelID: snacPayloadIn.ChannelID,
  184. ScreenName: sess.ScreenName,
  185. Event: snacPayloadIn.Event,
  186. },
  187. })
  188. return nil
  189. }
  190. }
  191. func (s ICBMService) EvilRequestHandler(sm SessionManager, fm FeedbagManager, sess *Session, snacPayloadIn oscar.SNAC_0x04_0x08_ICBMEvilRequest) (XMessage, error) {
  192. // don't let users warn themselves, it causes the AIM client to go into a
  193. // weird state.
  194. if snacPayloadIn.ScreenName == sess.ScreenName {
  195. return XMessage{
  196. snacFrame: oscar.SnacFrame{
  197. FoodGroup: ICBM,
  198. SubGroup: oscar.ICBMErr,
  199. },
  200. snacOut: oscar.SnacError{
  201. Code: ErrorCodeNotSupportedByHost,
  202. },
  203. }, nil
  204. }
  205. blocked, err := fm.Blocked(sess.ScreenName, snacPayloadIn.ScreenName)
  206. if err != nil {
  207. return XMessage{}, nil
  208. }
  209. if blocked != BlockedNo {
  210. return XMessage{
  211. snacFrame: oscar.SnacFrame{
  212. FoodGroup: ICBM,
  213. SubGroup: oscar.ICBMErr,
  214. },
  215. snacOut: oscar.SnacError{
  216. Code: ErrorCodeNotLoggedOn,
  217. },
  218. }, nil
  219. }
  220. recipSess, err := sm.RetrieveByScreenName(snacPayloadIn.ScreenName)
  221. if err != nil {
  222. return XMessage{}, nil
  223. }
  224. increase := evilDelta
  225. if snacPayloadIn.SendAs == 1 {
  226. increase = evilDeltaAnon
  227. }
  228. recipSess.IncreaseWarning(increase)
  229. var notif any
  230. if snacPayloadIn.SendAs == 0 {
  231. notif = oscar.SNAC_0x01_0x10_OServiceEvilNotification{
  232. NewEvil: recipSess.GetWarning(),
  233. TLVUserInfo: oscar.TLVUserInfo{
  234. ScreenName: sess.ScreenName,
  235. WarningLevel: recipSess.GetWarning(),
  236. },
  237. }
  238. } else {
  239. notif = oscar.SNAC_0x01_0x10_OServiceEvilNotificationAnon{
  240. NewEvil: recipSess.GetWarning(),
  241. }
  242. }
  243. sm.SendToScreenName(recipSess.ScreenName, XMessage{
  244. snacFrame: oscar.SnacFrame{
  245. FoodGroup: OSERVICE,
  246. SubGroup: oscar.OServiceEvilNotification,
  247. },
  248. snacOut: notif,
  249. })
  250. if err := NotifyArrival(recipSess, sm, fm); err != nil {
  251. return XMessage{}, nil
  252. }
  253. return XMessage{
  254. snacFrame: oscar.SnacFrame{
  255. FoodGroup: ICBM,
  256. SubGroup: oscar.ICBMEvilReply,
  257. },
  258. snacOut: oscar.SNAC_0x04_0x09_ICBMEvilReply{
  259. EvilDeltaApplied: increase,
  260. UpdatedEvilValue: recipSess.GetWarning(),
  261. },
  262. }, nil
  263. }