icbm.go 7.5 KB

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