diff --git a/src/tlslib.c b/src/tlslib.c index 3c10f55b..dfb3453e 100644 --- a/src/tlslib.c +++ b/src/tlslib.c @@ -38,12 +38,22 @@ ssize_t tls_send(gnutls_session_t session, const void *data, size_t data_size) { int ret; + int left = data_size; + const uint8_t* p = data; - do { - ret = gnutls_record_send(session, data, data_size); - } while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + while(left > 0) { + ret = gnutls_record_send(session, p, data_size); + if (ret < 0 && gnutls_error_is_fatal(ret) != 0) { + return ret; + } - return ret; + if (ret > 0) { + left -= ret; + p += ret; + } + } + + return data_size; } ssize_t tls_send_file(gnutls_session_t session, const char *file) diff --git a/src/vpn.h b/src/vpn.h index 2f35b782..735b7fac 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -105,6 +105,7 @@ typedef struct worker_st { int udp_fd; udp_port_state_t udp_state; unsigned int udp_port; + int udp_port_proto; /* the following are set only if authentication is complete */ char tun_name[IFNAMSIZ]; diff --git a/src/worker-vpn.c b/src/worker-vpn.c index cc00c5df..7248194a 100644 --- a/src/worker-vpn.c +++ b/src/worker-vpn.c @@ -590,7 +590,6 @@ int proto; return -1; } - proto = ((struct sockaddr*)&stcp)->sa_family; s = socket(proto, SOCK_DGRAM, IPPROTO_UDP); @@ -662,6 +661,7 @@ int proto; } else { ws->udp_port = ntohs(SA_IN6_PORT(&si)); } + ws->udp_port_proto = proto; ws->udp_fd = s; @@ -671,6 +671,27 @@ fail: return -1; } +static ssize_t sock_send(int sockfd, const void *buf, size_t len) +{ +int left = len; +int ret; +const uint8_t * p = buf; + + while(left > 0) { + ret = send(sockfd, p, left, 0); + if (ret == -1) { + if (errno != EAGAIN && errno != EINTR) + return ret; + } + + if (ret > 0) { + left -= ret; + p += ret; + } + } + + return len; +} static int connect_handler(worker_st *ws) { @@ -681,11 +702,13 @@ int l, e; int max; unsigned i; struct vpn_st vinfo; -uint8_t buffer[1024]; +uint8_t buffer[16*1024]; unsigned int buffer_size; char *p; unsigned tls_pending, dtls_pending = 0; time_t udp_recv_time = 0; +unsigned mtu_overhead, effective_mtu = 0; +gnutls_session_t ts; if (req->cookie_set == 0) { oclog(ws, LOG_INFO, "Connect request without authentication"); @@ -796,6 +819,11 @@ time_t udp_recv_time = 0; tls_puts(ws->session, "X-CSTP-Banner: Hello there\r\n"); tls_puts(ws->session, "\r\n"); + if (ws->udp_port_proto == AF_INET) + mtu_overhead = 20+8; + else + mtu_overhead = 40+8; + for(;;) { FD_ZERO(&rfds); @@ -819,14 +847,27 @@ time_t udp_recv_time = 0; if (ret <= 0) break; } - + if (FD_ISSET(ws->tun_fd, &rfds)) { - l = read(ws->tun_fd, buffer + 8, sizeof(buffer) - 8); - if (l <= 0) { - e = errno; - oclog(ws, LOG_ERR, "Received corrupt data from tun (%d): %s", l, strerror(e)); - exit(1); + if (ws->udp_state == UP_ACTIVE) { + l = effective_mtu; + ts = ws->dtls_session; + } else { + l = sizeof(buffer); + ts = ws->session; } + + l = recv(ws->tun_fd, buffer + 8, l - 8, 0); + if (l < 0) { + e = errno; + + if (e != EAGAIN && e != EINTR) { + oclog(ws, LOG_ERR, "Received corrupt data from tun (%d): %s", l, strerror(e)); + exit(1); + } + continue; + } + buffer[0] = 'S'; buffer[1] = 'T'; buffer[2] = 'F'; @@ -836,11 +877,18 @@ time_t udp_recv_time = 0; buffer[6] = 0; buffer[7] = 0; - if (ws->udp_state == UP_ACTIVE) - ret = tls_send(ws->dtls_session, buffer, l + 8); - else - ret = tls_send(ws->session, buffer, l + 8); + ret = tls_send(ts, buffer, l + 8); GNUTLS_FATAL_ERR(ret); + + if (ret == GNUTLS_E_LARGE_PACKET) { + /* XXX: we have to do something better than that. + * adjust mtu */ + if (effective_mtu > 100) + effective_mtu -= 32; + + ret = tls_send(ws->session, buffer, l + 8); + GNUTLS_FATAL_ERR(ret); + } } if (FD_ISSET(ws->conn_fd, &rfds) || tls_pending != 0) { @@ -879,6 +927,8 @@ time_t udp_recv_time = 0; ret = setup_dtls_connection(ws); if (ret < 0) exit(1); + + gnutls_dtls_set_mtu (ws->dtls_session, vinfo.mtu-mtu_overhead); break; case UP_HANDSHAKE: ret = gnutls_handshake(ws->dtls_session); @@ -889,8 +939,10 @@ time_t udp_recv_time = 0; break; } - if (ret == 0) + if (ret == 0) { ws->udp_state = UP_ACTIVE; + effective_mtu = gnutls_dtls_get_data_mtu(ws->dtls_session); + } break; default: @@ -993,17 +1045,13 @@ int pktlen, ret, e; oclog(ws, LOG_INFO, "Received BYE packet\n"); break; case AC_PKT_DATA: - ret = write(ws->tun_fd, buf + 8, pktlen); + ret = sock_send(ws->tun_fd, buf + 8, pktlen); if (ret == -1) { e = errno; oclog(ws, LOG_ERR, "Could not write data to tun: %s", strerror(e)); return -1; } - if (ret != pktlen) { - oclog(ws, LOG_ERR, "Could not write all data to tun"); - exit(1); - } break; }