1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2015 Intel Corporation.
5  *
6  * Author: David Woodhouse <dwmw2@infradead.org>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1, as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  */
17 
18 #include <config.h>
19 
20 #include <errno.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #ifndef _WIN32
29 #include <netinet/in.h>
30 #include <sys/socket.h>
31 #endif
32 
33 #include "openconnect-internal.h"
34 
35 /*
36  * The master-secret is generated randomly by the client. The server
37  * responds with a DTLS Session-ID. These, done over the HTTPS
38  * connection, are enough to 'resume' a DTLS session, bypassing all
39  * the normal setup of a normal DTLS connection.
40  *
41  * Cisco use a version of the protocol which predates RFC4347, but
42  * isn't quite the same as the pre-RFC version of the protocol which
43  * was in OpenSSL 0.9.8e -- it includes backports of some later
44  * OpenSSL patches.
45  *
46  * The openssl/ directory of this source tree should contain both a
47  * small patch against OpenSSL 0.9.8e to make it support Cisco's
48  * snapshot of the protocol, and a larger patch against newer OpenSSL
49  * which gives us an option to use the old protocol again.
50  *
51  * Cisco's server also seems to respond to the official version of the
52  * protocol, with a change in the ChangeCipherSpec packet which implies
53  * that it does know the difference and isn't just repeating the version
54  * number seen in the ClientHello. But although I can make the handshake
55  * complete by hacking tls1_mac() to use the _old_ protocol version
56  * number when calculating the MAC, the server still seems to be ignoring
57  * my subsequent data packets. So we use the old protocol, which is what
58  * their clients use anyway.
59  */
60 
61 #if defined(OPENCONNECT_OPENSSL)
62 #define DTLS_SEND SSL_write
63 #define DTLS_RECV SSL_read
64 #elif defined(OPENCONNECT_GNUTLS)
65 #define DTLS_SEND gnutls_record_send
66 #define DTLS_RECV gnutls_record_recv
67 #endif
68 
openconnect_bin2hex(const char * prefix,const uint8_t * data,unsigned len)69 char *openconnect_bin2hex(const char *prefix, const uint8_t *data, unsigned len)
70 {
71 	struct oc_text_buf *buf;
72 	char *p = NULL;
73 
74 	buf = buf_alloc();
75 	if (prefix)
76 		buf_append(buf, "%s", prefix);
77 	buf_append_hex(buf, data, len);
78 
79 	if (!buf_error(buf)) {
80 		p = buf->data;
81 		buf->data = NULL;
82 	}
83 	buf_free(buf);
84 
85 	return p;
86 }
87 
openconnect_bin2base64(const char * prefix,const uint8_t * data,unsigned len)88 char *openconnect_bin2base64(const char *prefix, const uint8_t *data, unsigned len)
89 {
90 	struct oc_text_buf *buf;
91 	char *p = NULL;
92 
93 	buf = buf_alloc();
94 	if (prefix)
95 		buf_append(buf, "%s", prefix);
96 	buf_append_base64(buf, data, len);
97 
98 	if (!buf_error(buf)) {
99 		p = buf->data;
100 		buf->data = NULL;
101 	}
102 	buf_free(buf);
103 
104 	return p;
105 }
106 
connect_dtls_socket(struct openconnect_info * vpninfo)107 static int connect_dtls_socket(struct openconnect_info *vpninfo)
108 {
109 	int dtls_fd, ret;
110 
111 	/* Sanity check for the removal of new_dtls_{fd,ssl} */
112 	if (vpninfo->dtls_fd != -1) {
113 		vpn_progress(vpninfo, PRG_ERR, _("DTLS connection attempted with an existing fd\n"));
114 		vpninfo->dtls_attempt_period = 0;
115 		return -EINVAL;
116 	}
117 
118 	if (!vpninfo->dtls_addr) {
119 		vpn_progress(vpninfo, PRG_ERR, _("No DTLS address\n"));
120 		vpninfo->dtls_attempt_period = 0;
121 		return -EINVAL;
122 	}
123 
124 	if (!vpninfo->dtls_cipher) {
125 		/* We probably didn't offer it any ciphers it liked */
126 		vpn_progress(vpninfo, PRG_ERR, _("Server offered no DTLS cipher option\n"));
127 		vpninfo->dtls_attempt_period = 0;
128 		return -EINVAL;
129 	}
130 
131 	if (vpninfo->proxy) {
132 		/* XXX: Theoretically, SOCKS5 proxies can do UDP too */
133 		vpn_progress(vpninfo, PRG_ERR, _("No DTLS when connected via proxy\n"));
134 		vpninfo->dtls_attempt_period = 0;
135 		return -EINVAL;
136 	}
137 
138 	dtls_fd = udp_connect(vpninfo);
139 	if (dtls_fd < 0)
140 		return -EINVAL;
141 
142 
143 	ret = start_dtls_handshake(vpninfo, dtls_fd);
144 	if (ret) {
145 		closesocket(dtls_fd);
146 		return ret;
147 	}
148 
149 	vpninfo->dtls_state = DTLS_CONNECTING;
150 
151 	vpninfo->dtls_fd = dtls_fd;
152 	monitor_fd_new(vpninfo, dtls);
153 	monitor_read_fd(vpninfo, dtls);
154 	monitor_except_fd(vpninfo, dtls);
155 
156 	time(&vpninfo->new_dtls_started);
157 
158 	return dtls_try_handshake(vpninfo);
159 }
160 
dtls_close(struct openconnect_info * vpninfo)161 void dtls_close(struct openconnect_info *vpninfo)
162 {
163 	if (vpninfo->dtls_ssl) {
164 		dtls_ssl_free(vpninfo);
165 		closesocket(vpninfo->dtls_fd);
166 		unmonitor_read_fd(vpninfo, dtls);
167 		unmonitor_write_fd(vpninfo, dtls);
168 		unmonitor_except_fd(vpninfo, dtls);
169 		vpninfo->dtls_ssl = NULL;
170 		vpninfo->dtls_fd = -1;
171 	}
172 	vpninfo->dtls_state = DTLS_SLEEPING;
173 }
174 
dtls_reconnect(struct openconnect_info * vpninfo)175 static int dtls_reconnect(struct openconnect_info *vpninfo)
176 {
177 	dtls_close(vpninfo);
178 
179 	if (vpninfo->dtls_state == DTLS_DISABLED)
180 		return -EINVAL;
181 
182 	vpninfo->dtls_state = DTLS_SLEEPING;
183 	return connect_dtls_socket(vpninfo);
184 }
185 
dtls_setup(struct openconnect_info * vpninfo,int dtls_attempt_period)186 int dtls_setup(struct openconnect_info *vpninfo, int dtls_attempt_period)
187 {
188 	struct oc_vpn_option *dtls_opt = vpninfo->dtls_options;
189 	int dtls_port = 0;
190 
191 	if (vpninfo->dtls_state == DTLS_DISABLED)
192 		return -EINVAL;
193 
194 	vpninfo->dtls_attempt_period = dtls_attempt_period;
195 	if (!dtls_attempt_period)
196 		return 0;
197 
198 	while (dtls_opt) {
199 		vpn_progress(vpninfo, PRG_DEBUG,
200 			     _("DTLS option %s : %s\n"),
201 			     dtls_opt->option, dtls_opt->value);
202 
203 		if (!strcmp(dtls_opt->option, "X-DTLS-Port")) {
204 			dtls_port = atol(dtls_opt->value);
205 		} else if (!strcmp(dtls_opt->option, "X-DTLS-Keepalive")) {
206 			vpninfo->dtls_times.keepalive = atol(dtls_opt->value);
207 		} else if (!strcmp(dtls_opt->option, "X-DTLS-DPD")) {
208 			int j = atol(dtls_opt->value);
209 			if (j && (!vpninfo->dtls_times.dpd || j < vpninfo->dtls_times.dpd))
210 				vpninfo->dtls_times.dpd = j;
211 		} else if (!strcmp(dtls_opt->option, "X-DTLS-Rekey-Method")) {
212 			if (!strcmp(dtls_opt->value, "new-tunnel"))
213 				vpninfo->dtls_times.rekey_method = REKEY_TUNNEL;
214 			else if (!strcmp(dtls_opt->value, "ssl"))
215 				vpninfo->dtls_times.rekey_method = REKEY_SSL;
216 			else
217 				vpninfo->dtls_times.rekey_method = REKEY_NONE;
218 		} else if (!strcmp(dtls_opt->option, "X-DTLS-Rekey-Time")) {
219 			vpninfo->dtls_times.rekey = atol(dtls_opt->value);
220 		}
221 
222 		dtls_opt = dtls_opt->next;
223 	}
224 	if (!dtls_port) {
225 		vpninfo->dtls_attempt_period = 0;
226 		return -EINVAL;
227 	}
228 	if (vpninfo->dtls_times.rekey <= 0)
229 		vpninfo->dtls_times.rekey_method = REKEY_NONE;
230 
231 	if (udp_sockaddr(vpninfo, dtls_port)) {
232 		vpninfo->dtls_attempt_period = 0;
233 		return -EINVAL;
234 	}
235 	if (connect_dtls_socket(vpninfo))
236 		return -EINVAL;
237 
238 	vpn_progress(vpninfo, PRG_DEBUG,
239 		     _("DTLS initialised. DPD %d, Keepalive %d\n"),
240 		     vpninfo->dtls_times.dpd, vpninfo->dtls_times.keepalive);
241 
242 	return 0;
243 }
244 
udp_tos_update(struct openconnect_info * vpninfo,struct pkt * pkt)245 int udp_tos_update(struct openconnect_info *vpninfo, struct pkt *pkt)
246 {
247 	int tos;
248 
249 	/* Extract TOS field from IP header (IPv4 and IPv6 differ) */
250 	switch(pkt->data[0] >> 4) {
251 		case 4:
252 			tos = pkt->data[1];
253 			break;
254 		case 6:
255 			tos = (load_be16(pkt->data) >> 4) & 0xff;
256 			break;
257 		default:
258 			vpn_progress(vpninfo, PRG_ERR,
259 				     _("Unknown packet (len %d) received: %02x %02x %02x %02x...\n"),
260 				     pkt->len, pkt->data[0], pkt->data[1], pkt->data[2], pkt->data[3]);
261 			return -EINVAL;
262 	}
263 
264 	/* set the actual value */
265 	if (tos != vpninfo->dtls_tos_current) {
266 		vpn_progress(vpninfo, PRG_DEBUG, _("TOS this: %d, TOS last: %d\n"),
267 			     tos, vpninfo->dtls_tos_current);
268 		if (setsockopt(vpninfo->dtls_fd, vpninfo->dtls_tos_proto,
269 			       vpninfo->dtls_tos_optname, (void *)&tos, sizeof(tos)))
270 			vpn_perror(vpninfo, _("UDP setsockopt"));
271 		else
272 			vpninfo->dtls_tos_current = tos;
273 	}
274 	return 0;
275 }
276 
dtls_mainloop(struct openconnect_info * vpninfo,int * timeout,int readable)277 int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
278 {
279 	int work_done = 0;
280 	char magic_pkt;
281 
282 	if (vpninfo->dtls_need_reconnect) {
283 		vpninfo->dtls_need_reconnect = 0;
284 		dtls_reconnect(vpninfo);
285 		return 1;
286 	}
287 
288 	if (vpninfo->dtls_state == DTLS_CONNECTING) {
289 		dtls_try_handshake(vpninfo);
290 		return 0;
291 	}
292 
293 	if (vpninfo->dtls_state == DTLS_SLEEPING) {
294 		int when = vpninfo->new_dtls_started + vpninfo->dtls_attempt_period - time(NULL);
295 
296 		if (when <= 0) {
297 			vpn_progress(vpninfo, PRG_DEBUG, _("Attempt new DTLS connection\n"));
298 			if (connect_dtls_socket(vpninfo) < 0)
299 				*timeout = 1000;
300 		} else if ((when * 1000) < *timeout) {
301 			*timeout = when * 1000;
302 		}
303 		return 0;
304 	}
305 
306 	while (readable) {
307 		int len = MAX(16384, vpninfo->ip_info.mtu);
308 		unsigned char *buf;
309 
310 		if (!vpninfo->dtls_pkt) {
311 			vpninfo->dtls_pkt = malloc(sizeof(struct pkt) + len);
312 			if (!vpninfo->dtls_pkt) {
313 				vpn_progress(vpninfo, PRG_ERR, _("Allocation failed\n"));
314 				break;
315 			}
316 		}
317 
318 		buf = vpninfo->dtls_pkt->data - 1;
319 		len = DTLS_RECV(vpninfo->dtls_ssl, buf, len + 1);
320 		if (len <= 0)
321 			break;
322 
323 		vpn_progress(vpninfo, PRG_TRACE,
324 			     _("Received DTLS packet 0x%02x of %d bytes\n"),
325 			     buf[0], len);
326 
327 		vpninfo->dtls_times.last_rx = time(NULL);
328 
329 		switch (buf[0]) {
330 		case AC_PKT_DATA:
331 			vpninfo->dtls_pkt->len = len - 1;
332 			queue_packet(&vpninfo->incoming_queue, vpninfo->dtls_pkt);
333 			vpninfo->dtls_pkt = NULL;
334 			work_done = 1;
335 			break;
336 
337 		case AC_PKT_DPD_OUT:
338 			vpn_progress(vpninfo, PRG_DEBUG, _("Got DTLS DPD request\n"));
339 
340 			/* FIXME: What if the packet doesn't get through? */
341 			magic_pkt = AC_PKT_DPD_RESP;
342 			if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1)
343 				vpn_progress(vpninfo, PRG_ERR,
344 					     _("Failed to send DPD response. Expect disconnect\n"));
345 			continue;
346 
347 		case AC_PKT_DPD_RESP:
348 			vpn_progress(vpninfo, PRG_DEBUG, _("Got DTLS DPD response\n"));
349 			break;
350 
351 		case AC_PKT_KEEPALIVE:
352 			vpn_progress(vpninfo, PRG_DEBUG, _("Got DTLS Keepalive\n"));
353 			break;
354 
355 		case AC_PKT_COMPRESSED:
356 			if (!vpninfo->dtls_compr) {
357 				vpn_progress(vpninfo, PRG_ERR,
358 					     _("Compressed DTLS packet received when compression not enabled\n"));
359 				goto unknown_pkt;
360 			}
361 			decompress_and_queue_packet(vpninfo, vpninfo->dtls_compr,
362 						    vpninfo->dtls_pkt->data, len - 1);
363 			break;
364 		default:
365 			vpn_progress(vpninfo, PRG_ERR,
366 				     _("Unknown DTLS packet type %02x, len %d\n"),
367 				     buf[0], len);
368 			if (1) {
369 				/* Some versions of OpenSSL have bugs with receiving out-of-order
370 				 * packets. Not only do they wrongly decide to drop packets if
371 				 * two packets get swapped in transit, but they also _fail_ to
372 				 * drop the packet in non-blocking mode; instead they return
373 				 * the appropriate length of garbage. So don't abort... for now. */
374 				break;
375 			} else {
376 			unknown_pkt:
377 				vpninfo->quit_reason = "Unknown packet received";
378 				return 1;
379 			}
380 
381 		}
382 	}
383 
384 	switch (keepalive_action(&vpninfo->dtls_times, timeout)) {
385 	case KA_REKEY: {
386 		int ret;
387 
388 		vpn_progress(vpninfo, PRG_INFO, _("DTLS rekey due\n"));
389 
390 		if (vpninfo->dtls_times.rekey_method == REKEY_SSL) {
391 			time(&vpninfo->new_dtls_started);
392 			vpninfo->dtls_state = DTLS_CONNECTING;
393 			ret = dtls_try_handshake(vpninfo);
394 			if (ret) {
395 				vpn_progress(vpninfo, PRG_ERR, _("DTLS Rehandshake failed; reconnecting.\n"));
396 				return connect_dtls_socket(vpninfo);
397 			}
398 		}
399 
400 		return 1;
401 	}
402 
403 	case KA_DPD_DEAD:
404 		vpn_progress(vpninfo, PRG_ERR, _("DTLS Dead Peer Detection detected dead peer!\n"));
405 		/* Fall back to SSL, and start a new DTLS connection */
406 		dtls_reconnect(vpninfo);
407 		return 1;
408 
409 	case KA_DPD:
410 		vpn_progress(vpninfo, PRG_DEBUG, _("Send DTLS DPD\n"));
411 
412 		magic_pkt = AC_PKT_DPD_OUT;
413 		if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1)
414 			vpn_progress(vpninfo, PRG_ERR,
415 				     _("Failed to send DPD request. Expect disconnect\n"));
416 
417 		/* last_dpd will just have been set */
418 		vpninfo->dtls_times.last_tx = vpninfo->dtls_times.last_dpd;
419 		work_done = 1;
420 		break;
421 
422 	case KA_KEEPALIVE:
423 		/* No need to send an explicit keepalive
424 		   if we have real data to send */
425 		if (vpninfo->outgoing_queue.head)
426 			break;
427 
428 		vpn_progress(vpninfo, PRG_DEBUG, _("Send DTLS Keepalive\n"));
429 
430 		magic_pkt = AC_PKT_KEEPALIVE;
431 		if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1)
432 			vpn_progress(vpninfo, PRG_ERR,
433 				     _("Failed to send keepalive request. Expect disconnect\n"));
434 		time(&vpninfo->dtls_times.last_tx);
435 		work_done = 1;
436 		break;
437 
438 	case KA_NONE:
439 		;
440 	}
441 
442 	/* Service outgoing packet queue */
443 	unmonitor_write_fd(vpninfo, dtls);
444 	while (vpninfo->outgoing_queue.head) {
445 		struct pkt *this = dequeue_packet(&vpninfo->outgoing_queue);
446 		struct pkt *send_pkt = this;
447 		int ret;
448 
449 		/* If TOS optname is set, we want to copy the TOS/TCLASS header
450 		   to the outer UDP packet */
451 		if (vpninfo->dtls_tos_optname)
452 			udp_tos_update(vpninfo, this);
453 
454 		/* One byte of header */
455 		this->cstp.hdr[7] = AC_PKT_DATA;
456 
457 		/* We can compress into vpninfo->deflate_pkt unless CSTP
458 		 * currently has a compressed packet pending — which it
459 		 * shouldn't if DTLS is active. */
460 		if (vpninfo->dtls_compr &&
461 		    vpninfo->current_ssl_pkt != vpninfo->deflate_pkt &&
462 		    !compress_packet(vpninfo, vpninfo->dtls_compr, this)) {
463 				send_pkt = vpninfo->deflate_pkt;
464 				send_pkt->cstp.hdr[7] = AC_PKT_COMPRESSED;
465 		}
466 
467 #ifdef OPENCONNECT_OPENSSL
468 		ret = SSL_write(vpninfo->dtls_ssl, &send_pkt->cstp.hdr[7], send_pkt->len + 1);
469 		if (ret <= 0) {
470 			ret = SSL_get_error(vpninfo->dtls_ssl, ret);
471 
472 			if (ret == SSL_ERROR_WANT_WRITE) {
473 				monitor_write_fd(vpninfo, dtls);
474 				requeue_packet(&vpninfo->outgoing_queue, this);
475 			} else if (ret != SSL_ERROR_WANT_READ) {
476 				/* If it's a real error, kill the DTLS connection and
477 				   requeue the packet to be sent over SSL */
478 				vpn_progress(vpninfo, PRG_ERR,
479 					     _("DTLS got write error %d. Falling back to SSL\n"),
480 					     ret);
481 				openconnect_report_ssl_errors(vpninfo);
482 				dtls_reconnect(vpninfo);
483 				requeue_packet(&vpninfo->outgoing_queue, this);
484 				work_done = 1;
485 			}
486 			return work_done;
487 		}
488 #else /* GnuTLS */
489 		ret = gnutls_record_send(vpninfo->dtls_ssl, &send_pkt->cstp.hdr[7], send_pkt->len + 1);
490 		if (ret <= 0) {
491 			if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED) {
492 				vpn_progress(vpninfo, PRG_ERR,
493 					     _("DTLS got write error: %s. Falling back to SSL\n"),
494 					     gnutls_strerror(ret));
495 				dtls_reconnect(vpninfo);
496 				work_done = 1;
497 			} else {
498 				/* Wake me up when it becomes writeable */
499 				monitor_write_fd(vpninfo, dtls);
500 			}
501 
502 			requeue_packet(&vpninfo->outgoing_queue, this);
503 			return work_done;
504 		}
505 #endif
506 		time(&vpninfo->dtls_times.last_tx);
507 		vpn_progress(vpninfo, PRG_TRACE,
508 			     _("Sent DTLS packet of %d bytes; DTLS send returned %d\n"),
509 			     this->len, ret);
510 		free(this);
511 	}
512 
513 	return work_done;
514 }
515 
516 /* This symbol is missing in glibc < 2.22 (bug 18643). */
517 #if defined(__linux__) && !defined(HAVE_IPV6_PATHMTU)
518 # define HAVE_IPV6_PATHMTU 1
519 # define IPV6_PATHMTU 61
520 #endif
521 
522 #define PKT_INTERVAL_MS 50
523 
524 /* Performs a binary search to detect MTU.
525  * @buf: is preallocated with MTU size
526  *
527  * Returns: new MTU or 0
528  */
probe_mtu(struct openconnect_info * vpninfo,unsigned char * buf)529 static int probe_mtu(struct openconnect_info *vpninfo, unsigned char *buf)
530 {
531 	int max, min, cur, ret, absolute_min, last;
532 	int tries = 0; /* Number of loops in bin search - includes resends */
533 	uint32_t id, id_len;
534 	struct timeval start_tv, now_tv, last_tv;
535 
536 	absolute_min = 576;
537 	if (vpninfo->ip_info.addr6)
538 		absolute_min = 1280;
539 
540 	/* We'll assume that it is at least functional, and permits the bare
541 	 * minimum MTU for the protocol(s) it transports. All else is mad. */
542 	min = absolute_min;
543 
544 	/* First send a probe at the configured maximum. Most of the time, this
545 	   one will probably work. */
546 	last = cur = max = vpninfo->ip_info.mtu;
547 
548 	if (max <= min)
549 		goto fail;
550 
551 	/* Generate unique ID */
552 	if (openconnect_random(&id, sizeof(id)) < 0)
553 		goto fail;
554 
555 	vpn_progress(vpninfo, PRG_DEBUG,
556 		     _("Initiating MTU detection (min=%d, max=%d)\n"), min, max);
557 
558 	gettimeofday(&start_tv, NULL);
559 	last_tv = start_tv;
560 
561 	while (1) {
562 		int wait_ms;
563 
564 #ifdef HAVE_IPV6_PATHMTU
565 		if (vpninfo->peer_addr->sa_family == AF_INET6) {
566 			struct ip6_mtuinfo mtuinfo;
567 			socklen_t len = sizeof(mtuinfo);
568 			int newmax;
569 
570 			if (getsockopt(vpninfo->dtls_fd, IPPROTO_IPV6, IPV6_PATHMTU, &mtuinfo, &len) >= 0) {
571 				newmax = mtuinfo.ip6m_mtu;
572 				if (newmax > 0) {
573 					newmax = dtls_set_mtu(vpninfo, newmax) - /*ipv6*/40 - /*udp*/20 - /*oc dtls*/1;
574 					if (absolute_min > newmax)
575 						goto fail;
576 					if (max > newmax)
577 						max = newmax;
578 					if (cur > newmax)
579 						cur = newmax;
580 				}
581 			}
582 		}
583 #endif
584 
585 		buf[0] = AC_PKT_DPD_OUT;
586 		id_len = id + cur;
587 		memcpy(&buf[1], &id_len, sizeof(id_len));
588 
589 		vpn_progress(vpninfo, PRG_TRACE,
590 			     _("Sending MTU DPD probe (%u bytes)\n"), cur);
591 		ret = openconnect_dtls_write(vpninfo, buf, cur + 1);
592 		if (ret != cur + 1) {
593 			vpn_progress(vpninfo, PRG_ERR,
594 				     _("Failed to send DPD request (%d %d)\n"), cur, ret);
595 			if (cur == max) {
596 				max = --cur;
597 				if (cur >= absolute_min)
598 					continue;
599 			}
600 			goto fail;
601 		}
602 		if (last == cur)
603 			tries++;
604 		else {
605 			tries = 0;
606 			last = cur;
607 		}
608 
609 		memset(buf, 0, sizeof(id)+1);
610 	keep_waiting:
611 		gettimeofday(&now_tv, NULL);
612 
613 		if (now_tv.tv_sec > start_tv.tv_sec + 10) {
614                         if (absolute_min == min) {
615                                 /* Hm, we never got *anything* back successfully? */
616                                 vpn_progress(vpninfo, PRG_ERR,
617                                              _("Too long time in MTU detect loop; assuming negotiated MTU.\n"));
618                                 goto fail;
619                         } else {
620                                 vpn_progress(vpninfo, PRG_ERR,
621                                              _("Too long time in MTU detect loop; MTU set to %d.\n"), min);
622 				ret = min;
623 				goto out;
624                         }
625 		}
626 
627 
628 		wait_ms = PKT_INTERVAL_MS -
629 			((now_tv.tv_sec - last_tv.tv_sec) * 1000) -
630 			((now_tv.tv_usec - last_tv.tv_usec) / 1000);
631 		if (wait_ms < 0 || wait_ms > PKT_INTERVAL_MS)
632 			wait_ms = PKT_INTERVAL_MS;
633 
634 		ret = openconnect_dtls_read(vpninfo, buf, max+1, wait_ms);
635 		if (ret > 0 && (buf[0] != AC_PKT_DPD_RESP || !memcpy(&id_len, &buf[1], sizeof(id_len)) ||
636 				id_len != id + ret - 1)) {
637 			vpn_progress(vpninfo, PRG_DEBUG,
638 				     _("Received unexpected packet (%.2x) in MTU detection; skipping.\n"), (unsigned)buf[0]);
639 			goto keep_waiting;
640 		}
641 
642 		if (ret == -ETIMEDOUT) {
643 			if (tries >= 6) {
644 				vpn_progress(vpninfo, PRG_DEBUG,
645 					     _("No response to size %u after %d tries; declare MTU is %u\n"),
646 					     last, tries, min);
647 				ret = min;
648 				goto out;
649 			}
650 		} else if (ret < 0) {
651 			vpn_progress(vpninfo, PRG_ERR,
652 				     _("Failed to recv DPD request (%d)\n"), ret);
653 			goto fail;
654 		} else if (ret > 0) {
655 			vpn_progress(vpninfo, PRG_TRACE,
656 				     _("Received MTU DPD probe (%u bytes)\n"), ret - 1);
657 			ret--;
658 			tries = 0;
659 		}
660 
661 		if (ret == max)
662 			goto out;
663 
664 		if (ret > min) {
665 			min = ret;
666 			if (min >= last) {
667 				cur = (min + max + 1) / 2;
668 			} else {
669 				cur = (min + last + 1) / 2;
670 			}
671 		} else {
672 			cur = (min + last + 1) / 2;
673 		}
674 	}
675  fail:
676 	ret = 0;
677  out:
678 	return ret;
679 }
680 
681 
682 
dtls_detect_mtu(struct openconnect_info * vpninfo)683 void dtls_detect_mtu(struct openconnect_info *vpninfo)
684 {
685 	int mtu = vpninfo->ip_info.mtu;
686 	int prev_mtu = vpninfo->ip_info.mtu;
687 	unsigned char *buf;
688 
689 	if (vpninfo->ip_info.mtu < 1 + sizeof(uint32_t))
690 		return;
691 
692 	/* detect MTU */
693 	buf = calloc(1, 1 + vpninfo->ip_info.mtu);
694 	if (!buf) {
695 		vpn_progress(vpninfo, PRG_ERR, _("Allocation failed\n"));
696 		return;
697 	}
698 
699 	mtu = probe_mtu(vpninfo, buf);
700 	if (mtu == 0)
701 		goto skip_mtu;
702 
703 	vpninfo->ip_info.mtu = mtu;
704 	if (prev_mtu != vpninfo->ip_info.mtu) {
705 		vpn_progress(vpninfo, PRG_INFO,
706 		     _("Detected MTU of %d bytes (was %d)\n"), vpninfo->ip_info.mtu, prev_mtu);
707 	} else {
708 		vpn_progress(vpninfo, PRG_DEBUG,
709 		     _("No change in MTU after detection (was %d)\n"), prev_mtu);
710 	}
711 
712  skip_mtu:
713 	free(buf);
714 }
715 
716