1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Jan Sebechlebsky <sebechlebskyjan@gmail.com>
5
6"""
7PPTP (Point to Point Tunneling Protocol)
8
9[RFC 2637]
10"""
11
12from scapy.packet import Packet, bind_layers
13from scapy.layers.inet import TCP
14from scapy.compat import orb
15from scapy.fields import ByteEnumField, FieldLenField, FlagsField, IntField, \
16 IntEnumField, LenField, XIntField, ShortField, ShortEnumField, \
17 StrFixedLenField, StrLenField, XShortField, XByteField
18
19_PPTP_MAGIC_COOKIE = 0x1a2b3c4d
20
21_PPTP_msg_type = {1: "Control Message",
22 2: "Managemenent Message"}
23
24_PPTP_ctrl_msg_type = { # Control Connection Management
25 1: "Start-Control-Connection-Request",
26 2: "Start-Control-Connection-Reply",
27 3: "Stop-Control-Connection-Request",
28 4: "Stop-Control-Connection-Reply",
29 5: "Echo-Request",
30 6: "Echo-Reply",
31 # Call Management
32 7: "Outgoing-Call-Request",
33 8: "Outgoing-Call-Reply",
34 9: "Incoming-Call-Request",
35 10: "Incoming-Call-Reply",
36 11: "Incoming-Call-Connected",
37 12: "Call-Clear-Request",
38 13: "Call-Disconnect-Notify",
39 # Error Reporting
40 14: "WAN-Error-Notify",
41 # PPP Session Control
42 15: "Set-Link-Info"}
43
44_PPTP_general_error_code = {0: "None",
45 1: "Not-Connected",
46 2: "Bad-Format",
47 3: "Bad-Value",
48 4: "No-Resource",
49 5: "Bad-Call ID",
50 6: "PAC-Error"}
51
52
53class PPTP(Packet):
54 name = "PPTP"
55 fields_desc = [FieldLenField("len", None, fmt="H", length_of="data",
56 adjust=lambda p, x: x + 12),
57 ShortEnumField("type", 1, _PPTP_msg_type),
58 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
59 ShortEnumField("ctrl_msg_type", 1, _PPTP_ctrl_msg_type),
60 XShortField("reserved_0", 0x0000),
61 StrLenField("data", "", length_from=lambda p: p.len - 12)]
62
63 registered_options = {}
64
65 @classmethod
66 def register_variant(cls):
67 cls.registered_options[cls.ctrl_msg_type.default] = cls
68
69 @classmethod
70 def dispatch_hook(cls, _pkt=None, *args, **kargs):
71 if _pkt:
72 o = orb(_pkt[9])
73 return cls.registered_options.get(o, cls)
74 return cls
75
76
77_PPTP_FRAMING_CAPABILITIES_FLAGS = ["Asynchronous Framing supported",
78 "Synchronous Framing supported"]
79
80_PPTP_BEARER_CAPABILITIES_FLAGS = ["Analog access supported",
81 "Digital access supported"]
82
83
84class PPTPStartControlConnectionRequest(PPTP):
85 name = "PPTP Start Control Connection Request"
86 fields_desc = [LenField("len", 156),
87 ShortEnumField("type", 1, _PPTP_msg_type),
88 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
89 ShortEnumField("ctrl_msg_type", 1, _PPTP_ctrl_msg_type),
90 XShortField("reserved_0", 0x0000),
91 ShortField("protocol_version", 0x0100),
92 XShortField("reserved_1", 0x0000),
93 FlagsField("framing_capabilities", 0, 32,
94 _PPTP_FRAMING_CAPABILITIES_FLAGS),
95 FlagsField("bearer_capabilities", 0, 32,
96 _PPTP_BEARER_CAPABILITIES_FLAGS),
97 ShortField("maximum_channels", 65535),
98 ShortField("firmware_revision", 256),
99 StrFixedLenField("host_name", "linux", 64),
100 StrFixedLenField("vendor_string", "", 64)]
101
102
103_PPTP_start_control_connection_result = {1: "OK",
104 2: "General error",
105 3: "Command channel already exists",
106 4: "Not authorized",
107 5: "Unsupported protocol version"}
108
109
110class PPTPStartControlConnectionReply(PPTP):
111 name = "PPTP Start Control Connection Reply"
112 fields_desc = [LenField("len", 156),
113 ShortEnumField("type", 1, _PPTP_msg_type),
114 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
115 ShortEnumField("ctrl_msg_type", 2, _PPTP_ctrl_msg_type),
116 XShortField("reserved_0", 0x0000),
117 ShortField("protocol_version", 0x0100),
118 ByteEnumField("result_code", 1,
119 _PPTP_start_control_connection_result),
120 ByteEnumField("error_code", 0, _PPTP_general_error_code),
121 FlagsField("framing_capabilities", 0, 32,
122 _PPTP_FRAMING_CAPABILITIES_FLAGS),
123 FlagsField("bearer_capabilities", 0, 32,
124 _PPTP_BEARER_CAPABILITIES_FLAGS),
125 ShortField("maximum_channels", 65535),
126 ShortField("firmware_revision", 256),
127 StrFixedLenField("host_name", "linux", 64),
128 StrFixedLenField("vendor_string", "", 64)]
129
130 def answers(self, other):
131 return isinstance(other, PPTPStartControlConnectionRequest)
132
133
134_PPTP_stop_control_connection_reason = {1: "None",
135 2: "Stop-Protocol",
136 3: "Stop-Local-Shutdown"}
137
138
139class PPTPStopControlConnectionRequest(PPTP):
140 name = "PPTP Stop Control Connection Request"
141 fields_desc = [LenField("len", 16),
142 ShortEnumField("type", 1, _PPTP_msg_type),
143 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
144 ShortEnumField("ctrl_msg_type", 3, _PPTP_ctrl_msg_type),
145 XShortField("reserved_0", 0x0000),
146 ByteEnumField("reason", 1,
147 _PPTP_stop_control_connection_reason),
148 XByteField("reserved_1", 0x00),
149 XShortField("reserved_2", 0x0000)]
150
151
152_PPTP_stop_control_connection_result = {1: "OK",
153 2: "General error"}
154
155
156class PPTPStopControlConnectionReply(PPTP):
157 name = "PPTP Stop Control Connection Reply"
158 fields_desc = [LenField("len", 16),
159 ShortEnumField("type", 1, _PPTP_msg_type),
160 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
161 ShortEnumField("ctrl_msg_type", 4, _PPTP_ctrl_msg_type),
162 XShortField("reserved_0", 0x0000),
163 ByteEnumField("result_code", 1,
164 _PPTP_stop_control_connection_result),
165 ByteEnumField("error_code", 0, _PPTP_general_error_code),
166 XShortField("reserved_2", 0x0000)]
167
168 def answers(self, other):
169 return isinstance(other, PPTPStopControlConnectionRequest)
170
171
172class PPTPEchoRequest(PPTP):
173 name = "PPTP Echo Request"
174 fields_desc = [LenField("len", 16),
175 ShortEnumField("type", 1, _PPTP_msg_type),
176 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
177 ShortEnumField("ctrl_msg_type", 5, _PPTP_ctrl_msg_type),
178 XShortField("reserved_0", 0x0000),
179 IntField("identifier", None)]
180
181
182_PPTP_echo_result = {1: "OK",
183 2: "General error"}
184
185
186class PPTPEchoReply(PPTP):
187 name = "PPTP Echo Reply"
188 fields_desc = [LenField("len", 20),
189 ShortEnumField("type", 1, _PPTP_msg_type),
190 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
191 ShortEnumField("ctrl_msg_type", 6, _PPTP_ctrl_msg_type),
192 XShortField("reserved_0", 0x0000),
193 IntField("identifier", None),
194 ByteEnumField("result_code", 1, _PPTP_echo_result),
195 ByteEnumField("error_code", 0, _PPTP_general_error_code),
196 XShortField("reserved_1", 0x0000)]
197
198 def answers(self, other):
199 return isinstance(other, PPTPEchoRequest) and other.identifier == self.identifier # noqa: E501
200
201
202_PPTP_bearer_type = {1: "Analog channel",
203 2: "Digital channel",
204 3: "Any type of channel"}
205
206_PPTP_framing_type = {1: "Asynchronous framing",
207 2: "Synchronous framing",
208 3: "Any type of framing"}
209
210
211class PPTPOutgoingCallRequest(PPTP):
212 name = "PPTP Outgoing Call Request"
213 fields_desc = [LenField("len", 168),
214 ShortEnumField("type", 1, _PPTP_msg_type),
215 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
216 ShortEnumField("ctrl_msg_type", 7, _PPTP_ctrl_msg_type),
217 XShortField("reserved_0", 0x0000),
218 ShortField("call_id", 1),
219 ShortField("call_serial_number", 0),
220 IntField("minimum_bps", 32768),
221 IntField("maximum_bps", 2147483648),
222 IntEnumField("bearer_type", 3, _PPTP_bearer_type),
223 IntEnumField("framing_type", 3, _PPTP_framing_type),
224 ShortField("pkt_window_size", 16),
225 ShortField("pkt_proc_delay", 0),
226 ShortField('phone_number_len', 0),
227 XShortField("reserved_1", 0x0000),
228 StrFixedLenField("phone_number", '', 64),
229 StrFixedLenField("subaddress", '', 64)]
230
231
232_PPTP_result_code = {1: "Connected",
233 2: "General error",
234 3: "No Carrier",
235 4: "Busy",
236 5: "No dial tone",
237 6: "Time-out",
238 7: "Do not accept"}
239
240
241class PPTPOutgoingCallReply(PPTP):
242 name = "PPTP Outgoing Call Reply"
243 fields_desc = [LenField("len", 32),
244 ShortEnumField("type", 1, _PPTP_msg_type),
245 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
246 ShortEnumField("ctrl_msg_type", 8, _PPTP_ctrl_msg_type),
247 XShortField("reserved_0", 0x0000),
248 ShortField("call_id", 1),
249 ShortField("peer_call_id", 1),
250 ByteEnumField("result_code", 1, _PPTP_result_code),
251 ByteEnumField("error_code", 0, _PPTP_general_error_code),
252 ShortField("cause_code", 0),
253 IntField("connect_speed", 100000000),
254 ShortField("pkt_window_size", 16),
255 ShortField("pkt_proc_delay", 0),
256 IntField("channel_id", 0)]
257
258 def answers(self, other):
259 return isinstance(other, PPTPOutgoingCallRequest) and other.call_id == self.peer_call_id # noqa: E501
260
261
262class PPTPIncomingCallRequest(PPTP):
263 name = "PPTP Incoming Call Request"
264 fields_desc = [LenField("len", 220),
265 ShortEnumField("type", 1, _PPTP_msg_type),
266 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
267 ShortEnumField("ctrl_msg_type", 9, _PPTP_ctrl_msg_type),
268 XShortField("reserved_0", 0x0000),
269 ShortField("call_id", 1),
270 ShortField("call_serial_number", 1),
271 IntEnumField("bearer_type", 3, _PPTP_bearer_type),
272 IntField("channel_id", 0),
273 ShortField("dialed_number_len", 0),
274 ShortField("dialing_number_len", 0),
275 StrFixedLenField("dialed_number", "", 64),
276 StrFixedLenField("dialing_number", "", 64),
277 StrFixedLenField("subaddress", "", 64)]
278
279
280class PPTPIncomingCallReply(PPTP):
281 name = "PPTP Incoming Call Reply"
282 fields_desc = [LenField("len", 148),
283 ShortEnumField("type", 1, _PPTP_msg_type),
284 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
285 ShortEnumField("ctrl_msg_type", 10, _PPTP_ctrl_msg_type),
286 XShortField("reserved_0", 0x0000),
287 ShortField("call_id", 1),
288 ShortField("peer_call_id", 1),
289 ByteEnumField("result_code", 1, _PPTP_result_code),
290 ByteEnumField("error_code", 0, _PPTP_general_error_code),
291 ShortField("pkt_window_size", 64),
292 ShortField("pkt_transmit_delay", 0),
293 XShortField("reserved_1", 0x0000)]
294
295 def answers(self, other):
296 return isinstance(other, PPTPIncomingCallRequest) and other.call_id == self.peer_call_id # noqa: E501
297
298
299class PPTPIncomingCallConnected(PPTP):
300 name = "PPTP Incoming Call Connected"
301 fields_desc = [LenField("len", 28),
302 ShortEnumField("type", 1, _PPTP_msg_type),
303 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
304 ShortEnumField("ctrl_msg_type", 11, _PPTP_ctrl_msg_type),
305 XShortField("reserved_0", 0x0000),
306 ShortField("peer_call_id", 1),
307 XShortField("reserved_1", 0x0000),
308 IntField("connect_speed", 100000000),
309 ShortField("pkt_window_size", 64),
310 ShortField("pkt_transmit_delay", 0),
311 IntEnumField("framing_type", 1, _PPTP_framing_type)]
312
313 def answers(self, other):
314 return isinstance(other, PPTPIncomingCallReply) and other.call_id == self.peer_call_id # noqa: E501
315
316
317class PPTPCallClearRequest(PPTP):
318 name = "PPTP Call Clear Request"
319 fields_desc = [LenField("len", 16),
320 ShortEnumField("type", 1, _PPTP_msg_type),
321 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
322 ShortEnumField("ctrl_msg_type", 12, _PPTP_ctrl_msg_type),
323 XShortField("reserved_0", 0x0000),
324 ShortField("call_id", 1),
325 XShortField("reserved_1", 0x0000)]
326
327
328_PPTP_call_disconnect_result = {1: "Lost Carrier",
329 2: "General error",
330 3: "Admin Shutdown",
331 4: "Request"}
332
333
334class PPTPCallDisconnectNotify(PPTP):
335 name = "PPTP Call Disconnect Notify"
336 fields_desc = [LenField("len", 148),
337 ShortEnumField("type", 1, _PPTP_msg_type),
338 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
339 ShortEnumField("ctrl_msg_type", 13, _PPTP_ctrl_msg_type),
340 XShortField("reserved_0", 0x0000),
341 ShortField("call_id", 1),
342 ByteEnumField("result_code", 1,
343 _PPTP_call_disconnect_result),
344 ByteEnumField("error_code", 0, _PPTP_general_error_code),
345 ShortField("cause_code", 0),
346 XShortField("reserved_1", 0x0000),
347 StrFixedLenField("call_statistic", "", 128)]
348
349
350class PPTPWANErrorNotify(PPTP):
351 name = "PPTP WAN Error Notify"
352 fields_desc = [LenField("len", 40),
353 ShortEnumField("type", 1, _PPTP_msg_type),
354 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
355 ShortEnumField("ctrl_msg_type", 14, _PPTP_ctrl_msg_type),
356 XShortField("reserved_0", 0x0000),
357 ShortField("peer_call_id", 1),
358 XShortField("reserved_1", 0x0000),
359 IntField("crc_errors", 0),
360 IntField("framing_errors", 0),
361 IntField("hardware_overruns", 0),
362 IntField("buffer_overruns", 0),
363 IntField("time_out_errors", 0),
364 IntField("alignment_errors", 0)]
365
366
367class PPTPSetLinkInfo(PPTP):
368 name = "PPTP Set Link Info"
369 fields_desc = [LenField("len", 24),
370 ShortEnumField("type", 1, _PPTP_msg_type),
371 XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
372 ShortEnumField("ctrl_msg_type", 15, _PPTP_ctrl_msg_type),
373 XShortField("reserved_0", 0x0000),
374 ShortField("peer_call_id", 1),
375 XShortField("reserved_1", 0x0000),
376 XIntField("send_accm", 0x00000000),
377 XIntField("receive_accm", 0x00000000)]
378
379
380bind_layers(TCP, PPTP, sport=1723)
381bind_layers(TCP, PPTP, dport=1723)