PageRenderTime 48ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/pentest/voiper/protocol_logic/sip_parser.py

https://github.com/sullivanmatt/Raspberry-Pwn
Python | 192 lines | 111 code | 23 blank | 58 comment | 21 complexity | f0e1204c1e505c242353cdbeae36035e MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, MPL-2.0-no-copyleft-exception, GPL-2.0, GPL-3.0
  1. '''
  2. This file is part of VoIPER.
  3. VoIPER is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. VoIPER is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with VoIPER. If not, see <http://www.gnu.org/licenses/>.
  13. Copyright 2008, http://www.unprotectedhex.com
  14. Contact: nnp@unprotectedhex.com
  15. '''
  16. import re
  17. BRANCH = 1
  18. FROM = 2
  19. TO = 3
  20. RCODE = 4
  21. TOTAG = 5
  22. CALLID = 6
  23. CSEQNUM = 7
  24. FROMTAG = 8
  25. RURI = 9
  26. VIA = 10
  27. WWW_AUTHEN_NONCE = 11
  28. WWW_AUTHEN_REALM = 12
  29. CONTACT = 13
  30. r_1XX = 99
  31. r_2XX = 98
  32. r_3XX = 97
  33. r_4XX = 96
  34. r_5XX = 95
  35. r_6XX = 94
  36. r_ACK = 89
  37. r_INVITE = 88
  38. r_REGISTER = 87
  39. r_OPTIONS = 86
  40. r_CANCEL = 85
  41. r_UNKNOWN = 69
  42. r_SEND = 68
  43. r_401 = 59
  44. r_180 = 58
  45. class SIPParser:
  46. def __init__(self):
  47. '''
  48. A class to implement all the functionality required to parse the
  49. relevant fields from a SIP message
  50. '''
  51. self.regex_dict = {BRANCH : r'^Via.*?branch\s*?=\s*?(?P<target>[\d\w-]+)',
  52. FROMTAG : r'^From.*?;tag=(?P<target>[\w\d-]+)',
  53. CALLID : r'^Call-ID\s*?:\s*(?P<target>\S+)',
  54. CSEQNUM : r'^CSeq\s*?:\s*(?P<target>\d+)',
  55. TO : r'^To\s*?:\s*(?P<target>.+)\r\n',
  56. FROM : r'^From\s*?:\s*(?P<target>.+)\r\n',
  57. RCODE : r'^(?P<target>INVITE|CANCEL|OPTIONS|REGISTER|SIP/2\.0 \d{3}|ACK)',
  58. RURI : r'(INVITE|CANCEL|OPTIONS|REGISTER)\s+(?P<target>.+?)\s+SIP/2.0',
  59. VIA : r'^Via\s*?:\s*(?P<target>.+)\r\n',
  60. WWW_AUTHEN_NONCE : r'^WWW-Authenticate\s*?:\s*Digest.+nonce="(?P<target>[\w\d\.-]+)"',
  61. WWW_AUTHEN_REALM : r'^WWW-Authenticate\s*?:\s*Digest.+realm="(?P<target>[\w\d\.@]+)"',
  62. CONTACT : r'^Contact\s*?:\s*?(?P<target><sip:[\w\d]+@[\w\d\.]+(:[\d]+)?(;transport=(udp|tcp))?>)',
  63. }
  64. self.regex_c = {}
  65. # compile those filthy regexs ;)
  66. for r_name in self.regex_dict.keys():
  67. r_val = self.regex_dict[r_name]
  68. r = re.compile(r_val, re.IGNORECASE | re.MULTILINE)
  69. self.regex_c[r_name] = r
  70. def parse(self, data, fields=None):
  71. '''
  72. Parses the provided data and extracts the relevant fields and their
  73. values into a dictionary
  74. @type data: String
  75. @param data: The SIP message to be parsed
  76. @type field: List
  77. @param field: A list of the fields to parse identified by the constants
  78. defined in the __init__ of this class. If None all fields are parsed
  79. @rtype: Dictionary
  80. @return: A dictionary of fields to and their associated values
  81. '''
  82. if fields == None:
  83. fields = self.regex_c.keys()
  84. res_dict = {}
  85. for r_name in fields:
  86. val = self.regex_c[r_name].search(data)
  87. if val and len(val.groups()) != 0:
  88. res_dict[r_name] = val.group('target')
  89. return self.normalise_rcodes(res_dict)
  90. def normalise_rcodes(self, data_dict):
  91. '''
  92. Method to change textual response codes to one of the constants
  93. defined in the __init__ method
  94. @type data_dict: Dictionary
  95. @param data_dict: A dictionary of message fields to their values
  96. parsed from a SIP message
  97. @rtype: Dictionary
  98. @return: A dictionary where the response code strings have been
  99. converted to constants defined in this class
  100. '''
  101. if data_dict.has_key(RCODE):
  102. r_code = data_dict[RCODE]
  103. if r_code.upper() == 'ACK':
  104. r_code = r_ACK
  105. elif r_code.upper() == 'INVITE':
  106. r_code = r_INVITE
  107. elif r_code.upper() == 'REGISTER':
  108. r_code = r_REGISTER
  109. elif r_code.upper() == 'OPTIONS':
  110. r_code = r_OPTIONS
  111. elif r_code.upper() == 'CANCEL':
  112. r_code = r_CANCEL
  113. elif r_code.find('SIP/2.0') != -1:
  114. data = r_code.split(" ")
  115. r_code = data[1]
  116. if r_code[0] == '1':
  117. r_code = r_1XX
  118. elif r_code[0] == '2':
  119. r_code = r_2XX
  120. elif r_code[0] == '3':
  121. r_code = r_3XX
  122. elif r_code[0] == '4':
  123. r_code = r_4XX
  124. elif r_code[0] == '5':
  125. r_code = r_5XX
  126. elif r_code[0] == '6':
  127. r_code = r_6XX
  128. elif r_code == '401':
  129. r_code = r_401
  130. elif r_code == '180':
  131. r_code = r_180
  132. else:
  133. r_code = r_UNKNOWN
  134. data_dict[RCODE] = r_code
  135. return data_dict
  136. def denormalise_rcode(self, r_code):
  137. '''
  138. Convert the int representation of a rcode to its textual value
  139. @type r_code: Integer
  140. @param r_code: The rcode to convert
  141. @rtype: String
  142. @return: The textual value corresponding to the given rcode
  143. '''
  144. r_dict = { 99 : 'r_1XX',
  145. 98 : 'r_2XX',
  146. 97 : 'r_3XX',
  147. 96 : 'r_4XX',
  148. 95 : 'r_5XX',
  149. 94 : 'r_6XX',
  150. 89 : 'r_ACK',
  151. 88 : 'r_INVITE',
  152. 87 : 'r_REGISTER',
  153. 86 : 'r_OPTIONS',
  154. 85 : 'r_CANCEL',
  155. 68 : 'r_UNKNOWN',
  156. 59 : 'r_401',
  157. 58 : 'r_180',
  158. }
  159. return r_dict[r_code]