# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Jan Sebechlebsky # 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 orb 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", 0x0100), 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", 0x0100), 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 # noqa: E501 _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 # noqa: E501 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 # noqa: E501 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 # noqa: E501 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)