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