1 |
|
|
/* $OpenBSD: pptp_ctrl.c,v 1.11 2016/04/16 18:32:29 krw Exp $ */ |
2 |
|
|
|
3 |
|
|
/*- |
4 |
|
|
* Copyright (c) 2009 Internet Initiative Japan Inc. |
5 |
|
|
* All rights reserved. |
6 |
|
|
* |
7 |
|
|
* Redistribution and use in source and binary forms, with or without |
8 |
|
|
* modification, are permitted provided that the following conditions |
9 |
|
|
* are met: |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer in the |
14 |
|
|
* documentation and/or other materials provided with the distribution. |
15 |
|
|
* |
16 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
17 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
18 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
19 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
20 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
21 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
22 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
23 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
24 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
25 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
26 |
|
|
* SUCH DAMAGE. |
27 |
|
|
*/ |
28 |
|
|
/**@file |
29 |
|
|
* PPTP(RFC 2637) control connection implementation. |
30 |
|
|
* currently it only support PAC part |
31 |
|
|
*/ |
32 |
|
|
/* $Id: pptp_ctrl.c,v 1.11 2016/04/16 18:32:29 krw Exp $ */ |
33 |
|
|
#include <sys/types.h> |
34 |
|
|
#include <sys/socket.h> |
35 |
|
|
#include <netinet/in.h> |
36 |
|
|
#include <stdio.h> |
37 |
|
|
#include <stdarg.h> |
38 |
|
|
#include <stdlib.h> |
39 |
|
|
#include <netdb.h> |
40 |
|
|
#include <unistd.h> |
41 |
|
|
#include <syslog.h> |
42 |
|
|
#include <time.h> |
43 |
|
|
#include <fcntl.h> |
44 |
|
|
#include <errno.h> |
45 |
|
|
#include <string.h> |
46 |
|
|
#include <event.h> |
47 |
|
|
|
48 |
|
|
#include "bytebuf.h" |
49 |
|
|
#include "debugutil.h" |
50 |
|
|
#include "hash.h" |
51 |
|
|
#include "slist.h" |
52 |
|
|
#include "time_utils.h" |
53 |
|
|
|
54 |
|
|
#include "version.h" |
55 |
|
|
|
56 |
|
|
#include "pptp.h" |
57 |
|
|
#include "pptp_local.h" |
58 |
|
|
#include "pptp_subr.h" |
59 |
|
|
|
60 |
|
|
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) |
61 |
|
|
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) |
62 |
|
|
|
63 |
|
|
/* periods of pptp_ctrl_timeout */ |
64 |
|
|
#define PPTP_CTRL_TIMEOUT_IVAL_SEC 2 |
65 |
|
|
|
66 |
|
|
#ifdef PPTP_CTRL_DEBUG |
67 |
|
|
#define PPTP_CTRL_ASSERT(x) ASSERT(x) |
68 |
|
|
#define PPTP_CTRL_DBG(x) pptp_ctrl_log x |
69 |
|
|
#else |
70 |
|
|
#define PPTP_CTRL_ASSERT(x) |
71 |
|
|
#define PPTP_CTRL_DBG(x) |
72 |
|
|
#endif |
73 |
|
|
|
74 |
|
|
static unsigned pptp_ctrl_seqno = 0; |
75 |
|
|
|
76 |
|
|
static void pptp_ctrl_log (pptp_ctrl *, int, const char *, ...) __printflike(3,4); |
77 |
|
|
static void pptp_ctrl_timeout (int, short, void *); |
78 |
|
|
static void pptp_ctrl_reset_timeout (pptp_ctrl *); |
79 |
|
|
static void pptp_ctrl_io_event (int, short, void *); |
80 |
|
|
static void pptp_ctrl_set_io_event (pptp_ctrl *); |
81 |
|
|
static int pptp_ctrl_output_flush (pptp_ctrl *); |
82 |
|
|
static void pptp_ctrl_SCCRx_string (struct pptp_scc *, u_char *, int); |
83 |
|
|
static int pptp_ctrl_recv_SCCRQ (pptp_ctrl *, u_char *, int); |
84 |
|
|
static int pptp_ctrl_recv_StopCCRP (pptp_ctrl *, u_char *, int); |
85 |
|
|
static int pptp_ctrl_send_StopCCRQ (pptp_ctrl *, int); |
86 |
|
|
static int pptp_ctrl_recv_StopCCRQ (pptp_ctrl *, u_char *, int); |
87 |
|
|
static int pptp_ctrl_send_StopCCRP (pptp_ctrl *, int, int); |
88 |
|
|
static int pptp_ctrl_send_SCCRP (pptp_ctrl *, int, int); |
89 |
|
|
static void pptp_ctrl_send_CDN (pptp_ctrl *, int, int, int, const char *); |
90 |
|
|
static void pptp_ctrl_process_echo_req (pptp_ctrl *, u_char *, int); |
91 |
|
|
static int pptp_ctrl_recv_echo_rep (pptp_ctrl *, u_char *, int); |
92 |
|
|
static void pptp_ctrl_send_echo_req (pptp_ctrl *); |
93 |
|
|
static int pptp_ctrl_input (pptp_ctrl *, u_char *, int); |
94 |
|
|
static int pptp_ctrl_call_input (pptp_ctrl *, int, u_char *, int); |
95 |
|
|
static const char *pptp_ctrl_state_string (int); |
96 |
|
|
static void pptp_ctrl_fini(pptp_ctrl *); |
97 |
|
|
|
98 |
|
|
/* |
99 |
|
|
* pptp_ctrl instance operation functions |
100 |
|
|
*/ |
101 |
|
|
pptp_ctrl * |
102 |
|
|
pptp_ctrl_create(void) |
103 |
|
|
{ |
104 |
|
|
pptp_ctrl *_this; |
105 |
|
|
|
106 |
|
|
if ((_this = malloc(sizeof(pptp_ctrl))) == NULL) |
107 |
|
|
return NULL; |
108 |
|
|
|
109 |
|
|
return _this; |
110 |
|
|
} |
111 |
|
|
|
112 |
|
|
int |
113 |
|
|
pptp_ctrl_init(pptp_ctrl *_this) |
114 |
|
|
{ |
115 |
|
|
time_t curr_time; |
116 |
|
|
|
117 |
|
|
PPTP_CTRL_ASSERT(_this != NULL); |
118 |
|
|
curr_time = get_monosec(); |
119 |
|
|
memset(_this, 0, sizeof(pptp_ctrl)); |
120 |
|
|
_this->id = pptp_ctrl_seqno++; |
121 |
|
|
_this->sock = -1; |
122 |
|
|
|
123 |
|
|
if ((_this->recv_buf = bytebuffer_create(PPTP_BUFSIZ)) == NULL) { |
124 |
|
|
pptp_ctrl_log(_this, LOG_ERR, "bytebuffer_create() failed at " |
125 |
|
|
"%s(): %m", __func__); |
126 |
|
|
goto fail; |
127 |
|
|
} |
128 |
|
|
if ((_this->send_buf = bytebuffer_create(PPTP_BUFSIZ)) == NULL) { |
129 |
|
|
pptp_ctrl_log(_this, LOG_ERR, "bytebuffer_create() failed at " |
130 |
|
|
"%s(): %m", __func__); |
131 |
|
|
goto fail; |
132 |
|
|
} |
133 |
|
|
_this->last_rcv_ctrl = curr_time; |
134 |
|
|
_this->last_snd_ctrl = curr_time; |
135 |
|
|
_this->echo_seq = arc4random(); |
136 |
|
|
_this->echo_interval = PPTP_CTRL_DEFAULT_ECHO_INTERVAL; |
137 |
|
|
_this->echo_timeout = PPTP_CTRL_DEFAULT_ECHO_TIMEOUT; |
138 |
|
|
slist_init(&_this->call_list); |
139 |
|
|
evtimer_set(&_this->ev_timer, pptp_ctrl_timeout, _this); |
140 |
|
|
|
141 |
|
|
return 0; |
142 |
|
|
fail: |
143 |
|
|
return 1; |
144 |
|
|
} |
145 |
|
|
|
146 |
|
|
int |
147 |
|
|
pptp_ctrl_start(pptp_ctrl *_this) |
148 |
|
|
{ |
149 |
|
|
int ival; |
150 |
|
|
char hbuf0[NI_MAXHOST], sbuf0[NI_MAXSERV]; |
151 |
|
|
char hbuf1[NI_MAXHOST], sbuf1[NI_MAXSERV]; |
152 |
|
|
struct sockaddr_storage sock; |
153 |
|
|
socklen_t socklen; |
154 |
|
|
|
155 |
|
|
PPTP_CTRL_ASSERT(_this != NULL); |
156 |
|
|
PPTP_CTRL_ASSERT(_this->sock >= 0); |
157 |
|
|
|
158 |
|
|
/* convert address to strings for logging */ |
159 |
|
|
strlcpy(hbuf0, "<unknown>", sizeof(hbuf0)); |
160 |
|
|
strlcpy(sbuf0, "<unknown>", sizeof(sbuf0)); |
161 |
|
|
strlcpy(hbuf1, "<unknown>", sizeof(hbuf1)); |
162 |
|
|
strlcpy(sbuf1, "<unknown>", sizeof(sbuf1)); |
163 |
|
|
if (getnameinfo((struct sockaddr *)&_this->peer, _this->peer.ss_len, |
164 |
|
|
hbuf0, sizeof(hbuf0), sbuf0, sizeof(sbuf0), |
165 |
|
|
NI_NUMERICHOST | NI_NUMERICSERV) != 0) { |
166 |
|
|
pptp_ctrl_log(_this, LOG_ERR, |
167 |
|
|
"getnameinfo() failed at %s(): %m", __func__); |
168 |
|
|
} |
169 |
|
|
socklen = sizeof(sock); |
170 |
|
|
if (getsockname(_this->sock, (struct sockaddr *)&sock, &socklen) != 0) { |
171 |
|
|
pptp_ctrl_log(_this, LOG_ERR, |
172 |
|
|
"getsockname() failed at %s(): %m", __func__); |
173 |
|
|
goto fail; |
174 |
|
|
} |
175 |
|
|
if (getnameinfo((struct sockaddr *)&sock, sock.ss_len, hbuf1, |
176 |
|
|
sizeof(hbuf1), sbuf1, sizeof(sbuf1), |
177 |
|
|
NI_NUMERICHOST | NI_NUMERICSERV) != 0) { |
178 |
|
|
pptp_ctrl_log(_this, LOG_ERR, |
179 |
|
|
"getnameinfo() failed at %s(): %m", __func__); |
180 |
|
|
} |
181 |
|
|
pptp_ctrl_log(_this, LOG_INFO, "Starting peer=%s:%s/tcp " |
182 |
|
|
"sock=%s:%s/tcp", hbuf0, sbuf0, hbuf1, sbuf1); |
183 |
|
|
|
184 |
|
|
if ((ival = fcntl(_this->sock, F_GETFL)) < 0) { |
185 |
|
|
pptp_ctrl_log(_this, LOG_ERR, |
186 |
|
|
"fcntl(F_GET_FL) failed at %s(): %m", __func__); |
187 |
|
|
goto fail; |
188 |
|
|
} else if (fcntl(_this->sock, F_SETFL, ival | O_NONBLOCK) < 0) { |
189 |
|
|
pptp_ctrl_log(_this, LOG_ERR, |
190 |
|
|
"fcntl(F_SET_FL) failed at %s(): %m", __func__); |
191 |
|
|
goto fail; |
192 |
|
|
} |
193 |
|
|
pptp_ctrl_set_io_event(_this); |
194 |
|
|
pptp_ctrl_reset_timeout(_this); |
195 |
|
|
|
196 |
|
|
return 0; |
197 |
|
|
fail: |
198 |
|
|
return 1; |
199 |
|
|
} |
200 |
|
|
|
201 |
|
|
/* Timer */ |
202 |
|
|
static void |
203 |
|
|
pptp_ctrl_timeout(int fd, short event, void *ctx) |
204 |
|
|
{ |
205 |
|
|
int i; |
206 |
|
|
pptp_call *call; |
207 |
|
|
pptp_ctrl *_this; |
208 |
|
|
time_t last, curr_time; |
209 |
|
|
|
210 |
|
|
_this = ctx; |
211 |
|
|
curr_time = get_monosec(); |
212 |
|
|
|
213 |
|
|
PPTP_CTRL_DBG((_this, DEBUG_LEVEL_3, "enter %s()", __func__)); |
214 |
|
|
/* clean up call */ |
215 |
|
|
i = 0; |
216 |
|
|
while (i < slist_length(&_this->call_list)) { |
217 |
|
|
call = slist_get(&_this->call_list, i); |
218 |
|
|
if (call->state == PPTP_CALL_STATE_CLEANUP_WAIT && |
219 |
|
|
curr_time - call->last_io > PPTP_CALL_CLEANUP_WAIT_TIME) { |
220 |
|
|
pptp_call_stop(call); |
221 |
|
|
pptp_call_destroy(call); |
222 |
|
|
slist_remove(&_this->call_list, i); |
223 |
|
|
} else |
224 |
|
|
i++; |
225 |
|
|
} |
226 |
|
|
|
227 |
|
|
/* State machine: Timeout */ |
228 |
|
|
switch (_this->state) { |
229 |
|
|
default: |
230 |
|
|
case PPTP_CTRL_STATE_WAIT_CTRL_REPLY: |
231 |
|
|
case PPTP_CTRL_STATE_IDLE: |
232 |
|
|
if (curr_time - _this->last_rcv_ctrl > PPTPD_IDLE_TIMEOUT) { |
233 |
|
|
pptp_ctrl_log(_this, LOG_ERR, |
234 |
|
|
"Timeout in state %s", |
235 |
|
|
pptp_ctrl_state_string(_this->state)); |
236 |
|
|
pptp_ctrl_fini(_this); |
237 |
|
|
return; |
238 |
|
|
} |
239 |
|
|
break; |
240 |
|
|
case PPTP_CTRL_STATE_ESTABLISHED: |
241 |
|
|
last = MAXIMUM(_this->last_rcv_ctrl, _this->last_snd_ctrl); |
242 |
|
|
|
243 |
|
|
if (curr_time - _this->last_rcv_ctrl |
244 |
|
|
>= _this->echo_interval + _this->echo_timeout) { |
245 |
|
|
pptp_ctrl_log(_this, LOG_INFO, |
246 |
|
|
"Timeout waiting for echo reply"); |
247 |
|
|
pptp_ctrl_fini(_this); |
248 |
|
|
return; |
249 |
|
|
} |
250 |
|
|
if (curr_time - last >= _this->echo_interval) { |
251 |
|
|
PPTP_CTRL_DBG((_this, LOG_DEBUG, "Echo")); |
252 |
|
|
_this->echo_seq++; |
253 |
|
|
pptp_ctrl_send_echo_req(_this); |
254 |
|
|
} |
255 |
|
|
break; |
256 |
|
|
case PPTP_CTRL_STATE_WAIT_STOP_REPLY: |
257 |
|
|
if (curr_time - _this->last_snd_ctrl > |
258 |
|
|
PPTP_CTRL_StopCCRP_WAIT_TIME) { |
259 |
|
|
pptp_ctrl_log(_this, LOG_WARNING, |
260 |
|
|
"Timeout waiting for StopCCRP"); |
261 |
|
|
pptp_ctrl_fini(_this); |
262 |
|
|
return; |
263 |
|
|
} |
264 |
|
|
break; |
265 |
|
|
case PPTP_CTRL_STATE_DISPOSING: |
266 |
|
|
pptp_ctrl_fini(_this); |
267 |
|
|
return; |
268 |
|
|
} |
269 |
|
|
pptp_ctrl_reset_timeout(_this); |
270 |
|
|
} |
271 |
|
|
|
272 |
|
|
static void |
273 |
|
|
pptp_ctrl_reset_timeout(pptp_ctrl *_this) |
274 |
|
|
{ |
275 |
|
|
struct timeval tv; |
276 |
|
|
|
277 |
|
|
switch (_this->state) { |
278 |
|
|
case PPTP_CTRL_STATE_DISPOSING: |
279 |
|
|
/* call back immediately */ |
280 |
|
|
timerclear(&tv); |
281 |
|
|
break; |
282 |
|
|
default: |
283 |
|
|
tv.tv_sec = PPTP_CTRL_TIMEOUT_IVAL_SEC; |
284 |
|
|
tv.tv_usec = 0; |
285 |
|
|
break; |
286 |
|
|
} |
287 |
|
|
evtimer_add(&_this->ev_timer, &tv); |
288 |
|
|
} |
289 |
|
|
|
290 |
|
|
/** |
291 |
|
|
* Terminate PPTP control connection |
292 |
|
|
* @result The value for Stop-Control-Connection-Request(StopCCRQ) result. |
293 |
|
|
This function will not sent StopCCRQ when the value == 0 and |
294 |
|
|
the specification does not require to sent it. |
295 |
|
|
* @see ::#PPTP_StopCCRQ_REASON_STOP_PROTOCOL |
296 |
|
|
* @see ::#PPTP_StopCCRQ_REASON_STOP_LOCAL_SHUTDOWN |
297 |
|
|
*/ |
298 |
|
|
void |
299 |
|
|
pptp_ctrl_stop(pptp_ctrl *_this, int result) |
300 |
|
|
{ |
301 |
|
|
int i; |
302 |
|
|
pptp_call *call; |
303 |
|
|
|
304 |
|
|
switch (_this->state) { |
305 |
|
|
case PPTP_CTRL_STATE_WAIT_STOP_REPLY: |
306 |
|
|
/* waiting responce. */ |
307 |
|
|
/* this state will timeout by pptp_ctrl_timeout */ |
308 |
|
|
break; |
309 |
|
|
case PPTP_CTRL_STATE_ESTABLISHED: |
310 |
|
|
if (result != 0) { |
311 |
|
|
for (i = 0; i < slist_length(&_this->call_list); i++) { |
312 |
|
|
call = slist_get(&_this->call_list, i); |
313 |
|
|
pptp_call_disconnect(call, |
314 |
|
|
PPTP_CDN_RESULT_ADMIN_SHUTDOWN, 0, NULL); |
315 |
|
|
} |
316 |
|
|
pptp_ctrl_send_StopCCRQ(_this, result); |
317 |
|
|
_this->state = PPTP_CTRL_STATE_WAIT_STOP_REPLY; |
318 |
|
|
break; |
319 |
|
|
} |
320 |
|
|
/* FALLTHROUGH */ |
321 |
|
|
default: |
322 |
|
|
pptp_ctrl_fini(_this); |
323 |
|
|
} |
324 |
|
|
return; |
325 |
|
|
} |
326 |
|
|
|
327 |
|
|
|
328 |
|
|
/* finish PPTP control */ |
329 |
|
|
static void |
330 |
|
|
pptp_ctrl_fini(pptp_ctrl *_this) |
331 |
|
|
{ |
332 |
|
|
pptp_call *call; |
333 |
|
|
|
334 |
|
|
PPTP_CTRL_ASSERT(_this != NULL); |
335 |
|
|
|
336 |
|
|
if (_this->sock >= 0) { |
337 |
|
|
event_del(&_this->ev_sock); |
338 |
|
|
close(_this->sock); |
339 |
|
|
_this->sock = -1; |
340 |
|
|
} |
341 |
|
|
for (slist_itr_first(&_this->call_list); |
342 |
|
|
slist_itr_has_next(&_this->call_list);) { |
343 |
|
|
call = slist_itr_next(&_this->call_list); |
344 |
|
|
pptp_call_stop(call); |
345 |
|
|
pptp_call_destroy(call); |
346 |
|
|
slist_itr_remove(&_this->call_list); |
347 |
|
|
} |
348 |
|
|
|
349 |
|
|
if (_this->on_io_event != 0) { |
350 |
|
|
/* |
351 |
|
|
* as the complete terminate process needs complicated |
352 |
|
|
* exception handling, do partially at here. |
353 |
|
|
* rest of part will be handled by timer-event-handler. |
354 |
|
|
*/ |
355 |
|
|
PPTP_CTRL_DBG((_this, LOG_DEBUG, "Disposing")); |
356 |
|
|
_this->state = PPTP_CTRL_STATE_DISPOSING; |
357 |
|
|
pptp_ctrl_reset_timeout(_this); |
358 |
|
|
return; |
359 |
|
|
} |
360 |
|
|
|
361 |
|
|
evtimer_del(&_this->ev_timer); |
362 |
|
|
slist_fini(&_this->call_list); |
363 |
|
|
|
364 |
|
|
pptp_ctrl_log (_this, LOG_NOTICE, "logtype=Finished"); |
365 |
|
|
|
366 |
|
|
/* disable _this */ |
367 |
|
|
pptpd_ctrl_finished_notify(_this->pptpd, _this); |
368 |
|
|
} |
369 |
|
|
|
370 |
|
|
/* free PPTP control context */ |
371 |
|
|
void |
372 |
|
|
pptp_ctrl_destroy(pptp_ctrl *_this) |
373 |
|
|
{ |
374 |
|
|
if (_this->send_buf != NULL) { |
375 |
|
|
bytebuffer_destroy(_this->send_buf); |
376 |
|
|
_this->send_buf = NULL; |
377 |
|
|
} |
378 |
|
|
if (_this->recv_buf != NULL) { |
379 |
|
|
bytebuffer_destroy(_this->recv_buf); |
380 |
|
|
_this->recv_buf = NULL; |
381 |
|
|
} |
382 |
|
|
free(_this); |
383 |
|
|
} |
384 |
|
|
|
385 |
|
|
/* |
386 |
|
|
* network I/O |
387 |
|
|
*/ |
388 |
|
|
/* I/O event dispather */ |
389 |
|
|
static void |
390 |
|
|
pptp_ctrl_io_event(int fd, short evmask, void *ctx) |
391 |
|
|
{ |
392 |
|
|
int sz, lpkt, hdrlen; |
393 |
|
|
u_char *pkt; |
394 |
|
|
pptp_ctrl *_this; |
395 |
|
|
|
396 |
|
|
_this = ctx; |
397 |
|
|
PPTP_CTRL_ASSERT(_this != NULL); |
398 |
|
|
|
399 |
|
|
_this->on_io_event = 1; |
400 |
|
|
if ((evmask & EV_WRITE) != 0) { |
401 |
|
|
if (pptp_ctrl_output_flush(_this) != 0 || |
402 |
|
|
_this->state == PPTP_CTRL_STATE_DISPOSING) |
403 |
|
|
goto fail; |
404 |
|
|
_this->send_ready = 1; |
405 |
|
|
} |
406 |
|
|
if ((evmask & EV_READ) != 0) { |
407 |
|
|
sz = read(_this->sock, bytebuffer_pointer(_this->recv_buf), |
408 |
|
|
bytebuffer_remaining(_this->recv_buf)); |
409 |
|
|
if (sz <= 0) { |
410 |
|
|
if (sz == 0 || errno == ECONNRESET) { |
411 |
|
|
pptp_ctrl_log(_this, LOG_INFO, |
412 |
|
|
"Connection closed by foreign host"); |
413 |
|
|
pptp_ctrl_fini(_this); |
414 |
|
|
goto fail; |
415 |
|
|
} else if (errno != EAGAIN && errno != EINTR) { |
416 |
|
|
pptp_ctrl_log(_this, LOG_INFO, |
417 |
|
|
"read() failed at %s(): %m", __func__); |
418 |
|
|
pptp_ctrl_fini(_this); |
419 |
|
|
goto fail; |
420 |
|
|
} |
421 |
|
|
} |
422 |
|
|
bytebuffer_put(_this->recv_buf, BYTEBUFFER_PUT_DIRECT, sz); |
423 |
|
|
bytebuffer_flip(_this->recv_buf); |
424 |
|
|
|
425 |
|
|
for (;;) { |
426 |
|
|
pkt = bytebuffer_pointer(_this->recv_buf); |
427 |
|
|
lpkt = bytebuffer_remaining(_this->recv_buf); |
428 |
|
|
if (pkt == NULL || |
429 |
|
|
lpkt < sizeof(struct pptp_ctrl_header)) |
430 |
|
|
break; /* read again */ |
431 |
|
|
|
432 |
|
|
hdrlen = pkt[0] << 8 | pkt[1]; |
433 |
|
|
if (lpkt < hdrlen) |
434 |
|
|
break; /* read again */ |
435 |
|
|
|
436 |
|
|
bytebuffer_get(_this->recv_buf, NULL, hdrlen); |
437 |
|
|
if (pptp_ctrl_input(_this, pkt, hdrlen) != 0 || |
438 |
|
|
_this->state == PPTP_CTRL_STATE_DISPOSING) { |
439 |
|
|
bytebuffer_compact(_this->recv_buf); |
440 |
|
|
goto fail; |
441 |
|
|
} |
442 |
|
|
} |
443 |
|
|
bytebuffer_compact(_this->recv_buf); |
444 |
|
|
} |
445 |
|
|
if (pptp_ctrl_output_flush(_this) != 0) |
446 |
|
|
goto fail; |
447 |
|
|
pptp_ctrl_set_io_event(_this); |
448 |
|
|
fail: |
449 |
|
|
_this->on_io_event = 0; |
450 |
|
|
} |
451 |
|
|
|
452 |
|
|
|
453 |
|
|
/* set i/o event mask */ |
454 |
|
|
static void |
455 |
|
|
pptp_ctrl_set_io_event(pptp_ctrl *_this) |
456 |
|
|
{ |
457 |
|
|
int evmask; |
458 |
|
|
|
459 |
|
|
PPTP_CTRL_ASSERT(_this != NULL); |
460 |
|
|
PPTP_CTRL_ASSERT(_this->sock >= 0); |
461 |
|
|
|
462 |
|
|
evmask = 0; |
463 |
|
|
if (bytebuffer_remaining(_this->recv_buf) > 128) |
464 |
|
|
evmask |= EV_READ; |
465 |
|
|
if (_this->send_ready == 0) |
466 |
|
|
evmask |= EV_WRITE; |
467 |
|
|
|
468 |
|
|
event_del(&_this->ev_sock); |
469 |
|
|
if (evmask != 0) { |
470 |
|
|
event_set(&_this->ev_sock, _this->sock, evmask, |
471 |
|
|
pptp_ctrl_io_event, _this); |
472 |
|
|
event_add(&_this->ev_sock, NULL); |
473 |
|
|
} |
474 |
|
|
} |
475 |
|
|
|
476 |
|
|
/** |
477 |
|
|
* Output PPTP control packet |
478 |
|
|
* @param pkt pointer to packet buffer. |
479 |
|
|
* when it was appended by _this-.send_buf using bytebuffer, |
480 |
|
|
* specify NULL. |
481 |
|
|
* @param lpkt packet length |
482 |
|
|
*/ |
483 |
|
|
void |
484 |
|
|
pptp_ctrl_output(pptp_ctrl *_this, u_char *pkt, int lpkt) |
485 |
|
|
{ |
486 |
|
|
PPTP_CTRL_ASSERT(_this != NULL); |
487 |
|
|
PPTP_CTRL_ASSERT(lpkt > 0); |
488 |
|
|
|
489 |
|
|
/* just put the packet into the buffer now. send it later */ |
490 |
|
|
bytebuffer_put(_this->send_buf, pkt, lpkt); |
491 |
|
|
|
492 |
|
|
if (_this->on_io_event != 0) { |
493 |
|
|
/* |
494 |
|
|
* pptp_ctrl_output_flush() will be called by the end of |
495 |
|
|
* the I/O event handler. |
496 |
|
|
*/ |
497 |
|
|
} else { |
498 |
|
|
/* |
499 |
|
|
* When this function is called by other than I/O event handler, |
500 |
|
|
* we need to call pptp_ctrl_output_flush(). However if we do |
501 |
|
|
* it here, then we need to consider the situation |
502 |
|
|
* 'flush => write failure => finalize'. The situation requires |
503 |
|
|
* the caller function to handle the exception and causes |
504 |
|
|
* complication. So we call pptp_ctrl_output_flush() by the |
505 |
|
|
* the next send ready event. |
506 |
|
|
*/ |
507 |
|
|
_this->send_ready = 0; /* clear 'send ready' */ |
508 |
|
|
pptp_ctrl_set_io_event(_this); /* wait 'send ready */ |
509 |
|
|
} |
510 |
|
|
|
511 |
|
|
return; |
512 |
|
|
} |
513 |
|
|
|
514 |
|
|
/* Send Stop-Control-Connection-Request */ |
515 |
|
|
|
516 |
|
|
/* flush output packet */ |
517 |
|
|
static int |
518 |
|
|
pptp_ctrl_output_flush(pptp_ctrl *_this) |
519 |
|
|
{ |
520 |
|
|
int sz; |
521 |
|
|
time_t curr_time; |
522 |
|
|
|
523 |
|
|
curr_time = get_monosec(); |
524 |
|
|
|
525 |
|
|
if (bytebuffer_position(_this->send_buf) <= 0) |
526 |
|
|
return 0; /* nothing to write */ |
527 |
|
|
if (_this->send_ready == 0) { |
528 |
|
|
pptp_ctrl_set_io_event(_this); |
529 |
|
|
return 0; /* not ready to write */ |
530 |
|
|
} |
531 |
|
|
|
532 |
|
|
bytebuffer_flip(_this->send_buf); |
533 |
|
|
|
534 |
|
|
if (PPTP_CTRL_CONF(_this)->ctrl_out_pktdump != 0) { |
535 |
|
|
pptp_ctrl_log(_this, LOG_DEBUG, "PPTP Control output packet"); |
536 |
|
|
show_hd(debug_get_debugfp(), |
537 |
|
|
bytebuffer_pointer(_this->send_buf), |
538 |
|
|
bytebuffer_remaining(_this->send_buf)); |
539 |
|
|
} |
540 |
|
|
if ((sz = write(_this->sock, bytebuffer_pointer(_this->send_buf), |
541 |
|
|
bytebuffer_remaining(_this->send_buf))) < 0) { |
542 |
|
|
pptp_ctrl_log(_this, LOG_ERR, "write to socket failed: %m"); |
543 |
|
|
pptp_ctrl_fini(_this); |
544 |
|
|
|
545 |
|
|
return 1; |
546 |
|
|
} |
547 |
|
|
_this->last_snd_ctrl = curr_time; |
548 |
|
|
bytebuffer_get(_this->send_buf, NULL, sz); |
549 |
|
|
bytebuffer_compact(_this->send_buf); |
550 |
|
|
_this->send_ready = 0; |
551 |
|
|
|
552 |
|
|
return 0; |
553 |
|
|
} |
554 |
|
|
|
555 |
|
|
/* convert Start-Control-Connection-{Request,Reply} packet to strings */ |
556 |
|
|
static void |
557 |
|
|
pptp_ctrl_SCCRx_string(struct pptp_scc *scc, u_char *buf, int lbuf) |
558 |
|
|
{ |
559 |
|
|
char buf1[128], buf2[128], buf3[128]; |
560 |
|
|
|
561 |
|
|
/* sanity check */ |
562 |
|
|
strlcpy(buf1, scc->host_name, sizeof(buf1)); |
563 |
|
|
strlcpy(buf2, scc->vendor_string, sizeof(buf2)); |
564 |
|
|
|
565 |
|
|
if (scc->result_code != 0) |
566 |
|
|
snprintf(buf3, sizeof(buf3), "result=%d error=%d ", |
567 |
|
|
scc->result_code, scc->error_code); |
568 |
|
|
else |
569 |
|
|
buf3[0] = '\0'; |
570 |
|
|
|
571 |
|
|
snprintf(buf, lbuf, |
572 |
|
|
"protocol_version=%d.%d %sframing=%s bearer=%s max_channels=%d " |
573 |
|
|
"firmware_revision=%d(0x%04x) host_name=\"%s\" " |
574 |
|
|
"vendor_string=\"%s\"", |
575 |
|
|
scc->protocol_version >> 8, scc->protocol_version & 0xff, buf3, |
576 |
|
|
pptp_framing_string(scc->framing_caps), |
577 |
|
|
pptp_bearer_string(scc->bearer_caps), scc->max_channels, |
578 |
|
|
scc->firmware_revision, scc->firmware_revision, buf1, buf2); |
579 |
|
|
} |
580 |
|
|
|
581 |
|
|
/* receive Start-Control-Connection-Request */ |
582 |
|
|
static int |
583 |
|
|
pptp_ctrl_recv_SCCRQ(pptp_ctrl *_this, u_char *pkt, int lpkt) |
584 |
|
|
{ |
585 |
|
|
char logbuf[512]; |
586 |
|
|
struct pptp_scc *scc; |
587 |
|
|
|
588 |
|
|
/* sanity check */ |
589 |
|
|
if (lpkt < sizeof(struct pptp_scc)) { |
590 |
|
|
pptp_ctrl_log(_this, LOG_ERR, "Received bad SCCRQ: packet too " |
591 |
|
|
"short: %d < %d", lpkt, (int)sizeof(struct pptp_scc)); |
592 |
|
|
return 1; |
593 |
|
|
} |
594 |
|
|
scc = (struct pptp_scc *)pkt; |
595 |
|
|
|
596 |
|
|
scc->protocol_version = ntohs(scc->protocol_version); |
597 |
|
|
scc->framing_caps = htonl(scc->framing_caps); |
598 |
|
|
scc->bearer_caps = htonl(scc->bearer_caps); |
599 |
|
|
scc->max_channels = htons(scc->max_channels); |
600 |
|
|
scc->firmware_revision = htons(scc->firmware_revision); |
601 |
|
|
|
602 |
|
|
/* check protocol version */ |
603 |
|
|
if (scc->protocol_version != PPTP_RFC_2637_VERSION) { |
604 |
|
|
pptp_ctrl_log(_this, LOG_ERR, "Received bad SCCRQ: " |
605 |
|
|
"unknown protocol version %d", scc->protocol_version); |
606 |
|
|
return 1; |
607 |
|
|
} |
608 |
|
|
|
609 |
|
|
pptp_ctrl_SCCRx_string(scc, logbuf, sizeof(logbuf)); |
610 |
|
|
pptp_ctrl_log(_this, LOG_INFO, "RecvSCCRQ %s", logbuf); |
611 |
|
|
|
612 |
|
|
return 0; |
613 |
|
|
} |
614 |
|
|
|
615 |
|
|
/* Receive Stop-Control-Connection-Reply */ |
616 |
|
|
static int |
617 |
|
|
pptp_ctrl_recv_StopCCRP(pptp_ctrl *_this, u_char *pkt, int lpkt) |
618 |
|
|
{ |
619 |
|
|
struct pptp_stop_ccrp *stop_ccrp; |
620 |
|
|
|
621 |
|
|
if (lpkt < sizeof(struct pptp_stop_ccrp)) { |
622 |
|
|
pptp_ctrl_log(_this, LOG_ERR, "Received bad StopCCRP: packet " |
623 |
|
|
"too short: %d < %d", lpkt, |
624 |
|
|
(int)sizeof(struct pptp_stop_ccrp)); |
625 |
|
|
return 1; |
626 |
|
|
} |
627 |
|
|
stop_ccrp = (struct pptp_stop_ccrp *)pkt; |
628 |
|
|
|
629 |
|
|
pptp_ctrl_log(_this, LOG_INFO, "RecvStopCCRP reason=%s(%u)", |
630 |
|
|
pptp_StopCCRP_result_string(stop_ccrp->result), stop_ccrp->result); |
631 |
|
|
|
632 |
|
|
return 0; |
633 |
|
|
} |
634 |
|
|
|
635 |
|
|
static int |
636 |
|
|
pptp_ctrl_send_StopCCRQ(pptp_ctrl *_this, int reason) |
637 |
|
|
{ |
638 |
|
|
int lpkt; |
639 |
|
|
struct pptp_stop_ccrq *stop_ccrq; |
640 |
|
|
|
641 |
|
|
stop_ccrq = bytebuffer_pointer(_this->send_buf); |
642 |
|
|
lpkt = bytebuffer_remaining(_this->send_buf); |
643 |
|
|
if (lpkt < sizeof(struct pptp_stop_ccrq)) { |
644 |
|
|
pptp_ctrl_log(_this, LOG_ERR, |
645 |
|
|
"SendCCRP failed: No buffer space available"); |
646 |
|
|
return -1; |
647 |
|
|
} |
648 |
|
|
memset(stop_ccrq, 0, sizeof(struct pptp_stop_ccrq)); |
649 |
|
|
|
650 |
|
|
pptp_init_header(&stop_ccrq->header, sizeof(struct pptp_stop_ccrq), |
651 |
|
|
PPTP_CTRL_MES_CODE_StopCCRQ); |
652 |
|
|
|
653 |
|
|
stop_ccrq->reason = reason; |
654 |
|
|
|
655 |
|
|
pptp_ctrl_log(_this, LOG_INFO, "SendStopCCRQ reason=%s(%u)", |
656 |
|
|
pptp_StopCCRQ_reason_string(stop_ccrq->reason), stop_ccrq->reason); |
657 |
|
|
|
658 |
|
|
pptp_ctrl_output(_this, NULL, sizeof(struct pptp_stop_ccrq)); |
659 |
|
|
|
660 |
|
|
return 0; |
661 |
|
|
} |
662 |
|
|
|
663 |
|
|
/* Receive Stop-Control-Connection-Request */ |
664 |
|
|
static int |
665 |
|
|
pptp_ctrl_recv_StopCCRQ(pptp_ctrl *_this, u_char *pkt, int lpkt) |
666 |
|
|
{ |
667 |
|
|
struct pptp_stop_ccrq *stop_ccrq; |
668 |
|
|
|
669 |
|
|
if (lpkt < sizeof(struct pptp_stop_ccrq)) { |
670 |
|
|
pptp_ctrl_log(_this, LOG_ERR, "Received bad StopCCRQ: packet " |
671 |
|
|
"too short: %d < %d", lpkt, |
672 |
|
|
(int)sizeof(struct pptp_stop_ccrq)); |
673 |
|
|
return 1; |
674 |
|
|
} |
675 |
|
|
stop_ccrq = (struct pptp_stop_ccrq *)pkt; |
676 |
|
|
|
677 |
|
|
pptp_ctrl_log(_this, LOG_INFO, "RecvStopCCRQ reason=%s(%u)", |
678 |
|
|
pptp_StopCCRQ_reason_string(stop_ccrq->reason), stop_ccrq->reason); |
679 |
|
|
|
680 |
|
|
return 0; |
681 |
|
|
} |
682 |
|
|
|
683 |
|
|
/* Send Stop-Control-Connection-Reply */ |
684 |
|
|
static int |
685 |
|
|
pptp_ctrl_send_StopCCRP(pptp_ctrl *_this, int result, int error) |
686 |
|
|
{ |
687 |
|
|
int lpkt; |
688 |
|
|
struct pptp_stop_ccrp *stop_ccrp; |
689 |
|
|
|
690 |
|
|
stop_ccrp = bytebuffer_pointer(_this->send_buf); |
691 |
|
|
|
692 |
|
|
lpkt = bytebuffer_remaining(_this->send_buf); |
693 |
|
|
if (lpkt < sizeof(struct pptp_stop_ccrp)) { |
694 |
|
|
pptp_ctrl_log(_this, LOG_ERR, |
695 |
|
|
"SendCCRQ failed: No buffer space available"); |
696 |
|
|
return -1; |
697 |
|
|
} |
698 |
|
|
memset(stop_ccrp, 0, sizeof(struct pptp_stop_ccrp)); |
699 |
|
|
|
700 |
|
|
pptp_init_header(&stop_ccrp->header, sizeof(struct pptp_stop_ccrp), |
701 |
|
|
PPTP_CTRL_MES_CODE_StopCCRP); |
702 |
|
|
|
703 |
|
|
stop_ccrp->result = result; |
704 |
|
|
stop_ccrp->error = error; |
705 |
|
|
|
706 |
|
|
pptp_ctrl_log(_this, LOG_INFO, |
707 |
|
|
"SendStopCCRP result=%s(%u) error=%s(%u)", |
708 |
|
|
pptp_StopCCRP_result_string(stop_ccrp->result), stop_ccrp->result, |
709 |
|
|
pptp_general_error_string(stop_ccrp->error), stop_ccrp->error); |
710 |
|
|
|
711 |
|
|
pptp_ctrl_output(_this, NULL, sizeof(struct pptp_stop_ccrp)); |
712 |
|
|
|
713 |
|
|
return 0; |
714 |
|
|
} |
715 |
|
|
|
716 |
|
|
/* Send Start-Control-Connection-Reply */ |
717 |
|
|
static int |
718 |
|
|
pptp_ctrl_send_SCCRP(pptp_ctrl *_this, int result, int error) |
719 |
|
|
{ |
720 |
|
|
int lpkt; |
721 |
|
|
struct pptp_scc *scc; |
722 |
|
|
char logbuf[512]; |
723 |
|
|
const char *val; |
724 |
|
|
|
725 |
|
|
scc = bytebuffer_pointer(_this->send_buf); |
726 |
|
|
lpkt = bytebuffer_remaining(_this->send_buf); |
727 |
|
|
if (lpkt < sizeof(struct pptp_scc)) { |
728 |
|
|
pptp_ctrl_log(_this, LOG_ERR, |
729 |
|
|
"SendSCCRP failed: No buffer space available"); |
730 |
|
|
return -1; |
731 |
|
|
} |
732 |
|
|
memset(scc, 0, sizeof(struct pptp_scc)); |
733 |
|
|
|
734 |
|
|
pptp_init_header(&scc->header, sizeof(struct pptp_scc), |
735 |
|
|
PPTP_CTRL_MES_CODE_SCCRP); |
736 |
|
|
|
737 |
|
|
scc->protocol_version = PPTP_RFC_2637_VERSION; |
738 |
|
|
scc->result_code = result; |
739 |
|
|
scc->error_code = error; |
740 |
|
|
|
741 |
|
|
/* XXX only support sync frames */ |
742 |
|
|
scc->framing_caps = PPTP_CTRL_FRAMING_SYNC; |
743 |
|
|
scc->bearer_caps = PPTP_CTRL_BEARER_DIGITAL; |
744 |
|
|
|
745 |
|
|
scc->max_channels = 4; /* XXX */ |
746 |
|
|
scc->firmware_revision = MAJOR_VERSION << 8 | MINOR_VERSION; |
747 |
|
|
|
748 |
|
|
/* this implementation only support these strings up to |
749 |
|
|
* 63 character */ |
750 |
|
|
/* host name */ |
751 |
|
|
|
752 |
|
|
if ((val = PPTP_CTRL_CONF(_this)->hostname) == NULL) |
753 |
|
|
val = ""; |
754 |
|
|
strlcpy(scc->host_name, val, sizeof(scc->host_name)); |
755 |
|
|
|
756 |
|
|
/* vender name */ |
757 |
|
|
if (PPTP_CTRL_CONF(_this)->vendor_name == NULL) |
758 |
|
|
val = PPTPD_DEFAULT_VENDOR_NAME; |
759 |
|
|
strlcpy(scc->vendor_string, val, sizeof(scc->vendor_string)); |
760 |
|
|
|
761 |
|
|
pptp_ctrl_SCCRx_string(scc, logbuf, sizeof(logbuf)); |
762 |
|
|
pptp_ctrl_log(_this, LOG_INFO, "SendSCCRP %s", logbuf); |
763 |
|
|
|
764 |
|
|
scc->protocol_version = htons(scc->protocol_version); |
765 |
|
|
scc->framing_caps = htonl(scc->framing_caps); |
766 |
|
|
scc->bearer_caps = htonl(scc->bearer_caps); |
767 |
|
|
scc->max_channels = htons(scc->max_channels); |
768 |
|
|
scc->firmware_revision = htons(scc->firmware_revision); |
769 |
|
|
|
770 |
|
|
pptp_ctrl_output(_this, NULL, sizeof(struct pptp_scc)); |
771 |
|
|
|
772 |
|
|
return 0; |
773 |
|
|
} |
774 |
|
|
|
775 |
|
|
/* receive ECHO and reply */ |
776 |
|
|
static void |
777 |
|
|
pptp_ctrl_process_echo_req(pptp_ctrl *_this, u_char *pkt, int lpkt) |
778 |
|
|
{ |
779 |
|
|
struct pptp_echo_rq *echo_rq; |
780 |
|
|
struct pptp_echo_rp *echo_rp; |
781 |
|
|
|
782 |
|
|
if (lpkt < sizeof(struct pptp_echo_rq)) { |
783 |
|
|
pptp_ctrl_log(_this, LOG_ERR, "Received bad EchoReq: packet " |
784 |
|
|
"too short: %d < %d", lpkt, |
785 |
|
|
(int)sizeof(struct pptp_echo_rq)); |
786 |
|
|
return; |
787 |
|
|
} |
788 |
|
|
echo_rq = (struct pptp_echo_rq *)pkt; |
789 |
|
|
|
790 |
|
|
PPTP_CTRL_DBG((_this, LOG_DEBUG, "RecvEchoReq")); |
791 |
|
|
|
792 |
|
|
echo_rp = bytebuffer_pointer(_this->send_buf); |
793 |
|
|
lpkt = bytebuffer_remaining(_this->send_buf); |
794 |
|
|
if (echo_rp == NULL || lpkt < sizeof(struct pptp_echo_rp)) { |
795 |
|
|
pptp_ctrl_log(_this, LOG_ERR, |
796 |
|
|
"Failed to send EchoReq: No buffer space available"); |
797 |
|
|
return; |
798 |
|
|
} |
799 |
|
|
memset(echo_rp, 0, sizeof(struct pptp_echo_rp)); |
800 |
|
|
|
801 |
|
|
pptp_init_header(&echo_rp->header, sizeof(struct pptp_echo_rp), |
802 |
|
|
PPTP_CTRL_MES_CODE_ECHO_RP); |
803 |
|
|
|
804 |
|
|
echo_rp->identifier = echo_rq->identifier; |
805 |
|
|
echo_rp->result_code = PPTP_ECHO_RP_RESULT_OK; |
806 |
|
|
echo_rp->error_code = PPTP_ERROR_NONE; |
807 |
|
|
echo_rp->reserved1 = htons(0); |
808 |
|
|
|
809 |
|
|
pptp_ctrl_output(_this, NULL, sizeof(struct pptp_echo_rp)); |
810 |
|
|
PPTP_CTRL_DBG((_this, LOG_DEBUG, "SendEchoReply")); |
811 |
|
|
} |
812 |
|
|
|
813 |
|
|
/* receiver Echo-Reply */ |
814 |
|
|
static int |
815 |
|
|
pptp_ctrl_recv_echo_rep(pptp_ctrl *_this, u_char *pkt, int lpkt) |
816 |
|
|
{ |
817 |
|
|
struct pptp_echo_rp *echo_rp; |
818 |
|
|
|
819 |
|
|
if (lpkt < sizeof(struct pptp_echo_rp)) { |
820 |
|
|
pptp_ctrl_log(_this, LOG_ERR, "Received bad EchoReq: packet " |
821 |
|
|
"too short: %d < %d", lpkt, |
822 |
|
|
(int)sizeof(struct pptp_echo_rp)); |
823 |
|
|
return 1; |
824 |
|
|
} |
825 |
|
|
echo_rp = (struct pptp_echo_rp *)pkt; |
826 |
|
|
|
827 |
|
|
if (echo_rp->result_code != PPTP_ECHO_RP_RESULT_OK) { |
828 |
|
|
pptp_ctrl_log(_this, LOG_ERR, "Received negative EchoReply: %s", |
829 |
|
|
pptp_general_error_string(echo_rp->error_code)); |
830 |
|
|
return 1; |
831 |
|
|
} |
832 |
|
|
if (_this->echo_seq != ntohl(echo_rp->identifier)) { |
833 |
|
|
pptp_ctrl_log(_this, LOG_ERR, "Received bad EchoReply: " |
834 |
|
|
"Identifier mismatch sent=%u recv=%u", |
835 |
|
|
_this->echo_seq , ntohl(echo_rp->identifier)); |
836 |
|
|
return 1; |
837 |
|
|
} |
838 |
|
|
PPTP_CTRL_DBG((_this, LOG_DEBUG, "RecvEchoReply")); |
839 |
|
|
return 0; |
840 |
|
|
} |
841 |
|
|
|
842 |
|
|
/* send Echo-Request */ |
843 |
|
|
static void |
844 |
|
|
pptp_ctrl_send_echo_req(pptp_ctrl *_this) |
845 |
|
|
{ |
846 |
|
|
int lpkt; |
847 |
|
|
struct pptp_echo_rq *echo_rq; |
848 |
|
|
|
849 |
|
|
echo_rq = (struct pptp_echo_rq *)bytebuffer_pointer(_this->send_buf); |
850 |
|
|
lpkt = bytebuffer_remaining(_this->send_buf); |
851 |
|
|
if (echo_rq == NULL || lpkt < sizeof(struct pptp_echo_rq)) { |
852 |
|
|
pptp_ctrl_log(_this, LOG_ERR, |
853 |
|
|
"SendEchoReq failed: No buffer space available"); |
854 |
|
|
return; |
855 |
|
|
} |
856 |
|
|
memset(echo_rq, 0, sizeof(struct pptp_echo_rq)); |
857 |
|
|
|
858 |
|
|
pptp_init_header(&echo_rq->header, sizeof(struct pptp_echo_rq), |
859 |
|
|
PPTP_CTRL_MES_CODE_ECHO_RQ); |
860 |
|
|
|
861 |
|
|
echo_rq->identifier = htonl(_this->echo_seq); |
862 |
|
|
|
863 |
|
|
pptp_ctrl_output(_this, NULL, sizeof(struct pptp_echo_rq)); |
864 |
|
|
PPTP_CTRL_DBG((_this, LOG_DEBUG, "SendEchoReq")); |
865 |
|
|
} |
866 |
|
|
|
867 |
|
|
/* send Call-Disconnect-Notify */ |
868 |
|
|
static void |
869 |
|
|
pptp_ctrl_send_CDN(pptp_ctrl *_this, int result, int error, int cause, |
870 |
|
|
const char *statistics) |
871 |
|
|
{ |
872 |
|
|
int lpkt; |
873 |
|
|
struct pptp_cdn *cdn; |
874 |
|
|
|
875 |
|
|
cdn = bytebuffer_pointer(_this->send_buf); |
876 |
|
|
lpkt = bytebuffer_remaining(_this->send_buf); |
877 |
|
|
if (lpkt < sizeof(struct pptp_cdn)) { |
878 |
|
|
pptp_ctrl_log(_this, LOG_ERR, |
879 |
|
|
"SendCCR failed: No buffer space available"); |
880 |
|
|
return; |
881 |
|
|
} |
882 |
|
|
memset(cdn, 0, sizeof(struct pptp_cdn)); |
883 |
|
|
|
884 |
|
|
pptp_init_header(&cdn->header, sizeof(struct pptp_cdn), |
885 |
|
|
PPTP_CTRL_MES_CODE_CDN); |
886 |
|
|
|
887 |
|
|
cdn->call_id = _this->id; |
888 |
|
|
cdn->result_code = result; |
889 |
|
|
cdn->error_code = error; |
890 |
|
|
cdn->cause_code = cause; |
891 |
|
|
if (statistics != NULL) |
892 |
|
|
strlcpy(cdn->statistics, statistics, sizeof(cdn->statistics)); |
893 |
|
|
|
894 |
|
|
cdn->call_id = htons(cdn->call_id); |
895 |
|
|
cdn->cause_code = htons(cdn->cause_code); |
896 |
|
|
|
897 |
|
|
pptp_ctrl_output(_this, NULL, sizeof(struct pptp_cdn)); |
898 |
|
|
} |
899 |
|
|
|
900 |
|
|
/* receive Control-packet */ |
901 |
|
|
static int |
902 |
|
|
pptp_ctrl_input(pptp_ctrl *_this, u_char *pkt, int lpkt) |
903 |
|
|
{ |
904 |
|
|
char errmes[256]; |
905 |
|
|
time_t curr_time; |
906 |
|
|
struct pptp_ctrl_header *hdr; |
907 |
|
|
|
908 |
|
|
PPTP_CTRL_ASSERT(lpkt >= sizeof(struct pptp_ctrl_header)); |
909 |
|
|
|
910 |
|
|
curr_time = get_monosec(); |
911 |
|
|
hdr = (struct pptp_ctrl_header *)pkt; |
912 |
|
|
|
913 |
|
|
hdr->length = ntohs(hdr->length); |
914 |
|
|
hdr->pptp_message_type = ntohs(hdr->pptp_message_type); |
915 |
|
|
hdr->magic_cookie = ntohl(hdr->magic_cookie); |
916 |
|
|
hdr->control_message_type = ntohs(hdr->control_message_type); |
917 |
|
|
hdr->reserved0 = ntohs(hdr->reserved0); |
918 |
|
|
|
919 |
|
|
/* sanity check */ |
920 |
|
|
PPTP_CTRL_ASSERT(hdr->length <= lpkt); |
921 |
|
|
|
922 |
|
|
_this->last_rcv_ctrl = curr_time; |
923 |
|
|
|
924 |
|
|
if (PPTP_CTRL_CONF(_this)->ctrl_in_pktdump != 0) { |
925 |
|
|
pptp_ctrl_log(_this, LOG_DEBUG, |
926 |
|
|
"PPTP Control input packet dump: mestype=%s(%d)", |
927 |
|
|
pptp_ctrl_mes_type_string(hdr->control_message_type), |
928 |
|
|
hdr->control_message_type); |
929 |
|
|
show_hd(debug_get_debugfp(), pkt, lpkt); |
930 |
|
|
} |
931 |
|
|
|
932 |
|
|
/* inspect packet body */ |
933 |
|
|
/* message type */ |
934 |
|
|
if (hdr->pptp_message_type != PPTP_MES_TYPE_CTRL) { |
935 |
|
|
snprintf(errmes, sizeof(errmes), "unknown message type %d", |
936 |
|
|
hdr->pptp_message_type); |
937 |
|
|
goto bad_packet; |
938 |
|
|
} |
939 |
|
|
/* magic cookie */ |
940 |
|
|
if (hdr->magic_cookie != PPTP_MAGIC_COOKIE) { |
941 |
|
|
snprintf(errmes, sizeof(errmes), "wrong magic %08x != %08x", |
942 |
|
|
hdr->magic_cookie, PPTP_MAGIC_COOKIE); |
943 |
|
|
goto bad_packet; |
944 |
|
|
} |
945 |
|
|
|
946 |
|
|
/* As there is possibility of state conflicts, |
947 |
|
|
* ECHO Reply requiries special care. |
948 |
|
|
*/ |
949 |
|
|
switch (hdr->control_message_type) { |
950 |
|
|
case PPTP_CTRL_MES_CODE_ECHO_RP: |
951 |
|
|
if (pptp_ctrl_recv_echo_rep(_this, pkt, lpkt) != 0) { |
952 |
|
|
pptp_ctrl_fini(_this); |
953 |
|
|
return 1; |
954 |
|
|
} |
955 |
|
|
return 0; |
956 |
|
|
} |
957 |
|
|
|
958 |
|
|
/* |
959 |
|
|
* State machine |
960 |
|
|
*/ |
961 |
|
|
switch (_this->state) { |
962 |
|
|
case PPTP_CTRL_STATE_IDLE: |
963 |
|
|
switch (hdr->control_message_type) { |
964 |
|
|
case PPTP_CTRL_MES_CODE_SCCRQ: |
965 |
|
|
if (pptp_ctrl_recv_SCCRQ(_this, pkt, lpkt) != 0) { |
966 |
|
|
return 0; |
967 |
|
|
} |
968 |
|
|
if (pptp_ctrl_send_SCCRP(_this, |
969 |
|
|
PPTP_SCCRP_RESULT_SUCCESS, PPTP_ERROR_NONE) != 0) { |
970 |
|
|
return 0; |
971 |
|
|
} |
972 |
|
|
_this->state = PPTP_CTRL_STATE_ESTABLISHED; |
973 |
|
|
return 0; |
974 |
|
|
default: |
975 |
|
|
break; |
976 |
|
|
} |
977 |
|
|
break; |
978 |
|
|
case PPTP_CTRL_STATE_ESTABLISHED: |
979 |
|
|
switch (hdr->control_message_type) { |
980 |
|
|
case PPTP_CTRL_MES_CODE_ECHO_RQ: |
981 |
|
|
pptp_ctrl_process_echo_req(_this, pkt, lpkt); |
982 |
|
|
return 0; |
983 |
|
|
/* dispatch to pptp_call_input() if it is call-related-packet */ |
984 |
|
|
case PPTP_CTRL_MES_CODE_SLI: |
985 |
|
|
case PPTP_CTRL_MES_CODE_ICRQ: |
986 |
|
|
case PPTP_CTRL_MES_CODE_ICRP: |
987 |
|
|
case PPTP_CTRL_MES_CODE_OCRQ: |
988 |
|
|
case PPTP_CTRL_MES_CODE_OCRP: |
989 |
|
|
case PPTP_CTRL_MES_CODE_ICCN: |
990 |
|
|
case PPTP_CTRL_MES_CODE_CDN: |
991 |
|
|
case PPTP_CTRL_MES_CODE_CCR: |
992 |
|
|
return pptp_ctrl_call_input(_this, |
993 |
|
|
hdr->control_message_type, pkt, lpkt); |
994 |
|
|
case PPTP_CTRL_MES_CODE_StopCCRQ: |
995 |
|
|
if (pptp_ctrl_recv_StopCCRQ(_this, pkt, lpkt) != 0) { |
996 |
|
|
pptp_ctrl_stop(_this, |
997 |
|
|
PPTP_StopCCRQ_REASON_STOP_PROTOCOL); |
998 |
|
|
return 0; |
999 |
|
|
} |
1000 |
|
|
if (pptp_ctrl_send_StopCCRP(_this, |
1001 |
|
|
PPTP_StopCCRP_RESULT_OK, PPTP_ERROR_NONE)!= 0) { |
1002 |
|
|
return 0; |
1003 |
|
|
} |
1004 |
|
|
pptp_ctrl_fini(_this); |
1005 |
|
|
return 1; |
1006 |
|
|
default: |
1007 |
|
|
break; |
1008 |
|
|
} |
1009 |
|
|
case PPTP_CTRL_STATE_WAIT_STOP_REPLY: |
1010 |
|
|
switch (hdr->control_message_type) { |
1011 |
|
|
case PPTP_CTRL_MES_CODE_StopCCRP: |
1012 |
|
|
pptp_ctrl_recv_StopCCRP(_this, pkt, lpkt); |
1013 |
|
|
pptp_ctrl_fini(_this); |
1014 |
|
|
return 1; |
1015 |
|
|
} |
1016 |
|
|
break; |
1017 |
|
|
case PPTP_CTRL_STATE_WAIT_CTRL_REPLY: |
1018 |
|
|
/* XXX this implementation only support PAC mode */ |
1019 |
|
|
break; |
1020 |
|
|
} |
1021 |
|
|
pptp_ctrl_log(_this, LOG_WARNING, |
1022 |
|
|
"Unhandled control message type=%s(%d)", |
1023 |
|
|
pptp_ctrl_mes_type_string(hdr->control_message_type), |
1024 |
|
|
hdr->control_message_type); |
1025 |
|
|
return 0; |
1026 |
|
|
|
1027 |
|
|
bad_packet: |
1028 |
|
|
pptp_ctrl_log(_this, LOG_ERR, "Received bad packet: %s", errmes); |
1029 |
|
|
pptp_ctrl_stop(_this, PPTP_StopCCRQ_REASON_STOP_PROTOCOL); |
1030 |
|
|
|
1031 |
|
|
return 0; |
1032 |
|
|
} |
1033 |
|
|
|
1034 |
|
|
/* receiver PPTP Call related messages */ |
1035 |
|
|
static int |
1036 |
|
|
pptp_ctrl_call_input(pptp_ctrl *_this, int mes_type, u_char *pkt, int lpkt) |
1037 |
|
|
{ |
1038 |
|
|
int i, call_id, lpkt0; |
1039 |
|
|
pptp_call *call; |
1040 |
|
|
const char *reason; |
1041 |
|
|
u_char *pkt0; |
1042 |
|
|
|
1043 |
|
|
pkt0 = pkt; |
1044 |
|
|
lpkt0 = lpkt; |
1045 |
|
|
call_id = -1; |
1046 |
|
|
pkt += sizeof(struct pptp_ctrl_header); |
1047 |
|
|
lpkt -= sizeof(struct pptp_ctrl_header); |
1048 |
|
|
reason = "(no reason)"; |
1049 |
|
|
|
1050 |
|
|
/* sanity check */ |
1051 |
|
|
if (lpkt < 4) { |
1052 |
|
|
reason = "received packet is too short"; |
1053 |
|
|
goto badpacket; |
1054 |
|
|
} |
1055 |
|
|
call = NULL; |
1056 |
|
|
call_id = ntohs(*(uint16_t *)pkt); |
1057 |
|
|
|
1058 |
|
|
switch (mes_type) { |
1059 |
|
|
case PPTP_CTRL_MES_CODE_SLI: /* PNS <=> PAC */ |
1060 |
|
|
/* only SLI contains Call-ID of this peer */ |
1061 |
|
|
for (i = 0; i < slist_length(&_this->call_list); i++) { |
1062 |
|
|
call = slist_get(&_this->call_list, i); |
1063 |
|
|
if (call->id == call_id) |
1064 |
|
|
break; |
1065 |
|
|
call = NULL; |
1066 |
|
|
} |
1067 |
|
|
if (call == NULL) { |
1068 |
|
|
reason = "Call Id is not associated by this control"; |
1069 |
|
|
goto badpacket; |
1070 |
|
|
} |
1071 |
|
|
goto call_searched; |
1072 |
|
|
case PPTP_CTRL_MES_CODE_ICRP: /* PNS => PAC */ |
1073 |
|
|
/* |
1074 |
|
|
* as this implementation never sent ICRQ, this case |
1075 |
|
|
* should not happen. |
1076 |
|
|
* But just to make sure, pptp_call.c can handle this |
1077 |
|
|
* message. |
1078 |
|
|
*/ |
1079 |
|
|
/* FALLTHROUGH */ |
1080 |
|
|
case PPTP_CTRL_MES_CODE_OCRQ: /* PNS => PAC */ |
1081 |
|
|
case PPTP_CTRL_MES_CODE_CCR: /* PNS => PAC */ |
1082 |
|
|
/* liner-search will be enough */ |
1083 |
|
|
for (i = 0; i < slist_length(&_this->call_list); i++) { |
1084 |
|
|
call = slist_get(&_this->call_list, i); |
1085 |
|
|
if (call->peers_call_id == call_id) |
1086 |
|
|
break; |
1087 |
|
|
call = NULL; |
1088 |
|
|
} |
1089 |
|
|
if (call == NULL && mes_type == PPTP_CTRL_MES_CODE_CCR) { |
1090 |
|
|
pptp_ctrl_send_CDN(_this, PPTP_CDN_RESULT_GENRIC_ERROR, |
1091 |
|
|
PPTP_ERROR_BAD_CALL, 0, NULL); |
1092 |
|
|
goto call_searched; |
1093 |
|
|
} |
1094 |
|
|
if (mes_type == PPTP_CTRL_MES_CODE_OCRQ) { |
1095 |
|
|
/* make new call */ |
1096 |
|
|
if (call != NULL) { |
1097 |
|
|
pptp_call_input(call, mes_type, pkt0, lpkt0); |
1098 |
|
|
return 0; |
1099 |
|
|
} |
1100 |
|
|
if ((call = pptp_call_create()) == NULL) { |
1101 |
|
|
pptp_ctrl_log(_this, LOG_ERR, |
1102 |
|
|
"pptp_call_create() failed: %m"); |
1103 |
|
|
goto fail; |
1104 |
|
|
} |
1105 |
|
|
if (pptp_call_init(call, _this) != 0) { |
1106 |
|
|
pptp_ctrl_log(_this, LOG_ERR, |
1107 |
|
|
"pptp_call_init() failed: %m"); |
1108 |
|
|
pptp_call_destroy(call); |
1109 |
|
|
goto fail; |
1110 |
|
|
} |
1111 |
|
|
slist_add(&_this->call_list, call); |
1112 |
|
|
} |
1113 |
|
|
call_searched: |
1114 |
|
|
if (call == NULL) { |
1115 |
|
|
reason = "Call Id is not associated by this control"; |
1116 |
|
|
goto badpacket; |
1117 |
|
|
} |
1118 |
|
|
pptp_call_input(call, mes_type, pkt0, lpkt0); |
1119 |
|
|
return 0; |
1120 |
|
|
case PPTP_CTRL_MES_CODE_OCRP: /* PAC => PNS */ |
1121 |
|
|
case PPTP_CTRL_MES_CODE_ICRQ: /* PAC => PNS */ |
1122 |
|
|
case PPTP_CTRL_MES_CODE_ICCN: /* PAC => PNS */ |
1123 |
|
|
case PPTP_CTRL_MES_CODE_CDN: /* PAC => PNS */ |
1124 |
|
|
/* don't receive because above messages are only of PNS */ |
1125 |
|
|
default: |
1126 |
|
|
break; |
1127 |
|
|
} |
1128 |
|
|
reason = "Message type is unexpected."; |
1129 |
|
|
/* FALLTHROUGH */ |
1130 |
|
|
badpacket: |
1131 |
|
|
pptp_ctrl_log(_this, LOG_INFO, |
1132 |
|
|
"Received a bad %s(%d) call_id=%d: %s", |
1133 |
|
|
pptp_ctrl_mes_type_string(mes_type), mes_type, call_id, reason); |
1134 |
|
|
return 0; |
1135 |
|
|
fail: |
1136 |
|
|
pptp_ctrl_stop(_this, PPTP_StopCCRQ_REASON_STOP_PROTOCOL); |
1137 |
|
|
return 0; |
1138 |
|
|
} |
1139 |
|
|
|
1140 |
|
|
|
1141 |
|
|
/* |
1142 |
|
|
* utilities |
1143 |
|
|
*/ |
1144 |
|
|
|
1145 |
|
|
/* logging with the label of the instance */ |
1146 |
|
|
static void |
1147 |
|
|
pptp_ctrl_log(pptp_ctrl *_this, int prio, const char *fmt, ...) |
1148 |
|
|
{ |
1149 |
|
|
char logbuf[BUFSIZ]; |
1150 |
|
|
va_list ap; |
1151 |
|
|
|
1152 |
|
|
va_start(ap, fmt); |
1153 |
|
|
#ifdef PPTPD_MULTIPLE |
1154 |
|
|
snprintf(logbuf, sizeof(logbuf), "pptpd id=%u ctrl=%u %s", |
1155 |
|
|
_this->pptpd->id, _this->id, fmt); |
1156 |
|
|
#else |
1157 |
|
|
snprintf(logbuf, sizeof(logbuf), "pptpd ctrl=%u %s", _this->id, fmt); |
1158 |
|
|
#endif |
1159 |
|
|
vlog_printf(prio, logbuf, ap); |
1160 |
|
|
va_end(ap); |
1161 |
|
|
} |
1162 |
|
|
|
1163 |
|
|
static const char * |
1164 |
|
|
pptp_ctrl_state_string(int state) |
1165 |
|
|
{ |
1166 |
|
|
switch (state) { |
1167 |
|
|
case PPTP_CTRL_STATE_IDLE: |
1168 |
|
|
return "idle"; |
1169 |
|
|
case PPTP_CTRL_STATE_WAIT_CTRL_REPLY: |
1170 |
|
|
return "wait-ctrl-reply"; |
1171 |
|
|
case PPTP_CTRL_STATE_ESTABLISHED: |
1172 |
|
|
return "established"; |
1173 |
|
|
case PPTP_CTRL_STATE_WAIT_STOP_REPLY: |
1174 |
|
|
return "wait-stop-reply"; |
1175 |
|
|
} |
1176 |
|
|
return "unknown"; |
1177 |
|
|
} |