Ticket #23: scapy-skinny.py

File scapy-skinny.py, 17.8 kB (added by nico@chdir.org, 2 years ago)

skinny extension

Line 
1 #! /usr/bin/env python
2
3 #############################################################################
4 ##                                                                         ##
5 ## scapy-skinny.py --- Skinny Client Control Protocol (SCCP) extension     ##
6 ##                                                                         ##
7 ## Copyright (C) 2006    Nicolas Bareil      <nicolas.bareil@ eads.net>    ##
8 ##                       EADS/CRC security team                            ##
9 ##                                                                         ##
10 ## This program is free software; you can redistribute it and/or modify it ##
11 ## under the terms of the GNU General Public License version 2 as          ##
12 ## published by the Free Software Foundation; version 2.                   ##
13 ##                                                                         ##
14 ## This program is distributed in the hope that it will be useful, but     ##
15 ## WITHOUT ANY WARRANTY; without even the implied warranty of              ##
16 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       ##
17 ## General Public License for more details.                                ##
18 ##                                                                         ##
19 #############################################################################
20
21 from scapy import *
22 import __builtin__
23
24 #####################################################################
25 # Helpers and constants
26 #####################################################################
27
28 skinny_messages_cls = {
29 # Station -> Callmanager
30   0x0000: "SkinnyMessageKeepAlive",
31   0x0001: "SkinnyMessageRegister",
32   0x0002: "SkinnyMessageIpPort",
33   0x0003: "SkinnyMessageKeypadButton",
34   0x0004: "SkinnyMessageEnblocCall",
35   0x0005: "SkinnyMessageStimulus",
36   0x0006: "SkinnyMessageOffHook",
37   0x0007: "SkinnyMessageOnHook",
38   0x0008: "SkinnyMessageHookFlash",
39   0x0009: "SkinnyMessageForwardStatReq",
40   0x000A: "SkinnyMessageSpeedDialStatReq",
41   0x000B: "SkinnyMessageLineStatReq",
42   0x000C: "SkinnyMessageConfigStatReq",
43   0x000D: "SkinnyMessageTimeDateReq",
44   0x000E: "SkinnyMessageButtonTemplateReq",
45   0x000F: "SkinnyMessageVersionReq",
46   0x0010: "SkinnyMessageCapabilitiesRes",
47   0x0011: "SkinnyMessageMediaPortList",
48   0x0012: "SkinnyMessageServerReq",
49   0x0020: "SkinnyMessageAlarm",
50   0x0021: "SkinnyMessageMulticastMediaReceptionAck",
51   0x0022: "SkinnyMessageOpenReceiveChannelAck",
52   0x0023: "SkinnyMessageConnectionStatisticsRes",
53   0x0024: "SkinnyMessageOffHookWithCgpn",
54   0x0025: "SkinnyMessageSoftKeySetReq",
55   0x0026: "SkinnyMessageSoftKeyEvent",
56   0x0027: "SkinnyMessageUnregister",
57   0x0028: "SkinnyMessageSoftKeyTemplateReq",
58   0x0029: "SkinnyMessageRegisterTokenReq",
59   0x002A: "SkinnyMessageMediaTransmissionFailure",
60   0x002B: "SkinnyMessageHeadsetStatus",
61   0x002C: "SkinnyMessageMediaResourceNotification",
62   0x002D: "SkinnyMessageRegisterAvailableLines",
63   0x002E: "SkinnyMessageDeviceToUserData",
64   0x002F: "SkinnyMessageDeviceToUserDataResponse",
65   0x0030: "SkinnyMessageUpdateCapabilities",
66   0x0031: "SkinnyMessageOpenMultiMediaReceiveChannelAck",
67   0x0032: "SkinnyMessageClearConference",
68   0x0033: "SkinnyMessageServiceURLStatReq",
69   0x0034: "SkinnyMessageFeatureStatReq",
70   0x0035: "SkinnyMessageCreateConferenceRes",
71   0x0036: "SkinnyMessageDeleteConferenceRes",
72   0x0037: "SkinnyMessageModifyConferenceRes",
73   0x0038: "SkinnyMessageAddParticipantRes",
74   0x0039: "SkinnyMessageAuditConferenceRes",
75   0x0040: "SkinnyMessageAuditParticipantRes",
76   0x0041: "SkinnyMessageDeviceToUserDataVersion1",
77 # Callmanager -> Station */
78   0x0081: "SkinnyMessageRegisterAck",
79   0x0082: "SkinnyMessageStartTone",
80   0x0083: "SkinnyMessageStopTone",
81   0x0085: "SkinnyMessageSetRinger",
82   0x0086: "SkinnyMessageSetLamp",
83   0x0087: "SkinnyMessageSetHkFDetect",
84   0x0088: "SkinnyMessageSpeakerMode",
85   0x0089: "SkinnyMessageSetMicroMode",
86   0x008A: "SkinnyMessageStartMediaTransmission",
87   0x008B: "SkinnyMessageStopMediaTransmission",
88   0x008C: "SkinnyMessageStartMediaReception",
89   0x008D: "SkinnyMessageStopMediaReception",
90   0x008F: "SkinnyMessageCallInfo",
91   0x0090: "SkinnyMessageForwardStat",
92   0x0091: "SkinnyMessageSpeedDialStat",
93   0x0092: "SkinnyMessageLineStat",
94   0x0093: "SkinnyMessageConfigStat",
95   0x0094: "SkinnyMessageTimeDate",
96   0x0095: "SkinnyMessageStartSessionTransmission",
97   0x0096: "SkinnyMessageStopSessionTransmission",
98   0x0097: "SkinnyMessageButtonTemplate",
99   0x0098: "SkinnyMessageVersion",
100   0x0099: "SkinnyMessageDisplayText",
101   0x009A: "SkinnyMessageClearDisplay",
102   0x009B: "SkinnyMessageCapabilitiesReq",
103   0x009C: "SkinnyMessageEnunciatorCommand",
104   0x009D: "SkinnyMessageRegisterReject",
105   0x009E: "SkinnyMessageServerRes",
106   0x009F: "SkinnyMessageReset",
107   0x0100: "SkinnyMessageKeepAliveAck",
108   0x0101: "SkinnyMessageStartMulticastMediaReception",
109   0x0102: "SkinnyMessageStartMulticastMediaTransmission",
110   0x0103: "SkinnyMessageStopMulticastMediaReception",
111   0x0104: "SkinnyMessageStopMulticastMediaTransmission",
112   0x0105: "SkinnyMessageOpenReceiveChannel",
113   0x0106: "SkinnyMessageCloseReceiveChannel",
114   0x0107: "SkinnyMessageConnectionStatisticsReq",
115   0x0108: "SkinnyMessageSoftKeyTemplateRes",
116   0x0109: "SkinnyMessageSoftKeySetRes",
117   0x0110: "SkinnyMessageSoftKeyEvent",
118   0x0111: "SkinnyMessageCallState",
119   0x0112: "SkinnyMessagePromptStatus",
120   0x0113: "SkinnyMessageClearPromptStatus",
121   0x0114: "SkinnyMessageDisplayNotify",
122   0x0115: "SkinnyMessageClearNotify",
123   0x0116: "SkinnyMessageCallPlane",
124   0x0117: "SkinnyMessageCallPlane",
125   0x0118: "SkinnyMessageUnregisterAck",
126   0x0119: "SkinnyMessageBackSpaceReq",
127   0x011A: "SkinnyMessageRegisterTokenAck",
128   0x011B: "SkinnyMessageRegisterTokenReject",
129   0x0042: "SkinnyMessageDeviceToUserDataResponseVersion1",
130   0x011C: "SkinnyMessageStartMediaFailureDetection",
131   0x011D: "SkinnyMessageDialedNumber",
132   0x011E: "SkinnyMessageUserToDeviceData",
133   0x011F: "SkinnyMessageFeatureStat",
134   0x0120: "SkinnyMessageDisplayPriNotify",
135   0x0121: "SkinnyMessageClearPriNotify",
136   0x0122: "SkinnyMessageStartAnnouncement",
137   0x0123: "SkinnyMessageStopAnnouncement",
138   0x0124: "SkinnyMessageAnnouncementFinish",
139   0x0127: "SkinnyMessageNotifyDtmfTone",
140   0x0128: "SkinnyMessageSendDtmfTone",
141   0x0129: "SkinnyMessageSubscribeDtmfPayloadReq",
142   0x012A: "SkinnyMessageSubscribeDtmfPayloadRes",
143   0x012B: "SkinnyMessageSubscribeDtmfPayloadErr",
144   0x012C: "SkinnyMessageUnSubscribeDtmfPayloadReq",
145   0x012D: "SkinnyMessageUnSubscribeDtmfPayloadRes",
146   0x012E: "SkinnyMessageUnSubscribeDtmfPayloadErr",
147   0x012F: "SkinnyMessageServiceURLStat",
148   0x0130: "SkinnyMessageCallSelectStat",
149   0x0131: "SkinnyMessageOpenMultiMediaChannel",
150   0x0132: "SkinnyMessageStartMultiMediaTransmission",
151   0x0133: "SkinnyMessageStopMultiMediaTransmission",
152   0x0134: "SkinnyMessageMiscellaneousCommand",
153   0x0135: "SkinnyMessageFlowControlCommand",
154   0x0136: "SkinnyMessageCloseMultiMediaReceiveChannel",
155   0x0137: "SkinnyMessageCreateConferenceReq",
156   0x0138: "SkinnyMessageDeleteConferenceReq",
157   0x0139: "SkinnyMessageModifyConferenceReq",
158   0x013A: "SkinnyMessageAddParticipantReq",
159   0x013B: "SkinnyMessageDropParticipantReq",
160   0x013C: "SkinnyMessageAuditConferenceReq",
161   0x013D: "SkinnyMessageAuditParticipantReq",
162   0x013F: "SkinnyMessageUserToDeviceDataVersion1",
163   }
164
165 skinny_callstates = {
166     0x1: "Off Hook",
167     0x2: "On Hook",
168     0x3: "Ring out",
169     0xc: "Proceeding",
170 }
171
172
173 skinny_ring_type = {
174     0x1: "Ring off"
175 }
176
177 skinny_speaker_modes = {
178     0x1: "Speaker on",
179     0x2: "Speaker off"
180 }
181
182 skinny_lamp_mode = {
183     0x1: "Off (?)",
184     0x2: "On",
185 }
186
187 skinny_stimulus = {
188     0x9: "Line"
189 }
190
191
192 ############
193 ## Fields ##
194 ############
195
196 class SkinnyDateTimeField(StrFixedLenField):
197     def __init__(self, name, default):
198         StrFixedLenField.__init__(self, name, default, 32)
199
200     def m2i(self, pkt, s):
201         year,month,dow,day,hour,min,sec,milisecond=struct.unpack('<8I', s)
202         return (year, month, day, hour, min, sec)
203    
204     def i2m(self, pkt, val):
205         if type(val) is str:
206             val = self.h2i(pkt, val)
207         l= val[:2] + (0,) + val[2:7] + (0,)
208         return struct.pack('<8I', *l)
209
210     def i2h(self, pkt, x):
211         if type(x) is str:
212             return x
213         else:
214             return time.ctime(time.mktime(x+(0,0,0)))
215
216     def i2repr(self, pkt, x):
217         return self.i2h(pkt, x)
218    
219     def h2i(self, pkt, s):
220         t = ()
221         if type(s) is str:
222             t = time.strptime(s)
223             t = t[:2] + t[2:-3]
224         else:
225             if not s:
226                 y,m,d,h,min,sec,rest,rest,rest = time.gmtime(time.time())
227                 t = (y,m,d,h,min,sec)
228             else:
229                 t=s
230         return t
231
232
233 ###########################
234 ## Packet abstract class ##
235 ###########################
236
237 class SkinnyMessageGeneric(Packet):
238     name='Generic message'
239
240 class SkinnyMessageKeepAlive(Packet):
241     name='keep alive'
242
243 class SkinnyMessageKeepAliveAck(Packet):
244     name='keep alive ack'
245
246 class SkinnyMessageOffHook(Packet):
247     name = 'Off Hook'
248     fields_desc = [ LEIntField("unknown1", 0),
249                     LEIntField("unknown2", 0),]
250        
251 class SkinnyMessageOnHook(SkinnyMessageOffHook):
252     name = 'On Hook'
253    
254 class SkinnyMessageCallState(Packet):
255     name='Skinny Call state message'
256     fields_desc = [ LEIntEnumField("state", 1, skinny_callstates),
257                     LEIntField("instance", 1),
258                     LEIntField("callid", 0),
259                     LEIntField("unknown1", 4),
260                     LEIntField("unknown2", 0),
261                     LEIntField("unknown3", 0) ]
262
263 class SkinnyMessageSoftKeyEvent(Packet):
264     name='Soft Key Event'
265     fields_desc = [ LEIntField("key", 0),
266                     LEIntField("instance", 1),
267                     LEIntField("callid", 0)]
268
269 class SkinnyMessageSetRinger(Packet):
270     name='Ring message'
271     fields_desc = [ LEIntEnumField("ring", 0x1, skinny_ring_type),
272                     LEIntField("unknown1", 0),
273                     LEIntField("unknown2", 0),
274                     LEIntField("unknown3", 0) ]
275
276 _skinny_tones = {
277     0x21: 'Inside dial tone',
278     0x22: 'xxx',
279     0x23: 'xxx',
280     0x24: 'Alerting tone',
281     0x25: 'Reorder Tone'
282     }
283
284 class SkinnyMessageStartTone(Packet):
285     name='Start tone'
286     fields_desc = [ LEIntEnumField("tone", 0x21, _skinny_tones),
287                     LEIntField("unknown1", 0),
288                     LEIntField("instance", 1),
289                     LEIntField("callid", 0)]
290
291 class SkinnyMessageStopTone(SkinnyMessageGeneric):
292     name='stop tone'
293     fields_desc = [ LEIntField("instance", 1),
294                     LEIntField("callid", 0)]
295
296    
297 class SkinnyMessageSpeakerMode(Packet):
298     name='Speaker mdoe'
299     fields_desc = [ LEIntEnumField("ring", 0x1, skinny_speaker_modes) ]
300
301 class SkinnyMessageSetLamp(Packet):
302     name='Lamp message (light of the phone)'
303     fields_desc = [ LEIntEnumField("stimulus", 0x5, skinny_stimulus),
304                     LEIntField("instance", 1),
305                     LEIntEnumField("mode", 2, skinny_lamp_mode) ]
306
307 class SkinnyMessageSoftKeyEvent(Packet):
308     name=' Call state message'
309     fields_desc = [ LEIntField("instance", 1),
310                     LEIntField("callid", 0),
311                     LEIntField("set", 0),
312                     LEIntField("map", 0xffff)]
313    
314 class SkinnyMessagePromptStatus(Packet):
315     name='Prompt status'
316     fields_desc = [ LEIntField("timeout", 0),
317                     StrFixedLenField("text", "\0"*32, 32),
318                     LEIntField("instance", 1),
319                     LEIntField("callid", 0)]
320
321 class SkinnyMessageCallPlane(Packet):
322     name='Activate/Desactivate Call Plane Message'
323     fields_desc = [ LEIntField("instance", 1)]
324    
325 class SkinnyMessageTimeDate(Packet):
326     name='Setting date and time'
327     fields_desc = [ SkinnyDateTimeField("settime", None),
328                     LEIntField("timestamp", 0) ]
329
330 class SkinnyMessageClearPromptStatus(Packet):
331     name='clear prompt status'
332     fields_desc = [ LEIntField("instance", 1),
333                     LEIntField("callid", 0)]
334
335 class SkinnyMessageKeypadButton(Packet):
336     name='keypad button'
337     fields_desc = [ LEIntField("key", 0),
338                     LEIntField("instance", 1),
339                     LEIntField("callid", 0)]
340
341 class SkinnyMessageDialedNumber(Packet):
342     name='dialed number'
343     fields_desc = [ StrFixedLenField("number", "1337", 24),
344                     LEIntField("instance", 1),
345                     LEIntField("callid", 0)]
346
347 _skinny_message_callinfo_restrictions = ['CallerName'
348                                          , 'CallerNumber'
349                                          , 'CalledName'
350                                          , 'CalledNumber'
351                                          , 'OriginalCalledName'
352                                          , 'OriginalCalledNumber'
353                                          , 'LastRedirectName'
354                                          , 'LastRedirectNumber'] + ['Bit%d' % i for i in range(8,15)]
355 class SkinnyMessageCallInfo(Packet):
356     name='call information'
357     fields_desc = [ StrFixedLenField("callername", "Jean Valjean", 40),
358                     StrFixedLenField("callernum", "1337", 24),
359                     StrFixedLenField("calledname", "Causette", 40),
360                     StrFixedLenField("callednum", "1034", 24),
361                     LEIntField("lineinstance", 1),
362                     LEIntField("callid", 0),
363                     StrFixedLenField("originalcalledname", "Causette", 40),
364                     StrFixedLenField("originalcallednum", "1034", 24),
365                     StrFixedLenField("lastredirectingname", "Causette", 40),
366                     StrFixedLenField("lastredirectingnum", "1034", 24),
367                     LEIntField("originalredirectreason", 0),
368                     LEIntField("lastredirectreason", 0),
369                     StrFixedLenField('voicemailboxG', '\0'*24, 24),
370                     StrFixedLenField('voicemailboxD', '\0'*24, 24),
371                     StrFixedLenField('originalvoicemailboxD', '\0'*24, 24),
372                     StrFixedLenField('lastvoicemailboxD', '\0'*24, 24),
373                     LEIntField('security', 0),
374                     FlagsField('restriction', 0, 16, _skinny_message_callinfo_restrictions),
375                     LEIntField('unknown', 0)]
376
377
378 class SkinnyRateField(LEIntField):
379     def i2repr(self, pkt, x):
380         if x is None:
381             x=0
382         return '%d ms/pkt' % x
383
384 _skinny_codecs = {
385     0x0: 'xxx',
386     0x1: 'xxx',
387     0x2: 'xxx',
388     0x3: 'xxx',
389     0x4: 'G711 ulaw 64k'
390     }
391
392 _skinny_echo = {
393     0x0: 'echo cancelation off',
394     0x1: 'echo cancelation on'
395     }
396
397 class SkinnyMessageOpenReceiveChannel(Packet):
398     name='open receive channel'
399     fields_desc = [LEIntField('conference', 0),
400                    LEIntField('passthru', 0),
401                    SkinnyRateField('rate', 20),
402                    LEIntEnumField('codec', 4, _skinny_codecs),
403                    LEIntEnumField('echo', 0, _skinny_echo),
404                    LEIntField('unknown1', 0),
405                    LEIntField('callid', 0)]
406
407     def guess_payload_class(self, p):
408         return Padding
409
410 _skinny_receive_channel_status = {
411     0x0: 'ok',
412     0x1: 'ko'
413     }
414
415 class SkinnyMessageOpenReceiveChannelAck(Packet):
416     name='open receive channel'
417     fields_desc = [LEIntEnumField('status', 0, _skinny_receive_channel_status),
418                    IPField('remote', '0.0.0.0'),
419                    LEIntField('port', RandShort()),
420                    LEIntField('passthru', 0),
421                    LEIntField('callid', 0)]
422
423 _skinny_silence = {
424     0x0: 'silence suppression off',
425     0x1: 'silence suppression on',
426     }
427
428 class SkinnyFramePerPacketField(LEIntField):
429     def i2repr(self, pkt, x):
430         if x is None:
431             x=0
432         return '%d frames/pkt' % x
433
434 class SkinnyMessageStartMediaTransmission(Packet):
435     name='start multimedia transmission'
436     fields_desc = [LEIntField('conference', 0),
437                    LEIntField('passthru', 0),
438                    IPField('remote', '0.0.0.0'),
439                    LEIntField('port', RandShort()),
440                    SkinnyRateField('rate', 20),
441                    LEIntEnumField('codec', 4, _skinny_codecs),
442                    LEIntField('precedence', 200),
443                    LEIntEnumField('silence', 0, _skinny_silence),
444                    SkinnyFramePerPacketField('maxframes', 0),
445                    LEIntField('unknown1', 0),
446                    LEIntField('callid', 0)]
447
448     def guess_payload_class(self, p):
449         return Padding
450    
451 class SkinnyMessageCloseReceiveChannel(Packet):
452     name='close receive channel'
453     fields_desc = [LEIntField('conference', 0),
454                    LEIntField('passthru', 0),
455                    IPField('remote', '0.0.0.0'),
456                    LEIntField('port', RandShort()),
457                    SkinnyRateField('rate', 20),
458                    LEIntEnumField('codec', 4, _skinny_codecs),
459                    LEIntField('precedence', 200),
460                    LEIntEnumField('silence', 0, _skinny_silence),
461                    LEIntField('callid', 0)]
462
463 class SkinnyMessageStopMultiMediaTransmission(Packet):
464     name='stop multimedia transmission'
465     fields_desc = [LEIntField('conference', 0),
466                    LEIntField('passthru', 0),
467                    LEIntField('callid', 0)]
468    
469 class Skinny(Packet):
470     name="Skinny"
471     fields_desc = [ LEIntField("len", None),
472                     LEIntField("res",0),
473                     LEIntEnumField("msg",0, skinny_messages) ]
474
475     def post_build(self, pkt, p):
476         if self.len is None:
477             l=len(p)+len(pkt)-8 # on compte pas les headers len et reserved
478             pkt=struct.pack('@I', l)+pkt[4:]
479         return pkt+p
480
481 # An helper
482 def get_cls(name, fallback_cls):
483     return globals().get(name, fallback_cls)
484     #return __builtin__.__dict__.get(name, fallback_cls)
485
486 for msgid,strcls in skinny_messages_cls.items():
487     cls=get_cls(strcls, SkinnyMessageGeneric)
488     bind_layers(Skinny, cls, {"msg": msgid})
489
490 bind_layers(TCP, Skinny, { "dport": 2000 } )
491 bind_layers(TCP, Skinny, { "sport": 2000 } )
492
493 if __name__ == "__main__":
494     interact(mydict=globals(),mybanner="Welcome to Skinny add-on")
495