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