| ## This file is part of Scapy |
| ## See http://www.secdev.org/projects/scapy for more informations |
| ## Copyright (C) Jan Sebechlebsky <sebechlebskyjan@gmail.com> |
| ## This program is published under a GPLv2 license |
| |
| """ |
| PPTP (Point to Point Tunneling Protocol) |
| |
| [RFC 2637] |
| """ |
| |
| from scapy.packet import Packet, bind_layers |
| from scapy.layers.inet import TCP |
| from scapy.compat import * |
| from scapy.fields import ByteEnumField, FieldLenField, FlagsField, IntField, IntEnumField,\ |
| LenField, XIntField, ShortField, ShortEnumField, StrFixedLenField,\ |
| StrLenField, XShortField, XByteField |
| |
| _PPTP_MAGIC_COOKIE = 0x1a2b3c4d |
| |
| _PPTP_msg_type = {1: "Control Message", |
| 2: "Managemenent Message"} |
| |
| _PPTP_ctrl_msg_type = { # Control Connection Management |
| 1: "Start-Control-Connection-Request", |
| 2: "Start-Control-Connection-Reply", |
| 3: "Stop-Control-Connection-Request", |
| 4: "Stop-Control-Connection-Reply", |
| 5: "Echo-Request", |
| 6: "Echo-Reply", |
| # Call Management |
| 7: "Outgoing-Call-Request", |
| 8: "Outgoing-Call-Reply", |
| 9: "Incoming-Call-Request", |
| 10: "Incoming-Call-Reply", |
| 11: "Incoming-Call-Connected", |
| 12: "Call-Clear-Request", |
| 13: "Call-Disconnect-Notify", |
| # Error Reporting |
| 14: "WAN-Error-Notify", |
| # PPP Session Control |
| 15: "Set-Link-Info"} |
| |
| _PPTP_general_error_code = {0: "None", |
| 1: "Not-Connected", |
| 2: "Bad-Format", |
| 3: "Bad-Value", |
| 4: "No-Resource", |
| 5: "Bad-Call ID", |
| 6: "PAC-Error"} |
| |
| |
| class PPTP(Packet): |
| name = "PPTP" |
| fields_desc = [FieldLenField("len", None, fmt="H", length_of="data", |
| adjust=lambda p, x: x + 12), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 1, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| StrLenField("data", "",length_from=lambda p: p.len - 12)] |
| |
| registered_options = {} |
| |
| @classmethod |
| def register_variant(cls): |
| cls.registered_options[cls.ctrl_msg_type.default] = cls |
| |
| @classmethod |
| def dispatch_hook(cls, _pkt=None, *args, **kargs): |
| if _pkt: |
| o = orb(_pkt[9]) |
| return cls.registered_options.get(o, cls) |
| return cls |
| |
| |
| _PPTP_FRAMING_CAPABILITIES_FLAGS = ["Asynchronous Framing supported", |
| "Synchronous Framing supported"] |
| |
| _PPTP_BEARER_CAPABILITIES_FLAGS = ["Analog access supported", |
| "Digital access supported"] |
| |
| |
| class PPTPStartControlConnectionRequest(PPTP): |
| name = "PPTP Start Control Connection Request" |
| fields_desc = [LenField("len", 156), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 1, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| ShortField("protocol_version", 1), |
| XShortField("reserved_1", 0x0000), |
| FlagsField("framing_capabilities", 0, 32, |
| _PPTP_FRAMING_CAPABILITIES_FLAGS), |
| FlagsField("bearer_capabilities", 0, 32, |
| _PPTP_BEARER_CAPABILITIES_FLAGS), |
| ShortField("maximum_channels", 65535), |
| ShortField("firmware_revision", 256), |
| StrFixedLenField("host_name", "linux", 64), |
| StrFixedLenField("vendor_string", "", 64)] |
| |
| _PPTP_start_control_connection_result = {1: "OK", |
| 2: "General error", |
| 3: "Command channel already exists", |
| 4: "Not authorized", |
| 5: "Unsupported protocol version"} |
| |
| |
| class PPTPStartControlConnectionReply(PPTP): |
| name = "PPTP Start Control Connection Reply" |
| fields_desc = [LenField("len", 156), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 2, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| ShortField("protocol_version", 1), |
| ByteEnumField("result_code", 1, |
| _PPTP_start_control_connection_result), |
| ByteEnumField("error_code", 0, _PPTP_general_error_code), |
| FlagsField("framing_capabilities", 0, 32, |
| _PPTP_FRAMING_CAPABILITIES_FLAGS), |
| FlagsField("bearer_capabilities", 0, 32, |
| _PPTP_BEARER_CAPABILITIES_FLAGS), |
| ShortField("maximum_channels", 65535), |
| ShortField("firmware_revision", 256), |
| StrFixedLenField("host_name", "linux", 64), |
| StrFixedLenField("vendor_string", "", 64)] |
| |
| def answers(self, other): |
| return isinstance(other, PPTPStartControlConnectionRequest) |
| |
| |
| _PPTP_stop_control_connection_reason = {1: "None", |
| 2: "Stop-Protocol", |
| 3: "Stop-Local-Shutdown"} |
| |
| |
| class PPTPStopControlConnectionRequest(PPTP): |
| name = "PPTP Stop Control Connection Request" |
| fields_desc = [LenField("len", 16), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 3, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| ByteEnumField("reason", 1, |
| _PPTP_stop_control_connection_reason), |
| XByteField("reserved_1", 0x00), |
| XShortField("reserved_2", 0x0000)] |
| |
| _PPTP_stop_control_connection_result = {1: "OK", |
| 2: "General error"} |
| |
| |
| class PPTPStopControlConnectionReply(PPTP): |
| name = "PPTP Stop Control Connection Reply" |
| fields_desc = [LenField("len", 16), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 4, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| ByteEnumField("result_code", 1, |
| _PPTP_stop_control_connection_result), |
| ByteEnumField("error_code", 0, _PPTP_general_error_code), |
| XShortField("reserved_2", 0x0000)] |
| |
| def answers(self, other): |
| return isinstance(other, PPTPStopControlConnectionRequest) |
| |
| |
| class PPTPEchoRequest(PPTP): |
| name = "PPTP Echo Request" |
| fields_desc = [LenField("len", 16), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 5, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| IntField("identifier", None)] |
| |
| _PPTP_echo_result = {1: "OK", |
| 2: "General error"} |
| |
| |
| class PPTPEchoReply(PPTP): |
| name = "PPTP Echo Reply" |
| fields_desc = [LenField("len", 20), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 6, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| IntField("identifier", None), |
| ByteEnumField("result_code", 1, _PPTP_echo_result), |
| ByteEnumField("error_code", 0, _PPTP_general_error_code), |
| XShortField("reserved_1", 0x0000)] |
| |
| def answers(self, other): |
| return isinstance(other, PPTPEchoRequest) and other.identifier == self.identifier |
| |
| _PPTP_bearer_type = {1: "Analog channel", |
| 2: "Digital channel", |
| 3: "Any type of channel"} |
| |
| _PPTP_framing_type = {1: "Asynchronous framing", |
| 2: "Synchronous framing", |
| 3: "Any type of framing"} |
| |
| |
| class PPTPOutgoingCallRequest(PPTP): |
| name = "PPTP Outgoing Call Request" |
| fields_desc = [LenField("len", 168), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 7, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| ShortField("call_id", 1), |
| ShortField("call_serial_number", 0), |
| IntField("minimum_bps", 32768), |
| IntField("maximum_bps", 2147483648), |
| IntEnumField("bearer_type", 3, _PPTP_bearer_type), |
| IntEnumField("framing_type", 3, _PPTP_framing_type), |
| ShortField("pkt_window_size", 16), |
| ShortField("pkt_proc_delay", 0), |
| ShortField('phone_number_len', 0), |
| XShortField("reserved_1", 0x0000), |
| StrFixedLenField("phone_number", '', 64), |
| StrFixedLenField("subaddress", '', 64)] |
| |
| _PPTP_result_code = {1: "Connected", |
| 2: "General error", |
| 3: "No Carrier", |
| 4: "Busy", |
| 5: "No dial tone", |
| 6: "Time-out", |
| 7: "Do not accept"} |
| |
| |
| class PPTPOutgoingCallReply(PPTP): |
| name = "PPTP Outgoing Call Reply" |
| fields_desc = [LenField("len", 32), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 8, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| ShortField("call_id", 1), |
| ShortField("peer_call_id", 1), |
| ByteEnumField("result_code", 1, _PPTP_result_code), |
| ByteEnumField("error_code", 0, _PPTP_general_error_code), |
| ShortField("cause_code", 0), |
| IntField("connect_speed", 100000000), |
| ShortField("pkt_window_size", 16), |
| ShortField("pkt_proc_delay", 0), |
| IntField("channel_id", 0)] |
| |
| def answers(self, other): |
| return isinstance(other, PPTPOutgoingCallRequest) and other.call_id == self.peer_call_id |
| |
| |
| class PPTPIncomingCallRequest(PPTP): |
| name = "PPTP Incoming Call Request" |
| fields_desc = [LenField("len", 220), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 9, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| ShortField("call_id", 1), |
| ShortField("call_serial_number", 1), |
| IntEnumField("bearer_type", 3, _PPTP_bearer_type), |
| IntField("channel_id", 0), |
| ShortField("dialed_number_len", 0), |
| ShortField("dialing_number_len", 0), |
| StrFixedLenField("dialed_number", "", 64), |
| StrFixedLenField("dialing_number", "", 64), |
| StrFixedLenField("subaddress", "", 64)] |
| |
| |
| class PPTPIncomingCallReply(PPTP): |
| name = "PPTP Incoming Call Reply" |
| fields_desc = [LenField("len", 148), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 10, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| ShortField("call_id", 1), |
| ShortField("peer_call_id", 1), |
| ByteEnumField("result_code", 1, _PPTP_result_code), |
| ByteEnumField("error_code", 0, _PPTP_general_error_code), |
| ShortField("pkt_window_size", 64), |
| ShortField("pkt_transmit_delay", 0), |
| XShortField("reserved_1", 0x0000)] |
| |
| def answers(self, other): |
| return isinstance(other, PPTPIncomingCallRequest) and other.call_id == self.peer_call_id |
| |
| |
| class PPTPIncomingCallConnected(PPTP): |
| name = "PPTP Incoming Call Connected" |
| fields_desc = [LenField("len", 28), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 11, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| ShortField("peer_call_id", 1), |
| XShortField("reserved_1", 0x0000), |
| IntField("connect_speed", 100000000), |
| ShortField("pkt_window_size", 64), |
| ShortField("pkt_transmit_delay", 0), |
| IntEnumField("framing_type", 1, _PPTP_framing_type)] |
| |
| def answers(self, other): |
| return isinstance(other, PPTPIncomingCallReply) and other.call_id == self.peer_call_id |
| |
| |
| class PPTPCallClearRequest(PPTP): |
| name = "PPTP Call Clear Request" |
| fields_desc = [LenField("len", 16), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 12, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| ShortField("call_id", 1), |
| XShortField("reserved_1", 0x0000)] |
| |
| _PPTP_call_disconnect_result = {1: "Lost Carrier", |
| 2: "General error", |
| 3: "Admin Shutdown", |
| 4: "Request"} |
| |
| |
| class PPTPCallDisconnectNotify(PPTP): |
| name = "PPTP Call Disconnect Notify" |
| fields_desc = [LenField("len", 148), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 13, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| ShortField("call_id", 1), |
| ByteEnumField("result_code", 1, |
| _PPTP_call_disconnect_result), |
| ByteEnumField("error_code", 0, _PPTP_general_error_code), |
| ShortField("cause_code", 0), |
| XShortField("reserved_1", 0x0000), |
| StrFixedLenField("call_statistic", "", 128)] |
| |
| |
| class PPTPWANErrorNotify(PPTP): |
| name = "PPTP WAN Error Notify" |
| fields_desc = [LenField("len", 40), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 14, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| ShortField("peer_call_id", 1), |
| XShortField("reserved_1", 0x0000), |
| IntField("crc_errors", 0), |
| IntField("framing_errors", 0), |
| IntField("hardware_overruns", 0), |
| IntField("buffer_overruns", 0), |
| IntField("time_out_errors", 0), |
| IntField("alignment_errors", 0)] |
| |
| |
| class PPTPSetLinkInfo(PPTP): |
| name = "PPTP Set Link Info" |
| fields_desc = [LenField("len", 24), |
| ShortEnumField("type", 1, _PPTP_msg_type), |
| XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), |
| ShortEnumField("ctrl_msg_type", 15, _PPTP_ctrl_msg_type), |
| XShortField("reserved_0", 0x0000), |
| ShortField("peer_call_id", 1), |
| XShortField("reserved_1", 0x0000), |
| XIntField("send_accm", 0x00000000), |
| XIntField("receive_accm", 0x00000000)] |
| |
| bind_layers(TCP, PPTP, sport=1723) |
| bind_layers(TCP, PPTP, dport=1723) |