Ticket #23: scapy-skinny.py

File scapy-skinny.py, 17.8 KB (added by nico@…, 3 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
21from scapy import *
22import __builtin__
23
24#####################################################################
25# Helpers and constants
26#####################################################################
27
28skinny_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
165skinny_callstates = {
166    0x1: "Off Hook",
167    0x2: "On Hook",
168    0x3: "Ring out",
169    0xc: "Proceeding",
170}
171
172
173skinny_ring_type = {
174    0x1: "Ring off"
175}
176
177skinny_speaker_modes = {
178    0x1: "Speaker on",
179    0x2: "Speaker off"
180}
181
182skinny_lamp_mode = {
183    0x1: "Off (?)",
184    0x2: "On",
185}
186
187skinny_stimulus = {
188    0x9: "Line"
189}
190
191
192############
193## Fields ##
194############
195
196class 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
237class SkinnyMessageGeneric(Packet):
238    name='Generic message'
239
240class SkinnyMessageKeepAlive(Packet):
241    name='keep alive'
242
243class SkinnyMessageKeepAliveAck(Packet):
244    name='keep alive ack'
245
246class SkinnyMessageOffHook(Packet):
247    name = 'Off Hook'
248    fields_desc = [ LEIntField("unknown1", 0),
249                    LEIntField("unknown2", 0),]
250       
251class SkinnyMessageOnHook(SkinnyMessageOffHook):
252    name = 'On Hook'
253   
254class 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
263class SkinnyMessageSoftKeyEvent(Packet):
264    name='Soft Key Event'
265    fields_desc = [ LEIntField("key", 0),
266                    LEIntField("instance", 1),
267                    LEIntField("callid", 0)]
268
269class 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
284class 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
291class SkinnyMessageStopTone(SkinnyMessageGeneric):
292    name='stop tone'
293    fields_desc = [ LEIntField("instance", 1),
294                    LEIntField("callid", 0)]
295
296   
297class SkinnyMessageSpeakerMode(Packet):
298    name='Speaker mdoe'
299    fields_desc = [ LEIntEnumField("ring", 0x1, skinny_speaker_modes) ]
300
301class 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
307class 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   
314class 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
321class SkinnyMessageCallPlane(Packet):
322    name='Activate/Desactivate Call Plane Message'
323    fields_desc = [ LEIntField("instance", 1)]
324   
325class SkinnyMessageTimeDate(Packet):
326    name='Setting date and time'
327    fields_desc = [ SkinnyDateTimeField("settime", None),
328                    LEIntField("timestamp", 0) ]
329
330class SkinnyMessageClearPromptStatus(Packet):
331    name='clear prompt status'
332    fields_desc = [ LEIntField("instance", 1),
333                    LEIntField("callid", 0)]
334
335class SkinnyMessageKeypadButton(Packet):
336    name='keypad button'
337    fields_desc = [ LEIntField("key", 0),
338                    LEIntField("instance", 1),
339                    LEIntField("callid", 0)]
340
341class 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)]
355class 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
378class 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
397class 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
415class 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
428class SkinnyFramePerPacketField(LEIntField):
429    def i2repr(self, pkt, x):
430        if x is None:
431            x=0
432        return '%d frames/pkt' % x
433
434class 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   
451class 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
463class SkinnyMessageStopMultiMediaTransmission(Packet):
464    name='stop multimedia transmission'
465    fields_desc = [LEIntField('conference', 0),
466                   LEIntField('passthru', 0),
467                   LEIntField('callid', 0)]
468   
469class 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
482def get_cls(name, fallback_cls):
483    return globals().get(name, fallback_cls)
484    #return __builtin__.__dict__.get(name, fallback_cls)
485
486for msgid,strcls in skinny_messages_cls.items():
487    cls=get_cls(strcls, SkinnyMessageGeneric)
488    bind_layers(Skinny, cls, {"msg": msgid})
489
490bind_layers(TCP, Skinny, { "dport": 2000 } )
491bind_layers(TCP, Skinny, { "sport": 2000 } )
492
493if __name__ == "__main__":
494    interact(mydict=globals(),mybanner="Welcome to Skinny add-on")