Home | History | Annotate | Download | only in contrib
      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 
     26 from __future__ import absolute_import
     27 from scapy.packet import *
     28 from scapy.fields import *
     29 from scapy.layers.inet import TCP
     30 from scapy.modules.six.moves import range
     31 
     32 #####################################################################
     33 # Helpers and constants
     34 #####################################################################
     35 
     36 skinny_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 
    173 skinny_callstates = {
    174     0x1: "Off Hook",
    175     0x2: "On Hook",
    176     0x3: "Ring out",
    177     0xc: "Proceeding",
    178 }
    179 
    180 
    181 skinny_ring_type = {
    182     0x1: "Ring off"
    183 }
    184 
    185 skinny_speaker_modes = {
    186     0x1: "Speaker on",
    187     0x2: "Speaker off"
    188 }
    189 
    190 skinny_lamp_mode = {
    191     0x1: "Off (?)",
    192     0x2: "On",
    193 }
    194 
    195 skinny_stimulus = {
    196     0x9: "Line"
    197 }
    198 
    199 
    200 ############
    201 ## Fields ##
    202 ############
    203 
    204 class 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 
    245 class SkinnyMessageGeneric(Packet):
    246     name='Generic message'
    247 
    248 class SkinnyMessageKeepAlive(Packet):
    249     name='keep alive'
    250 
    251 class SkinnyMessageKeepAliveAck(Packet):
    252     name='keep alive ack'
    253 
    254 class SkinnyMessageOffHook(Packet):
    255     name = 'Off Hook'
    256     fields_desc = [ LEIntField("unknown1", 0),
    257                     LEIntField("unknown2", 0),]
    258         
    259 class SkinnyMessageOnHook(SkinnyMessageOffHook):
    260     name = 'On Hook'
    261     
    262 class 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 
    271 class SkinnyMessageSoftKeyEvent(Packet):
    272     name='Soft Key Event'
    273     fields_desc = [ LEIntField("key", 0),
    274                     LEIntField("instance", 1),
    275                     LEIntField("callid", 0)]
    276 
    277 class 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 
    292 class 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 
    299 class SkinnyMessageStopTone(SkinnyMessageGeneric):
    300     name='stop tone'
    301     fields_desc = [ LEIntField("instance", 1),
    302                     LEIntField("callid", 0)]
    303 
    304     
    305 class SkinnyMessageSpeakerMode(Packet):
    306     name='Speaker mdoe'
    307     fields_desc = [ LEIntEnumField("ring", 0x1, skinny_speaker_modes) ]
    308 
    309 class 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 
    315 class 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     
    322 class 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 
    329 class SkinnyMessageCallPlane(Packet):
    330     name='Activate/Desactivate Call Plane Message'
    331     fields_desc = [ LEIntField("instance", 1)]
    332     
    333 class SkinnyMessageTimeDate(Packet):
    334     name='Setting date and time'
    335     fields_desc = [ SkinnyDateTimeField("settime", None),
    336                     LEIntField("timestamp", 0) ]
    337 
    338 class SkinnyMessageClearPromptStatus(Packet):
    339     name='clear prompt status'
    340     fields_desc = [ LEIntField("instance", 1),
    341                     LEIntField("callid", 0)]
    342 
    343 class SkinnyMessageKeypadButton(Packet):
    344     name='keypad button'
    345     fields_desc = [ LEIntField("key", 0),
    346                     LEIntField("instance", 1),
    347                     LEIntField("callid", 0)]
    348 
    349 class 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)]
    363 class 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 
    386 class 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 
    405 class 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 
    423 class 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 
    436 class SkinnyFramePerPacketField(LEIntField):
    437     def i2repr(self, pkt, x):
    438         if x is None:
    439             x=0
    440         return '%d frames/pkt' % x
    441 
    442 class 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     
    459 class 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 
    471 class SkinnyMessageStopMultiMediaTransmission(Packet):
    472     name='stop multimedia transmission'
    473     fields_desc = [LEIntField('conference', 0),
    474                    LEIntField('passthru', 0),
    475                    LEIntField('callid', 0)]
    476     
    477 class 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 
    490 def get_cls(name, fallback_cls):
    491     return globals().get(name, fallback_cls)
    492     #return __builtin__.__dict__.get(name, fallback_cls)
    493 
    494 for msgid,strcls in skinny_messages_cls.items():
    495     cls=get_cls(strcls, SkinnyMessageGeneric)
    496     bind_layers(Skinny, cls, {"msg": msgid})
    497 
    498 bind_layers(TCP, Skinny, { "dport": 2000 } )
    499 bind_layers(TCP, Skinny, { "sport": 2000 } )
    500 
    501 if __name__ == "__main__":
    502     from scapy.main import interact
    503     interact(mydict=globals(),mybanner="Welcome to Skinny add-on")
    504 
    505