void Allocator::realloc(Pointer &p, size_t new_size) { index_t pointer_id = p.get_id(); if(pointer_id == -1) { p = alloc(new_size); return; } size_t original_blocks = get_size_blocks(pointer_id); size_t required_blocks = bytes_to_blocks(new_size); if(required_blocks <= original_blocks){ shrink(pointer_id, required_blocks); } else { if(!realloc_inplace(p, required_blocks)){ realloc_move(p, required_blocks); } } }
int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout) { int work_done = 0; char magic_pkt; 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_TRACE, _("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_TRACE, _("Got DTLS DPD response\n")); break; case AC_PKT_KEEPALIVE: vpn_progress(vpninfo, PRG_TRACE, _("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")); /* There ought to be a method of rekeying DTLS without tearing down the CSTP session and restarting, but we don't (yet) know it */ ret = cstp_reconnect(vpninfo); if (ret) { vpn_progress(vpninfo, PRG_ERR, _("Reconnect failed\n")); vpninfo->quit_reason = "CSTP reconnect failed"; return ret; } if (dtls_restart(vpninfo)) vpn_progress(vpninfo, PRG_ERR, _("DTLS rekey failed\n")); 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_restart(vpninfo); return 1; case KA_DPD: vpn_progress(vpninfo, PRG_TRACE, _("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_TRACE, _("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 */ FD_CLR(vpninfo->dtls_fd, &vpninfo->select_wfds); 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) { FD_SET(vpninfo->dtls_fd, &vpninfo->select_wfds); 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_restart(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_restart(vpninfo); vpninfo->outgoing_queue = this; vpninfo->outgoing_qlen++; work_done = 1; } else if (gnutls_record_get_direction(vpninfo->dtls_ssl)) { FD_SET(vpninfo->dtls_fd, &vpninfo->select_wfds); 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; }