static int dtls_restart(struct openconnect_info *vpninfo) { if (vpninfo->dtls_ssl) { SSL_free(vpninfo->dtls_ssl); close(vpninfo->dtls_fd); FD_CLR(vpninfo->dtls_fd, &vpninfo->select_rfds); FD_CLR(vpninfo->dtls_fd, &vpninfo->select_wfds); FD_CLR(vpninfo->dtls_fd, &vpninfo->select_efds); vpninfo->dtls_ssl = NULL; vpninfo->dtls_fd = -1; } return connect_dtls_socket(vpninfo); }
int setup_dtls(struct openconnect_info *vpninfo) { struct vpn_option *dtls_opt = vpninfo->dtls_options; int dtls_port = 0; while (dtls_opt) { vpn_progress(vpninfo, PRG_TRACE, _("DTLS option %s : %s\n"), dtls_opt->option, dtls_opt->value); if (!strcmp(dtls_opt->option + 7, "Port")) { dtls_port = atol(dtls_opt->value); } else if (!strcmp(dtls_opt->option + 7, "Keepalive")) { vpninfo->dtls_times.keepalive = atol(dtls_opt->value); } else if (!strcmp(dtls_opt->option + 7, "DPD")) { int j = atol(dtls_opt->value); if (j && (!vpninfo->dtls_times.dpd || j < vpninfo->dtls_times.dpd)) vpninfo->dtls_times.dpd = j; } else if (!strcmp(dtls_opt->option + 7, "Rekey-Time")) { vpninfo->dtls_times.rekey = atol(dtls_opt->value); } else if (!strcmp(dtls_opt->option + 7, "CipherSuite")) { vpninfo->dtls_cipher = strdup(dtls_opt->value); } dtls_opt = dtls_opt->next; } if (!dtls_port) { vpninfo->dtls_attempt_period = 0; return -EINVAL; } vpninfo->dtls_addr = malloc(vpninfo->peer_addrlen); if (!vpninfo->dtls_addr) { vpninfo->dtls_attempt_period = 0; return -ENOMEM; } memcpy(vpninfo->dtls_addr, vpninfo->peer_addr, vpninfo->peer_addrlen); if (vpninfo->peer_addr->sa_family == AF_INET) { struct sockaddr_in *sin = (void *)vpninfo->dtls_addr; sin->sin_port = htons(dtls_port); } else if (vpninfo->peer_addr->sa_family == AF_INET6) { struct sockaddr_in6 *sin = (void *)vpninfo->dtls_addr; sin->sin6_port = htons(dtls_port); } else { vpn_progress(vpninfo, PRG_ERR, _("Unknown protocol family %d. Cannot do DTLS\n"), vpninfo->peer_addr->sa_family); vpninfo->dtls_attempt_period = 0; return -EINVAL; } if (connect_dtls_socket(vpninfo)) return -EINVAL; vpn_progress(vpninfo, PRG_TRACE, _("DTLS connected. DPD %d, Keepalive %d\n"), vpninfo->dtls_times.dpd, vpninfo->dtls_times.keepalive); return 0; }
int openconnect_setup_dtls(struct openconnect_info *vpninfo, int dtls_attempt_period) { struct oc_vpn_option *dtls_opt = vpninfo->dtls_options; int dtls_port = 0; vpninfo->dtls_attempt_period = dtls_attempt_period; if (!dtls_attempt_period) return 0; #if defined(OPENCONNECT_GNUTLS) && defined(DTLS_OPENSSL) /* If we're using GnuTLS for authentication but OpenSSL for DTLS, we'll need to initialise OpenSSL now... */ SSL_library_init(); ERR_clear_error(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); #endif while (dtls_opt) { vpn_progress(vpninfo, PRG_TRACE, _("DTLS option %s : %s\n"), dtls_opt->option, dtls_opt->value); if (!strcmp(dtls_opt->option + 7, "Port")) { dtls_port = atol(dtls_opt->value); } else if (!strcmp(dtls_opt->option + 7, "Keepalive")) { vpninfo->dtls_times.keepalive = atol(dtls_opt->value); } else if (!strcmp(dtls_opt->option + 7, "DPD")) { int j = atol(dtls_opt->value); if (j && (!vpninfo->dtls_times.dpd || j < vpninfo->dtls_times.dpd)) vpninfo->dtls_times.dpd = j; } else if (!strcmp(dtls_opt->option + 7, "Rekey-Time")) { vpninfo->dtls_times.rekey = atol(dtls_opt->value); } else if (!strcmp(dtls_opt->option + 7, "CipherSuite")) { vpninfo->dtls_cipher = strdup(dtls_opt->value); } dtls_opt = dtls_opt->next; } if (!dtls_port) { vpninfo->dtls_attempt_period = 0; return -EINVAL; } vpninfo->dtls_addr = malloc(vpninfo->peer_addrlen); if (!vpninfo->dtls_addr) { vpninfo->dtls_attempt_period = 0; return -ENOMEM; } memcpy(vpninfo->dtls_addr, vpninfo->peer_addr, vpninfo->peer_addrlen); if (vpninfo->peer_addr->sa_family == AF_INET) { struct sockaddr_in *sin = (void *)vpninfo->dtls_addr; sin->sin_port = htons(dtls_port); } else if (vpninfo->peer_addr->sa_family == AF_INET6) { struct sockaddr_in6 *sin = (void *)vpninfo->dtls_addr; sin->sin6_port = htons(dtls_port); } else { vpn_progress(vpninfo, PRG_ERR, _("Unknown protocol family %d. Cannot do DTLS\n"), vpninfo->peer_addr->sa_family); vpninfo->dtls_attempt_period = 0; return -EINVAL; } if (connect_dtls_socket(vpninfo)) return -EINVAL; vpn_progress(vpninfo, PRG_TRACE, _("DTLS initialised. DPD %d, Keepalive %d\n"), vpninfo->dtls_times.dpd, vpninfo->dtls_times.keepalive); return 0; }
static int dtls_restart(struct openconnect_info *vpninfo) { dtls_close(vpninfo, 0); return connect_dtls_socket(vpninfo); }
int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout) { int work_done = 0; char magic_pkt; if (vpninfo->dtls_state == DTLS_CONNECTING) { dtls_try_handshake(vpninfo); return 0; } if (vpninfo->dtls_state == DTLS_SLEEPING) { int when = vpninfo->new_dtls_started + vpninfo->dtls_attempt_period - time(NULL); if (when <= 0) { vpn_progress(vpninfo, PRG_DEBUG, _("Attempt new DTLS connection\n")); connect_dtls_socket(vpninfo); } else if ((when * 1000) < *timeout) { *timeout = when * 1000; } return 0; } while (1) { int len = vpninfo->ip_info.mtu; unsigned char *buf; if (!dtls_pkt || len > dtls_pkt_max) { realloc_inplace(dtls_pkt, sizeof(struct pkt) + len); if (!dtls_pkt) { vpn_progress(vpninfo, PRG_ERR, "Allocation failed\n"); break; } dtls_pkt_max = len; } buf = dtls_pkt->data - 1; len = DTLS_RECV(vpninfo->dtls_ssl, buf, len + 1); if (len <= 0) break; vpn_progress(vpninfo, PRG_TRACE, _("Received DTLS packet 0x%02x of %d bytes\n"), buf[0], len); vpninfo->dtls_times.last_rx = time(NULL); switch (buf[0]) { case AC_PKT_DATA: dtls_pkt->len = len - 1; queue_packet(&vpninfo->incoming_queue, dtls_pkt); dtls_pkt = NULL; work_done = 1; break; case AC_PKT_DPD_OUT: vpn_progress(vpninfo, PRG_DEBUG, _("Got DTLS DPD request\n")); /* FIXME: What if the packet doesn't get through? */ magic_pkt = AC_PKT_DPD_RESP; if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1) vpn_progress(vpninfo, PRG_ERR, _("Failed to send DPD response. Expect disconnect\n")); continue; case AC_PKT_DPD_RESP: vpn_progress(vpninfo, PRG_DEBUG, _("Got DTLS DPD response\n")); break; case AC_PKT_KEEPALIVE: vpn_progress(vpninfo, PRG_DEBUG, _("Got DTLS Keepalive\n")); break; default: vpn_progress(vpninfo, PRG_ERR, _("Unknown DTLS packet type %02x, len %d\n"), buf[0], len); if (1) { /* Some versions of OpenSSL have bugs with receiving out-of-order * packets. Not only do they wrongly decide to drop packets if * two packets get swapped in transit, but they also _fail_ to * drop the packet in non-blocking mode; instead they return * the appropriate length of garbage. So don't abort... for now. */ break; } else { vpninfo->quit_reason = "Unknown packet received"; return 1; } } } switch (keepalive_action(&vpninfo->dtls_times, timeout)) { case KA_REKEY: { int ret; vpn_progress(vpninfo, PRG_INFO, _("DTLS rekey due\n")); if (vpninfo->dtls_times.rekey_method == REKEY_SSL) { time(&vpninfo->new_dtls_started); vpninfo->dtls_state = DTLS_CONNECTING; ret = dtls_try_handshake(vpninfo); if (ret) { vpn_progress(vpninfo, PRG_ERR, _("DTLS Rehandshake failed; reconnecting.\n")); return connect_dtls_socket(vpninfo); } } return 1; } case KA_DPD_DEAD: vpn_progress(vpninfo, PRG_ERR, _("DTLS Dead Peer Detection detected dead peer!\n")); /* Fall back to SSL, and start a new DTLS connection */ dtls_reconnect(vpninfo); return 1; case KA_DPD: vpn_progress(vpninfo, PRG_DEBUG, _("Send DTLS DPD\n")); magic_pkt = AC_PKT_DPD_OUT; if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1) vpn_progress(vpninfo, PRG_ERR, _("Failed to send DPD request. Expect disconnect\n")); /* last_dpd will just have been set */ vpninfo->dtls_times.last_tx = vpninfo->dtls_times.last_dpd; work_done = 1; break; case KA_KEEPALIVE: /* No need to send an explicit keepalive if we have real data to send */ if (vpninfo->outgoing_queue) break; vpn_progress(vpninfo, PRG_DEBUG, _("Send DTLS Keepalive\n")); magic_pkt = AC_PKT_KEEPALIVE; if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1) vpn_progress(vpninfo, PRG_ERR, _("Failed to send keepalive request. Expect disconnect\n")); time(&vpninfo->dtls_times.last_tx); work_done = 1; break; case KA_NONE: ; } /* Service outgoing packet queue */ unmonitor_write_fd(vpninfo, dtls); while (vpninfo->outgoing_queue) { struct pkt *this = vpninfo->outgoing_queue; int ret; vpninfo->outgoing_queue = this->next; vpninfo->outgoing_qlen--; /* One byte of header */ this->hdr[7] = AC_PKT_DATA; #if defined(DTLS_OPENSSL) ret = SSL_write(vpninfo->dtls_ssl, &this->hdr[7], this->len + 1); if (ret <= 0) { ret = SSL_get_error(vpninfo->dtls_ssl, ret); if (ret == SSL_ERROR_WANT_WRITE) { monitor_write_fd(vpninfo, dtls); vpninfo->outgoing_queue = this; vpninfo->outgoing_qlen++; } else if (ret != SSL_ERROR_WANT_READ) { /* If it's a real error, kill the DTLS connection and requeue the packet to be sent over SSL */ vpn_progress(vpninfo, PRG_ERR, _("DTLS got write error %d. Falling back to SSL\n"), ret); openconnect_report_ssl_errors(vpninfo); dtls_reconnect(vpninfo); vpninfo->outgoing_queue = this; vpninfo->outgoing_qlen++; work_done = 1; } return work_done; } #elif defined(DTLS_GNUTLS) ret = gnutls_record_send(vpninfo->dtls_ssl, &this->hdr[7], this->len + 1); if (ret <= 0) { if (ret != GNUTLS_E_AGAIN) { vpn_progress(vpninfo, PRG_ERR, _("DTLS got write error: %s. Falling back to SSL\n"), gnutls_strerror(ret)); dtls_reconnect(vpninfo); vpninfo->outgoing_queue = this; vpninfo->outgoing_qlen++; work_done = 1; } else if (gnutls_record_get_direction(vpninfo->dtls_ssl)) { monitor_write_fd(vpninfo, dtls); vpninfo->outgoing_queue = this; vpninfo->outgoing_qlen++; } return work_done; } #endif time(&vpninfo->dtls_times.last_tx); vpn_progress(vpninfo, PRG_TRACE, _("Sent DTLS packet of %d bytes; DTLS send returned %d\n"), this->len, ret); free(this); } return work_done; }
int dtls_reconnect(struct openconnect_info *vpninfo) { dtls_close(vpninfo); vpninfo->dtls_state = DTLS_SLEEPING; return connect_dtls_socket(vpninfo); }