/** * gnutls_record_send: * @session: is a #gnutls_session_t structure. * @data: contains the data to send * @data_size: is the length of the data * * This function has the similar semantics with send(). The only * difference is that it accepts a GnuTLS session, and uses different * error codes. * Note that if the send buffer is full, send() will block this * function. See the send() documentation for more information. * * You can replace the default push function which is send(), by using * gnutls_transport_set_push_function(). * * If the EINTR is returned by the internal push function * then %GNUTLS_E_INTERRUPTED will be returned. If * %GNUTLS_E_INTERRUPTED or %GNUTLS_E_AGAIN is returned, you must * call this function again, with the exact same parameters; alternatively * you could provide a %NULL pointer for data, and 0 for * size. cf. gnutls_record_get_direction(). * * Note that in DTLS this function will return the %GNUTLS_E_LARGE_PACKET * error code if the send data exceed the data MTU value - as returned * by gnutls_dtls_get_data_mtu(). The errno value EMSGSIZE * also maps to %GNUTLS_E_LARGE_PACKET. * Note that since 3.2.13 this function can be called under cork in DTLS * mode, and will refuse to send data over the MTU size by returning * %GNUTLS_E_LARGE_PACKET. * * Returns: The number of bytes sent, or a negative error code. The * number of bytes sent might be less than @data_size. The maximum * number of bytes this function can send in a single call depends * on the negotiated maximum record size. **/ ssize_t gnutls_record_send(gnutls_session_t session, const void *data, size_t data_size) { if (session->internals.record_flush_mode == RECORD_FLUSH) { return _gnutls_send_int(session, GNUTLS_APPLICATION_DATA, -1, EPOCH_WRITE_CURRENT, data, data_size, MBUFFER_FLUSH); } else { /* GNUTLS_CORKED */ int ret; if (IS_DTLS(session)) { if (data_size + session->internals.record_presend_buffer.length > gnutls_dtls_get_data_mtu(session)) { return gnutls_assert_val(GNUTLS_E_LARGE_PACKET); } } ret = _gnutls_buffer_append_data(&session->internals. record_presend_buffer, data, data_size); if (ret < 0) return gnutls_assert_val(ret); return data_size; } }
/* This function fragments and transmits a previously buffered * outgoing message. It accepts mtu_data which is a buffer to * be reused (should be set to NULL initially). */ static inline int transmit_message (gnutls_session_t session, mbuffer_st *bufel, uint8_t **buf) { uint8_t *data, *mtu_data; int ret = 0; unsigned int offset, frag_len, data_size; const unsigned int mtu = gnutls_dtls_get_data_mtu(session) - DTLS_HANDSHAKE_HEADER_SIZE; if (bufel->type == GNUTLS_CHANGE_CIPHER_SPEC) { _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d)\n", session, bufel->handshake_sequence, _gnutls_handshake2str (bufel->htype), bufel->htype); return _gnutls_send_int (session, bufel->type, -1, bufel->epoch, _mbuffer_get_uhead_ptr(bufel), _mbuffer_get_uhead_size(bufel), 0); } if (*buf == NULL) *buf = gnutls_malloc(mtu + DTLS_HANDSHAKE_HEADER_SIZE); if (*buf == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); mtu_data = *buf; data = _mbuffer_get_udata_ptr( bufel); data_size = _mbuffer_get_udata_size(bufel); /* Write fixed headers */ /* Handshake type */ mtu_data[0] = (uint8_t) bufel->htype; /* Total length */ _gnutls_write_uint24 (data_size, &mtu_data[1]); /* Handshake sequence */ _gnutls_write_uint16 (bufel->handshake_sequence, &mtu_data[4]); /* Chop up and send handshake message into mtu-size pieces. */ for (offset=0; offset <= data_size; offset += mtu) { /* Calculate fragment length */ if(offset + mtu > data_size) frag_len = data_size - offset; else frag_len = mtu; /* Fragment offset */ _gnutls_write_uint24 (offset, &mtu_data[6]); /* Fragment length */ _gnutls_write_uint24 (frag_len, &mtu_data[9]); memcpy (&mtu_data[DTLS_HANDSHAKE_HEADER_SIZE], data+offset, frag_len); _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d) with " "length: %u, offset: %u, fragment length: %u\n", session, bufel->handshake_sequence, _gnutls_handshake2str (bufel->htype), bufel->htype, data_size, offset, frag_len); ret = _gnutls_send_int (session, bufel->type, bufel->htype, bufel->epoch, mtu_data, DTLS_HANDSHAKE_HEADER_SIZE + frag_len, 0); if (ret < 0) { gnutls_assert(); break; } } return ret; }
static void client(int fd, const char *prio, unsigned overhead) { int ret; gnutls_anon_client_credentials_t anoncred; gnutls_certificate_credentials_t x509_cred; gnutls_session_t session; /* Need to enable anonymous KX specifically. */ global_init(); if (debug) { gnutls_global_set_log_function(client_log_func); gnutls_global_set_log_level(7); } gnutls_anon_allocate_client_credentials(&anoncred); gnutls_certificate_allocate_credentials(&x509_cred); /* Initialize TLS session */ gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_DATAGRAM); /* Use default priorities */ ret = gnutls_priority_set_direct(session, prio, NULL); if (ret < 0) { fail("error in setting priority: %s\n", prio); exit(1); } /* put the anonymous credentials to the current session */ gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred); gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred); gnutls_transport_set_int(session, fd); /* Perform the TLS handshake */ do { ret = gnutls_handshake(session); } while (ret < 0 && gnutls_error_is_fatal(ret) == 0); if (ret < 0) { fail("client: Handshake failed for %s\n", prio); gnutls_perror(ret); exit(1); } else { if (debug) success("client: Handshake was completed\n"); } if (debug) success("client: TLS version is: %s\n", gnutls_protocol_get_name (gnutls_protocol_get_version(session))); gnutls_dtls_set_mtu(session, MTU); ret = gnutls_dtls_get_data_mtu(session); if (MTU - ret != (int) overhead) { fail("overhead for %s is %d, expected %u\n", prio, MTU - ret, overhead); exit(1); } close(fd); gnutls_deinit(session); gnutls_anon_free_client_credentials(anoncred); gnutls_certificate_free_credentials(x509_cred); gnutls_global_deinit(); }
static void server(int fd) { int ret; char buffer[MAX_BUF + 1]; gnutls_session_t session; gnutls_anon_server_credentials_t anoncred; /* this must be called once in the program */ global_init(); if (debug) { gnutls_global_set_log_function(server_log_func); gnutls_global_set_log_level(4711); } gnutls_anon_allocate_server_credentials(&anoncred); gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM); gnutls_dtls_set_mtu(session, 1500); /* avoid calling all the priority functions, since the defaults * are adequate. */ gnutls_priority_set_direct(session, "NONE:+VERS-DTLS1.0:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-ECDH:+CURVE-ALL", NULL); gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred); gnutls_transport_set_int(session, fd); do { ret = gnutls_handshake(session); } while (ret < 0 && gnutls_error_is_fatal(ret) == 0); if (ret < 0) { close(fd); gnutls_deinit(session); terminate(); fail("server: Handshake has failed (%s)\n\n", gnutls_strerror(ret)); } if (debug) success("server: Handshake was completed\n"); if (debug) success("server: TLS version is: %s\n", gnutls_protocol_get_name (gnutls_protocol_get_version(session))); /* see the Getting peer's information example */ /* print_info(session); */ /* avoid uninitialized warnings */ memset(buffer, 1, sizeof(buffer)); ret = gnutls_record_send(session, buffer, gnutls_dtls_get_data_mtu(session) + 12); if (ret != GNUTLS_E_LARGE_PACKET) { terminate(); fail("send[%d]: %s\n", __LINE__, gnutls_strerror(ret)); } ret = gnutls_record_send(session, buffer, gnutls_dtls_get_data_mtu(session) + 5048); if (ret != GNUTLS_E_LARGE_PACKET) { terminate(); fail("send[%d]: %s\n", __LINE__, gnutls_strerror(ret)); } ret = gnutls_record_send(session, buffer, gnutls_dtls_get_data_mtu(session)); if (ret < 0) { terminate(); fail("send[%d]: %s\n", __LINE__, gnutls_strerror(ret)); } gnutls_dtls_set_mtu(session, MAX_MTU); ret = gnutls_record_send(session, buffer, gnutls_dtls_get_data_mtu(session) + 12); if (ret != GNUTLS_E_LARGE_PACKET) { terminate(); fail("send[%d]: %s\n", __LINE__, gnutls_strerror(ret)); } ret = gnutls_record_send(session, buffer, gnutls_dtls_get_data_mtu(session) + 5048); if (ret != GNUTLS_E_LARGE_PACKET) { terminate(); fail("send[%d]: %s\n", __LINE__, gnutls_strerror(ret)); } ret = gnutls_record_send(session, buffer, gnutls_dtls_get_data_mtu(session)); if (ret > 16384 || ret < 0) { terminate(); fail("send[%d]: %s\n", __LINE__, gnutls_strerror(ret)); } /* test cork and uncork */ gnutls_record_cork(session); ret = gnutls_record_send(session, buffer, gnutls_dtls_get_data_mtu(session)); if (ret < 0) { terminate(); fail("send[%d]: %s\n", __LINE__, gnutls_strerror(ret)); } ret = gnutls_record_uncork(session, 0); if (ret < 0) { terminate(); fail("send[%d]: %s\n", __LINE__, gnutls_strerror(ret)); } gnutls_record_cork(session); ret = gnutls_record_send(session, buffer, gnutls_dtls_get_data_mtu(session) - 16); if (ret < 0) { terminate(); fail("send[%d]: %s\n", __LINE__, gnutls_strerror(ret)); } ret = gnutls_record_send(session, buffer, gnutls_dtls_get_data_mtu(session)); if (ret != GNUTLS_E_LARGE_PACKET) { terminate(); fail("send[%d]: %s\n", __LINE__, gnutls_strerror(ret)); } ret = gnutls_record_uncork(session, GNUTLS_RECORD_WAIT); if (ret < 0) { terminate(); fail("send[%d]: %s\n", __LINE__, gnutls_strerror(ret)); } /* do not wait for the peer to close the connection. */ gnutls_bye(session, GNUTLS_SHUT_WR); close(fd); gnutls_deinit(session); gnutls_anon_free_server_credentials(anoncred); gnutls_global_deinit(); if (debug) success("server: finished\n"); }
static void dtls_mtu_try(const char *name, const char *client_prio, unsigned link_mtu, unsigned tunnel_mtu) { int ret; /* Server stuff. */ gnutls_certificate_credentials_t serverx509cred; gnutls_session_t server; int sret = GNUTLS_E_AGAIN; /* Client stuff. */ gnutls_certificate_credentials_t clientx509cred; gnutls_session_t client; int cret = GNUTLS_E_AGAIN; unsigned dmtu; unsigned i; /* General init. */ gnutls_global_set_log_function(tls_log_func); if (debug) gnutls_global_set_log_level(6); reset_buffers(); /* Init server */ assert(gnutls_certificate_allocate_credentials(&serverx509cred)>=0); assert(gnutls_certificate_set_x509_key_mem(serverx509cred, &server_cert, &server_key, GNUTLS_X509_FMT_PEM) >= 0); assert(gnutls_init(&server, GNUTLS_SERVER|GNUTLS_DATAGRAM|GNUTLS_NONBLOCK) >= 0); gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, serverx509cred); assert(gnutls_priority_set_direct(server, "NORMAL:+ANON-ECDH:+ANON-DH:+ECDHE-RSA:+DHE-RSA:+RSA:+ECDHE-ECDSA:+CURVE-X25519", NULL) >= 0); gnutls_transport_set_push_function(server, server_push); gnutls_transport_set_pull_function(server, server_pull); gnutls_transport_set_pull_timeout_function(server, server_pull_timeout_func); gnutls_transport_set_ptr(server, server); /* Init client */ ret = gnutls_init(&client, GNUTLS_CLIENT|GNUTLS_DATAGRAM|GNUTLS_NONBLOCK); if (ret < 0) exit(1); assert(gnutls_certificate_allocate_credentials(&clientx509cred) >= 0); assert(gnutls_certificate_set_x509_trust_mem(clientx509cred, &ca_cert, GNUTLS_X509_FMT_PEM)>=0); assert(gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, clientx509cred) >= 0); gnutls_transport_set_push_function(client, client_push); gnutls_transport_set_pull_function(client, client_pull); gnutls_transport_set_pull_timeout_function(client, client_pull_timeout_func); gnutls_transport_set_ptr(client, client); ret = gnutls_priority_set_direct(client, client_prio, NULL); if (ret < 0) { fail("%s: error in priority setting\n", name); exit(1); } success("negotiating %s\n", name); HANDSHAKE_DTLS(client, server); gnutls_dtls_set_mtu(client, link_mtu); dmtu = gnutls_dtls_get_data_mtu(client); if (dmtu != tunnel_mtu) { fail("%s: Calculated MTU (%d) does not match expected (%d)\n", name, dmtu, tunnel_mtu); } { char msg[dmtu+1]; memset(msg, 1, sizeof(msg)); ret = gnutls_record_send(client, msg, dmtu+1); if (ret != (int)GNUTLS_E_LARGE_PACKET) { myfail("could send larger packet than MTU (%d), ret: %d\n", dmtu, ret); } ret = gnutls_record_send(client, msg, dmtu); if (ret != (int)dmtu) { myfail("could not send %d bytes (sent %d)\n", dmtu, ret); } memset(msg, 2, dmtu); ret = gnutls_record_recv(server, msg, dmtu); if (ret != (int)dmtu) { myfail("could not receive %d bytes (received %d)\n", dmtu, ret); } for (i=0;i<dmtu;i++) assert(msg[i]==1); } gnutls_bye(client, GNUTLS_SHUT_RDWR); gnutls_bye(server, GNUTLS_SHUT_RDWR); gnutls_deinit(client); gnutls_deinit(server); gnutls_certificate_free_credentials(serverx509cred); gnutls_certificate_free_credentials(clientx509cred); }