diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2011-02-19 16:57:44 +0100 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2011-02-19 19:08:35 +0100 |
commit | 089391da55d232908b9cdbba7f27b0ccfea26cca (patch) | |
tree | dbbdf207f712fb4c3b8975944ccea36c4f08bc35 | |
parent | b5c0c987913d93e0bb8414c4f48242ee8f350382 (diff) | |
download | gnutls-089391da55d232908b9cdbba7f27b0ccfea26cca.tar.gz |
Changes to allow DTLS server side to operate. Added a simple UDP server on gnutls-serv.
Server other cleanups.
-rw-r--r-- | lib/Makefile.am | 2 | ||||
-rw-r--r-- | lib/ext_session_ticket.c | 4 | ||||
-rw-r--r-- | lib/gnutls_buffers.c | 152 | ||||
-rw-r--r-- | lib/gnutls_buffers.h | 3 | ||||
-rw-r--r-- | lib/gnutls_constate.c | 17 | ||||
-rw-r--r-- | lib/gnutls_dtls.c | 66 | ||||
-rw-r--r-- | lib/gnutls_handshake.c | 68 | ||||
-rw-r--r-- | lib/gnutls_int.h | 7 | ||||
-rw-r--r-- | lib/gnutls_state.c | 5 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 5 | ||||
-rw-r--r-- | lib/libgnutls.map | 9 | ||||
-rw-r--r-- | lib/system.c | 25 | ||||
-rw-r--r-- | lib/system.h | 2 | ||||
-rw-r--r-- | lib/system_override.c | 202 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/serv-gaa.c | 117 | ||||
-rw-r--r-- | src/serv-gaa.h | 40 | ||||
-rw-r--r-- | src/serv.c | 122 | ||||
-rw-r--r-- | src/serv.gaa | 5 | ||||
-rw-r--r-- | src/udp-serv.c | 174 | ||||
-rw-r--r-- | src/udp-serv.h | 8 |
21 files changed, 726 insertions, 309 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 6a2b1dce46..edf5209182 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -81,7 +81,7 @@ COBJECTS = gnutls_record.c gnutls_compress.c debug.c gnutls_cipher.c \ crypto.c random.c ext_signature.c cryptodev.c system.c \ crypto-api.c ext_safe_renegotiation.c gnutls_privkey.c \ pkcs11.c pkcs11_privkey.c gnutls_pubkey.c pkcs11_write.c locks.c \ - pkcs11_secret.c hash.c gnutls_dtls.c + pkcs11_secret.c hash.c gnutls_dtls.c system_override.c if ENABLE_NETTLE diff --git a/lib/ext_session_ticket.c b/lib/ext_session_ticket.c index 9c44b90555..f51c127e7b 100644 --- a/lib/ext_session_ticket.c +++ b/lib/ext_session_ticket.c @@ -683,10 +683,8 @@ _gnutls_send_new_session_ticket (gnutls_session_t session, int again) data_size = p - data; } - ret = _gnutls_send_handshake (session, data_size ? bufel : NULL, + return _gnutls_send_handshake (session, data_size ? bufel : NULL, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET); - - return ret; } int diff --git a/lib/gnutls_buffers.c b/lib/gnutls_buffers.c index a57784f633..41b024488f 100644 --- a/lib/gnutls_buffers.c +++ b/lib/gnutls_buffers.c @@ -68,58 +68,6 @@ */ #define MAX_QUEUE 16 -/** - * gnutls_transport_set_errno: - * @session: is a #gnutls_session_t structure. - * @err: error value to store in session-specific errno variable. - * - * Store @err in the session-specific errno variable. Useful values - * for @err is EAGAIN and EINTR, other values are treated will be - * treated as real errors in the push/pull function. - * - * This function is useful in replacement push/pull functions set by - * gnutls_transport_set_push_function and - * gnutls_transport_set_pullpush_function under Windows, where the - * replacement push/pull may not have access to the same @errno - * variable that is used by GnuTLS (e.g., the application is linked to - * msvcr71.dll and gnutls is linked to msvcrt.dll). - * - * If you don't have the @session variable easily accessible from the - * push/pull function, and don't worry about thread conflicts, you can - * also use gnutls_transport_set_global_errno(). - **/ -void -gnutls_transport_set_errno (gnutls_session_t session, int err) -{ - session->internals.errnum = err; -} - -/** - * gnutls_transport_set_global_errno: - * @err: error value to store in global errno variable. - * - * Store @err in the global errno variable. Useful values for @err is - * EAGAIN and EINTR, other values are treated will be treated as real - * errors in the push/pull function. - * - * This function is useful in replacement push/pull functions set by - * gnutls_transport_set_push_function and - * gnutls_transport_set_pullpush_function under Windows, where the - * replacement push/pull may not have access to the same @errno - * variable that is used by GnuTLS (e.g., the application is linked to - * msvcr71.dll and gnutls is linked to msvcrt.dll). - * - * Whether this function is thread safe or not depends on whether the - * global variable errno is thread safe, some system libraries make it - * a thread-local variable. When feasible, using the guaranteed - * thread-safe gnutls_transport_set_errno() may be better. - **/ -void -gnutls_transport_set_global_errno (int err) -{ - errno = err; -} - /* Buffers received packets of type APPLICATION DATA and * HANDSHAKE DATA. */ @@ -451,16 +399,11 @@ _gnutls_writev_emu (gnutls_session_t session, const giovec_t * giovec, { int ret, j = 0; gnutls_transport_ptr_t fd = session->internals.transport_send_ptr; - void *iptr; - size_t sizeOfPtr; size_t total = 0; for (j = 0; j < giovec_cnt; j++) { - sizeOfPtr = giovec[j].iov_len; - iptr = giovec[j].iov_base; - - ret = session->internals.push_func (fd, iptr, sizeOfPtr); + ret = session->internals.push_func (fd, giovec[j].iov_base, giovec[j].iov_len); if (ret == -1) break; @@ -673,7 +616,8 @@ _gnutls_io_read_buffered (gnutls_session_t session, size_t total, * select think, that the socket is ready for reading. * MSG_PEEK is only used with berkeley style sockets. */ - if (ret == readsize && recvlowat > 0 && !_gnutls_is_dtls(session)) + if (ret == readsize && recvlowat > 0 && !_gnutls_is_dtls(session) && + session->internals.pull_func == system_read) { ret2 = _gnutls_read (session, &bufel, recvlowat, system_read_peek); @@ -844,13 +788,16 @@ _gnutls_io_write_flush (gnutls_session_t session) * on timeout and a negative value on error. */ int -_gnutls_io_check_recv (gnutls_session_t session, int ms) +_gnutls_io_check_recv (gnutls_session_t session, void* data, size_t data_size, unsigned int ms) { gnutls_transport_ptr_t fd = session->internals.transport_send_ptr; int ret; - - ret = system_recv_timeout(fd, ms); + if (session->internals.pull_timeout_func == system_recv_timeout && + session->internals.pull_func != system_read) + return gnutls_assert_val(GNUTLS_E_PULL_ERROR); + + ret = session->internals.pull_timeout_func(fd, data, data_size, ms); if (ret == -1) return gnutls_assert_val(GNUTLS_E_PULL_ERROR); @@ -1111,84 +1058,3 @@ _gnutls_handshake_buffer_clear (gnutls_session_t session) return 0; } -/** - * gnutls_transport_set_pull_function: - * @session: is a #gnutls_session_t structure. - * @pull_func: a callback function similar to read() - * - * This is the function where you set a function for gnutls to receive - * data. Normally, if you use berkeley style sockets, do not need to - * use this function since the default (recv(2)) will probably be ok. - * - * PULL_FUNC is of the form, - * ssize_t (*gnutls_pull_func)(gnutls_transport_ptr_t, void*, size_t); - **/ -void -gnutls_transport_set_pull_function (gnutls_session_t session, - gnutls_pull_func pull_func) -{ - session->internals.pull_func = pull_func; -} - -/** - * gnutls_transport_set_push_function: - * @session: is a #gnutls_session_t structure. - * @push_func: a callback function similar to write() - * - * This is the function where you set a push function for gnutls to - * use in order to send data. If you are going to use berkeley style - * sockets, you do not need to use this function since the default - * (send(2)) will probably be ok. Otherwise you should specify this - * function for gnutls to be able to send data. - * - * PUSH_FUNC is of the form, - * ssize_t (*gnutls_push_func)(gnutls_transport_ptr_t, const void*, size_t); - **/ -void -gnutls_transport_set_push_function (gnutls_session_t session, - gnutls_push_func push_func) -{ - session->internals.push_func = push_func; - session->internals.vec_push_func = NULL; -} - -/** - * gnutls_transport_set_push_function2: - * @session: is a #gnutls_session_t structure. - * @vec_func: a callback function similar to writev() - * - * This is the function where you set a push function for gnutls to - * use in order to send data. If you are going to use berkeley style - * sockets, you do not need to use this function since the default - * (send(2)) will probably be ok. Otherwise you should specify this - * function for gnutls to be able to send data. - * - * PUSH_FUNC is of the form, - * ssize_t (*gnutls_push_func)(gnutls_transport_ptr_t, const void*, size_t); - **/ -void -gnutls_transport_set_push_function2 (gnutls_session_t session, - gnutls_vec_push_func vec_func) -{ - session->internals.push_func = NULL; - session->internals.vec_push_func = vec_func; -} - -/** - * gnutls_transport_set_errno_function: - * @session: is a #gnutls_session_t structure. - * @errno_func: a callback function similar to write() - * - * This is the function where you set a function to retrieve errno - * after a failed push or pull operation. - * - * errno_func is of the form, - * int (*gnutls_errno_func)(gnutls_transport_ptr_t); - * and should return the errno. - **/ -void -gnutls_transport_set_errno_function (gnutls_session_t session, - gnutls_errno_func errno_func) -{ - session->internals.errno_func = errno_func; -} diff --git a/lib/gnutls_buffers.h b/lib/gnutls_buffers.h index 8028f734ef..37371ba6fb 100644 --- a/lib/gnutls_buffers.h +++ b/lib/gnutls_buffers.h @@ -60,7 +60,8 @@ int _gnutls_handshake_io_cache_int (gnutls_session_t, gnutls_handshake_description_t, mbuffer_st * bufel); ssize_t _gnutls_io_write_flush (gnutls_session_t session); -int _gnutls_io_check_recv (gnutls_session_t session, int ms); +int +_gnutls_io_check_recv (gnutls_session_t session, void* data, size_t data_size, unsigned int ms); ssize_t _gnutls_handshake_io_write_flush (gnutls_session_t session); #endif diff --git a/lib/gnutls_constate.c b/lib/gnutls_constate.c index aadb110d66..9e25d32fde 100644 --- a/lib/gnutls_constate.c +++ b/lib/gnutls_constate.c @@ -677,6 +677,7 @@ epoch_get_slot (gnutls_session_t session, uint16_t epoch) if (epoch_index >= MAX_EPOCH_INDEX) { + _gnutls_dtls_log("Epoch %d out of range (idx: %d, max: %d)\n", (int)epoch, (int)epoch_index, MAX_EPOCH_INDEX); gnutls_assert (); return NULL; } @@ -746,9 +747,19 @@ epoch_alive (gnutls_session_t session, record_parameters_st * params) const security_parameters_st *sp = &session->security_parameters; /* DTLS will, in addition, need to check the epoch timeout value. */ - return (params->epoch == sp->epoch_read - || params->epoch == sp->epoch_write - || params->epoch == sp->epoch_next); + if (params->usage_cnt > 0) + return 1; + + if (params->epoch == sp->epoch_read) + return 1; + + if (params->epoch == sp->epoch_write) + return 1; + + if (params->epoch == sp->epoch_next) + return 1; + + return 0; } void diff --git a/lib/gnutls_dtls.c b/lib/gnutls_dtls.c index 81d3e5f88f..fdb8479347 100644 --- a/lib/gnutls_dtls.c +++ b/lib/gnutls_dtls.c @@ -57,10 +57,7 @@ _gnutls_dtls_handshake_enqueue (gnutls_session_t session, msg = gnutls_malloc (sizeof(dtls_hsk_retransmit_buffer)); if (msg == NULL) - { - gnutls_assert (); - return GNUTLS_E_MEMORY_ERROR; - } + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); msg->bufel = bufel; @@ -70,9 +67,11 @@ _gnutls_dtls_handshake_enqueue (gnutls_session_t session, msg->type = type; msg->sequence = sequence; - _gnutls_dtls_log ("DTLS[%p]: Enqueued Packet[%u] %s(%d) with length: %u\n", + params->usage_cnt++; + + _gnutls_dtls_log ("DTLS[%p]: Enqueued Packet[%u] %s(%d) with length: %u on epoch %d\n", session, (uint)sequence, _gnutls_handshake2str (type), - type, msg->bufel->msg.size); + type, msg->bufel->msg.size, msg->epoch); *(session->internals.dtls.retransmit_end) = msg; session->internals.dtls.retransmit_end = &msg->next; @@ -93,10 +92,11 @@ transmit_message (gnutls_session_t session, if (msg->type == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC) { - return _gnutls_send_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, + ret = _gnutls_send_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, msg->epoch, _mbuffer_get_uhead_ptr(msg->bufel), _mbuffer_get_uhead_size(msg->bufel), 0); + goto leave_epoch; } mtu_data = gnutls_malloc(mtu + DTLS_HANDSHAKE_HEADER_SIZE); @@ -119,7 +119,7 @@ transmit_message (gnutls_session_t session, _gnutls_write_uint16 (msg->sequence, &mtu_data[4]); /* Chop up and send handshake message into mtu-size pieces. */ - for (offset=0; offset < data_size; offset += mtu) + for (offset=0; offset <= data_size; offset += mtu) { /* Calculate fragment length */ if(offset + mtu > data_size) @@ -153,9 +153,29 @@ transmit_message (gnutls_session_t session, } gnutls_free (mtu_data); +leave_epoch: + return ret; } +static int drop_usage_count(gnutls_session_t session) +{ + dtls_hsk_retransmit_buffer *msg; + record_parameters_st * params; + int ret; + + for (msg = session->internals.dtls.retransmit; msg != NULL; msg = msg->next) + { + ret = _gnutls_epoch_get( session, msg->epoch, ¶ms); + if (ret < 0) + return gnutls_assert_val(ret); + params->usage_cnt--; + if (params->usage_cnt < 0) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + return 0; +} + /* This function transmits the flight that has been previously * buffered. * @@ -170,26 +190,47 @@ int ret; /* PREPARING -> SENDING state transition */ dtls_hsk_retransmit_buffer *msg; unsigned int total_timeout = 0; + gnutls_handshake_description_t last_type = 0; do { _gnutls_dtls_log ("DTLS[%p]: Start of flight transmission.\n", session); for (msg = session->internals.dtls.retransmit; msg != NULL; msg = msg->next) - transmit_message (session, msg); + { + transmit_message (session, msg); + last_type = msg->type; + } ret = _gnutls_io_write_flush (session); if (ret < 0) return gnutls_assert_val(ret); - ret = _gnutls_io_check_recv(session, session->internals.dtls.retrans_timeout); - total_timeout += session->internals.dtls.retrans_timeout; + /* last message in handshake -> no ack */ + if (session->security_parameters.entity == GNUTLS_SERVER && + last_type == GNUTLS_HANDSHAKE_FINISHED) + { + opaque c; + ret = _gnutls_io_check_recv(session, &c, 1, session->internals.dtls.retrans_timeout); + if (ret == GNUTLS_E_TIMEDOUT) + ret = 0; + else if (ret >= 0) + { + if (c == GNUTLS_HANDSHAKE) /* retransmit */ + ret = GNUTLS_E_TIMEDOUT; + } + total_timeout += session->internals.dtls.retrans_timeout; + } + else /* all other messages -> implicit ack (receive of next flight) */ + { + ret = _gnutls_io_check_recv(session, NULL, 0, session->internals.dtls.retrans_timeout); + total_timeout += session->internals.dtls.retrans_timeout; + } if (total_timeout >= session->internals.dtls.total_timeout) { ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT); goto cleanup; } - } while(ret == GNUTLS_E_TIMEDOUT); if (ret < 0) @@ -202,6 +243,7 @@ int ret; ret = 0; cleanup: + drop_usage_count(session); _gnutls_dtls_clear_outgoing_buffer (session); /* SENDING -> WAITING state transition */ diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index 9889c3bd51..454232b26f 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -518,6 +518,16 @@ _gnutls_read_client_hello (gnutls_session_t session, opaque * data, session->internals.resumed = RESUME_FALSE; } + if (_gnutls_is_dtls(session)) + { + int cookie_size; + + DECR_LEN (len, 1); + cookie_size = data[pos++]; + DECR_LEN (len, cookie_size); + pos+=cookie_size; + } + /* Remember ciphersuites for later */ DECR_LEN (len, 2); @@ -943,31 +953,20 @@ _gnutls_server_select_suite (gnutls_session_t session, opaque * data, gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } -#ifdef HANDSHAKE_DEBUG - _gnutls_handshake_log ("HSK[%p]: Requested cipher suites: \n", session); - for (j = 0; j < datalen; j += 2) - { - memcpy (&cs.suite, &data[j], 2); - _gnutls_handshake_log ("\t%s\n", _gnutls_cipher_suite_get_name (&cs)); - } - _gnutls_handshake_log ("HSK[%p]: Supported cipher suites: \n", session); - for (j = 0; j < x; j++) - _gnutls_handshake_log ("\t%s\n", - _gnutls_cipher_suite_get_name (&ciphers[j])); -#endif - memset (session->security_parameters.current_cipher_suite.suite, '\0', 2); + memset (session->security_parameters.current_cipher_suite.suite, 0, 2); retval = GNUTLS_E_UNKNOWN_CIPHER_SUITE; + _gnutls_handshake_log ("HSK[%p]: Requested cipher suites[size: %d]: \n", session, (int)datalen); for (j = 0; j < datalen; j += 2) { + memcpy (&cs.suite, &data[j], 2); + _gnutls_handshake_log ("\t0x%.2x, 0x%.2x %s\n", data[j], data[j+1], _gnutls_cipher_suite_get_name (&cs)); for (i = 0; i < x; i++) { if (memcmp (ciphers[i].suite, &data[j], 2) == 0) { - memcpy (&cs.suite, &data[j], 2); - _gnutls_handshake_log ("HSK[%p]: Selected cipher suite: %s\n", session, _gnutls_cipher_suite_get_name (&cs)); @@ -1212,7 +1211,7 @@ _gnutls_send_handshake (gnutls_session_t session, mbuffer_st * bufel, pos += 3; } - _gnutls_handshake_log ("HSK[%p]: %s was sent [%ld bytes]\n", + _gnutls_handshake_log ("HSK[%p]: %s was queued [%ld bytes]\n", session, _gnutls_handshake2str (type), (long) datasize); @@ -1240,7 +1239,7 @@ _gnutls_send_handshake (gnutls_session_t session, mbuffer_st * bufel, _gnutls_handshake_hash_add_sent (session, type, data, datasize)) < 0) { gnutls_assert (); - _mbuffer_xfree(&bufel); + _mbuffer_xfree(&bufel); return ret; } @@ -1418,6 +1417,9 @@ _gnutls_recv_handshake_header (gnutls_session_t session, return length32; else if (*recv_type != type) { + _gnutls_handshake_log ("HSK[%p]: %s was received, expected %s\n", + session, _gnutls_handshake2str (*recv_type), + _gnutls_handshake2str (type)); gnutls_assert (); return GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET; } @@ -1604,7 +1606,7 @@ _gnutls_recv_handshake (gnutls_session_t session, uint8_t ** data, else /* Signal our caller we have received a verification cookie and ClientHello needs to be sent again. */ - ret = 1; + ret = 1; goto cleanup; /* caller doesn't need dataptr */ @@ -2282,7 +2284,7 @@ _gnutls_send_client_hello (gnutls_session_t session, int again) _gnutls_send_handshake (session, bufel, GNUTLS_HANDSHAKE_CLIENT_HELLO); cleanup: - gnutls_free (bufel); + _mbuffer_xfree(&bufel); _gnutls_buffer_clear(&extdata); return ret; } @@ -2870,20 +2872,20 @@ _gnutls_handshake_client (gnutls_session_t session) case STATE11: if (_gnutls_is_dtls (session)) - { - ret = - _gnutls_recv_handshake (session, NULL, NULL, - GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST, - OPTIONAL_PACKET); - STATE = STATE11; - IMED_RET ("recv hello verify", ret, 1); - - if (ret == 1) - { - STATE = STATE0; - return 1; - } - } + { + ret = + _gnutls_recv_handshake (session, NULL, NULL, + GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST, + OPTIONAL_PACKET); + STATE = STATE11; + IMED_RET ("recv hello verify", ret, 1); + + if (ret == 1) + { + STATE = STATE0; + return 1; + } + } case STATE2: /* receive the server hello */ ret = diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 0923bbc0ba..d3da01d6ef 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -491,6 +491,12 @@ struct record_parameters_st record_state_st read; record_state_st write; + + /* Whether this state is in use, i.e., if there is + a pending handshake message waiting to be encrypted + under this epoch's parameters. + */ + int usage_cnt; }; typedef struct @@ -703,6 +709,7 @@ typedef struct /* PUSH & PULL functions. */ + gnutls_pull_timeout_func pull_timeout_func; gnutls_pull_func pull_func; gnutls_push_func push_func; gnutls_vec_push_func vec_push_func; diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c index 0ceaa2d74e..ab2350e2cd 100644 --- a/lib/gnutls_state.c +++ b/lib/gnutls_state.c @@ -372,6 +372,7 @@ gnutls_init (gnutls_session_t * session, gnutls_connection_end_t con_end) #endif gnutls_transport_set_pull_function (*session, system_read); gnutls_transport_set_errno_function (*session, system_errno); + gnutls_transport_set_pull_timeout_function (*session, system_recv_timeout); return 0; } @@ -407,8 +408,8 @@ gnutls_init_dtls (gnutls_session_t * session, /* Initialize pointer used to enqueue messages for retransmit. */ (*session)->internals.dtls.retransmit_end = &(*session)->internals.dtls.retransmit; - (*session)->internals.dtls.retrans_timeout = 100; - (*session)->internals.dtls.total_timeout = 3000; + (*session)->internals.dtls.retrans_timeout = 1000; + (*session)->internals.dtls.total_timeout = 4000; return 0; } diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 93b1cea44e..ebe09bd63f 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -1184,6 +1184,8 @@ extern "C" typedef ssize_t (*gnutls_push_func) (gnutls_transport_ptr_t, const void *, size_t); + typedef int (*gnutls_pull_timeout_func) (gnutls_transport_ptr_t, void*data, size_t size, unsigned int ms); + typedef ssize_t (*gnutls_vec_push_func) (gnutls_transport_ptr_t, const giovec_t * iov, int iovcnt); @@ -1210,6 +1212,9 @@ extern "C" void gnutls_transport_set_pull_function (gnutls_session_t session, gnutls_pull_func pull_func); + void gnutls_transport_set_pull_timeout_function (gnutls_session_t session, + gnutls_pull_timeout_func func); + void gnutls_transport_set_errno_function (gnutls_session_t session, gnutls_errno_func errno_func); diff --git a/lib/libgnutls.map b/lib/libgnutls.map index fa16a998ef..5576c5a953 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -625,7 +625,6 @@ GNUTLS_2_12 gnutls_pkcs11_privkey_decrypt_data; gnutls_pkcs11_obj_export; gnutls_pkcs11_type_get_name; - gnutls_privkey_init; gnutls_privkey_deinit; gnutls_privkey_get_pk_algorithm; @@ -667,7 +666,6 @@ GNUTLS_2_12 gnutls_global_set_mutex; gnutls_transport_set_push_function2; gnutls_transport_set_errno_function; - gnutls_sec_param_to_pk_bits; gnutls_sec_param_get_name; gnutls_pk_bits_to_sec_param; @@ -687,6 +685,10 @@ GNUTLS_2_12 gnutls_openpgp_crt_verify_hash; gnutls_pubkey_import_privkey; gnutls_pubkey_verify_data; +} GNUTLS_2_10; + +GNUTLS_3_0_0 { + global: gnutls_x509_trust_list_verify_crt; gnutls_x509_trust_list_add_crls; gnutls_x509_trust_list_add_cas; @@ -698,7 +700,8 @@ GNUTLS_2_12 gnutls_cipher_tag; gnutls_cipher_add_auth; gnutls_dtls_set_timeouts; -} GNUTLS_2_10; + gnutls_transport_set_pull_timeout_function; +} GNUTLS_2_12; GNUTLS_PRIVATE { global: diff --git a/lib/system.c b/lib/system.c index d89c283709..169dd1551c 100644 --- a/lib/system.c +++ b/lib/system.c @@ -105,12 +105,15 @@ system_read_peek (gnutls_transport_ptr ptr, void *data, size_t data_size) return recv (GNUTLS_POINTER_TO_INT (ptr), data, data_size, MSG_PEEK); } -/* Wait for data to be received within a timeout period in milliseconds +/* Wait for data to be received within a timeout period in milliseconds. + * If data_size > 0 it will return the specified amount of data in + * peek mode. */ -int system_recv_timeout(gnutls_transport_ptr ptr, size_t ms) +int system_recv_timeout(gnutls_transport_ptr ptr, void* data, size_t data_size, unsigned int ms) { fd_set rfds; struct timeval tv; +int ret; FD_ZERO(&rfds); FD_SET(GNUTLS_POINTER_TO_INT(ptr), &rfds); @@ -118,7 +121,23 @@ struct timeval tv; tv.tv_sec = 0; tv.tv_usec = ms * 1000; - return select(GNUTLS_POINTER_TO_INT(ptr)+1, &rfds, NULL, NULL, &tv); + ret = select(GNUTLS_POINTER_TO_INT(ptr)+1, &rfds, NULL, NULL, &tv); + + if (ret <= 0 || data_size == 0) + return ret; + + /* only report ok if the next message is from the peer we expect + * from + */ + ret = recv(GNUTLS_POINTER_TO_INT(ptr), data, data_size, MSG_PEEK); + if (ret > 0) + { + return ret; + } + else + { + return -1; + } } /* Thread stuff */ diff --git a/lib/system.h b/lib/system.h index 6ec4e83204..207e6fa49d 100644 --- a/lib/system.h +++ b/lib/system.h @@ -8,7 +8,7 @@ #endif int system_errno (gnutls_transport_ptr); -int system_recv_timeout(gnutls_transport_ptr ptr, size_t ms); +int system_recv_timeout(gnutls_transport_ptr ptr,void*data, size_t, unsigned int ms); #ifdef _WIN32 ssize_t system_write (gnutls_transport_ptr ptr, const void *data, diff --git a/lib/system_override.c b/lib/system_override.c new file mode 100644 index 0000000000..852ec2bc16 --- /dev/null +++ b/lib/system_override.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, + * 2009, 2010, 2011 Free Software Foundation, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains function that will override the + * default berkeley sockets API per session. + */ + +#include <gnutls_int.h> +#include <gnutls_errors.h> +#include <gnutls_num.h> +#include <gnutls_record.h> +#include <gnutls_buffers.h> +#include <gnutls_mbuffers.h> +#include <gnutls_state.h> +#include <gnutls_dtls.h> +#include <system.h> + +#include <errno.h> + +/** + * gnutls_transport_set_errno: + * @session: is a #gnutls_session_t structure. + * @err: error value to store in session-specific errno variable. + * + * Store @err in the session-specific errno variable. Useful values + * for @err is EAGAIN and EINTR, other values are treated will be + * treated as real errors in the push/pull function. + * + * This function is useful in replacement push/pull functions set by + * gnutls_transport_set_push_function and + * gnutls_transport_set_pullpush_function under Windows, where the + * replacement push/pull may not have access to the same @errno + * variable that is used by GnuTLS (e.g., the application is linked to + * msvcr71.dll and gnutls is linked to msvcrt.dll). + * + * If you don't have the @session variable easily accessible from the + * push/pull function, and don't worry about thread conflicts, you can + * also use gnutls_transport_set_global_errno(). + **/ +void +gnutls_transport_set_errno (gnutls_session_t session, int err) +{ + session->internals.errnum = err; +} + +/** + * gnutls_transport_set_global_errno: + * @err: error value to store in global errno variable. + * + * Store @err in the global errno variable. Useful values for @err is + * EAGAIN and EINTR, other values are treated will be treated as real + * errors in the push/pull function. + * + * This function is useful in replacement push/pull functions set by + * gnutls_transport_set_push_function and + * gnutls_transport_set_pullpush_function under Windows, where the + * replacement push/pull may not have access to the same @errno + * variable that is used by GnuTLS (e.g., the application is linked to + * msvcr71.dll and gnutls is linked to msvcrt.dll). + * + * Whether this function is thread safe or not depends on whether the + * global variable errno is thread safe, some system libraries make it + * a thread-local variable. When feasible, using the guaranteed + * thread-safe gnutls_transport_set_errno() may be better. + **/ +void +gnutls_transport_set_global_errno (int err) +{ + errno = err; +} + +/** + * gnutls_transport_set_pull_function: + * @session: is a #gnutls_session_t structure. + * @pull_func: a callback function similar to read() + * + * This is the function where you set a function for gnutls to receive + * data. Normally, if you use berkeley style sockets, do not need to + * use this function since the default (recv(2)) will probably be ok. + * + * gnutls_pull_func is of the form, + * ssize_t (*gnutls_pull_func)(gnutls_transport_ptr_t, void*, size_t); + **/ +void +gnutls_transport_set_pull_function (gnutls_session_t session, + gnutls_pull_func pull_func) +{ + session->internals.pull_func = pull_func; +} + +/** + * gnutls_transport_set_pull_timeout_function: + * @session: is a #gnutls_session_t structure. + * @func: a callback function + * + * This is the function where you set a function for gnutls to know + * whether data are ready to be received within a time limit in + * milliseconds. The callback should return 0 on timeout, a positive + * number if data can be received, and -1 on error. + * If the #data_size is non-zero that function should copy that + * amount of data received in peek mode (i.e., if any other + * function is called to receive data, it should return them again). + * + * The callback function is used in DTLS only. + * + * gnutls_pull_timeout_func is of the form, + * ssize_t (*gnutls_pull_timeout_func)(gnutls_transport_ptr_t, void*data, size_t size, unsigned int ms); + * + **/ +void +gnutls_transport_set_pull_timeout_function (gnutls_session_t session, + gnutls_pull_timeout_func func) +{ + session->internals.pull_timeout_func = func; +} + +/** + * gnutls_transport_set_push_function: + * @session: is a #gnutls_session_t structure. + * @push_func: a callback function similar to write() + * + * This is the function where you set a push function for gnutls to + * use in order to send data. If you are going to use berkeley style + * sockets, you do not need to use this function since the default + * (send(2)) will probably be ok. Otherwise you should specify this + * function for gnutls to be able to send data. + * + * push_func is of the form, + * ssize_t (*gnutls_push_func)(gnutls_transport_ptr_t, const void*, size_t); + * + **/ +void +gnutls_transport_set_push_function (gnutls_session_t session, + gnutls_push_func push_func) +{ + session->internals.push_func = push_func; + session->internals.vec_push_func = NULL; +} + +/** + * gnutls_transport_set_push_function2: + * @session: is a #gnutls_session_t structure. + * @vec_func: a callback function similar to writev() + * + * This is the function where you set a push function for gnutls to + * use in order to send data. If you are going to use berkeley style + * sockets, you do not need to use this function since the default + * (send(2)) will probably be ok. Otherwise you should specify this + * function for gnutls to be able to send data. + * + * vec_func is of the form, + * ssize_t (*gnutls_vec_push_func) (gnutls_transport_ptr_t, const giovec_t * iov, int iovcnt); + * + **/ +void +gnutls_transport_set_push_function2 (gnutls_session_t session, + gnutls_vec_push_func vec_func) +{ + session->internals.push_func = NULL; + session->internals.vec_push_func = vec_func; +} + +/** + * gnutls_transport_set_errno_function: + * @session: is a #gnutls_session_t structure. + * @errno_func: a callback function similar to write() + * + * This is the function where you set a function to retrieve errno + * after a failed push or pull operation. + * + * errno_func is of the form, + * int (*gnutls_errno_func)(gnutls_transport_ptr_t); + * and should return the errno. + **/ +void +gnutls_transport_set_errno_function (gnutls_session_t session, + gnutls_errno_func errno_func) +{ + session->internals.errno_func = errno_func; +} diff --git a/src/Makefile.am b/src/Makefile.am index 509a446b0d..622ee8c2ed 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -40,7 +40,7 @@ endif noinst_LTLIBRARIES = -gnutls_serv_SOURCES = list.h serv.c common.h common.c certtool-common.h +gnutls_serv_SOURCES = list.h serv.c udp-serv.c common.h common.c certtool-common.h gnutls_serv_LDADD = ../lib/libgnutls.la ../libextra/libgnutls-extra.la gnutls_serv_LDADD += libcmd-serv.la ../gl/libgnu.la gnutls_serv_LDADD += $(LTLIBGCRYPT) $(LIBSOCKET) $(GETADDRINFO_LIB) diff --git a/src/serv-gaa.c b/src/serv-gaa.c index a3c050d50b..e1fd2ece15 100644 --- a/src/serv-gaa.c +++ b/src/serv-gaa.c @@ -133,6 +133,7 @@ void gaa_help(void) __gaa_helpsingle(0, "noticket", "", "Does not issue session tickets."); __gaa_helpsingle(0, "http", "", "Act as an HTTP Server."); __gaa_helpsingle(0, "echo", "", "Act as an Echo Server."); + __gaa_helpsingle(0, "udp", "", "Enable UDP server instead of TCP."); __gaa_helpsingle(0, "dhparams", "FILE ", "DH params file to use."); __gaa_helpsingle(0, "x509fmtder", "", "Use DER format for certificates"); __gaa_helpsingle(0, "x509cafile", "FILE ", "Certificate file or PKCS #11 URL to use."); @@ -169,44 +170,46 @@ typedef struct _gaainfo gaainfo; struct _gaainfo { -#line 88 "serv.gaa" +#line 91 "serv.gaa" char *priorities; -#line 85 "serv.gaa" +#line 88 "serv.gaa" char *srp_passwd_conf; -#line 82 "serv.gaa" +#line 85 "serv.gaa" char *srp_passwd; -#line 79 "serv.gaa" +#line 82 "serv.gaa" char *psk_hint; -#line 76 "serv.gaa" +#line 79 "serv.gaa" char *psk_passwd; -#line 73 "serv.gaa" +#line 76 "serv.gaa" int disable_client_cert; -#line 70 "serv.gaa" +#line 73 "serv.gaa" int require_cert; -#line 67 "serv.gaa" +#line 70 "serv.gaa" char *x509_dsacertfile; -#line 64 "serv.gaa" +#line 67 "serv.gaa" char *x509_dsakeyfile; -#line 61 "serv.gaa" +#line 64 "serv.gaa" char *x509_certfile; -#line 58 "serv.gaa" +#line 61 "serv.gaa" char *x509_keyfile; -#line 55 "serv.gaa" +#line 58 "serv.gaa" char *pgp_subkey; -#line 52 "serv.gaa" +#line 55 "serv.gaa" char *pgp_certfile; -#line 49 "serv.gaa" +#line 52 "serv.gaa" char *pgp_keyfile; -#line 46 "serv.gaa" +#line 49 "serv.gaa" char *pgp_keyring; -#line 43 "serv.gaa" +#line 46 "serv.gaa" char *x509_crlfile; -#line 40 "serv.gaa" +#line 43 "serv.gaa" char *x509_cafile; -#line 37 "serv.gaa" +#line 40 "serv.gaa" int fmtder; -#line 34 "serv.gaa" +#line 37 "serv.gaa" char *dh_params_file; +#line 34 "serv.gaa" + int udp; #line 30 "serv.gaa" int http; #line 27 "serv.gaa" @@ -275,7 +278,7 @@ static int gaa_error = 0; #define GAA_MULTIPLE_OPTION 3 #define GAA_REST 0 -#define GAA_NB_OPTION 30 +#define GAA_NB_OPTION 31 #define GAAOPTID_version 1 #define GAAOPTID_help 2 #define GAAOPTID_list 3 @@ -298,14 +301,15 @@ static int gaa_error = 0; #define GAAOPTID_x509cafile 20 #define GAAOPTID_x509fmtder 21 #define GAAOPTID_dhparams 22 -#define GAAOPTID_echo 23 -#define GAAOPTID_http 24 -#define GAAOPTID_noticket 25 -#define GAAOPTID_nodb 26 -#define GAAOPTID_quiet 27 -#define GAAOPTID_port 28 -#define GAAOPTID_generate 29 -#define GAAOPTID_debug 30 +#define GAAOPTID_udp 23 +#define GAAOPTID_echo 24 +#define GAAOPTID_http 25 +#define GAAOPTID_noticket 26 +#define GAAOPTID_nodb 27 +#define GAAOPTID_quiet 28 +#define GAAOPTID_port 29 +#define GAAOPTID_generate 30 +#define GAAOPTID_debug 31 #line 168 "gaa.skel" @@ -655,6 +659,7 @@ static int gaa_get_option_num(char *str, int status) GAA_CHECK1STR("a", GAAOPTID_disable_client_cert); GAA_CHECK1STR("r", GAAOPTID_require_cert); GAA_CHECK1STR("", GAAOPTID_x509fmtder); + GAA_CHECK1STR("", GAAOPTID_udp); GAA_CHECK1STR("", GAAOPTID_echo); GAA_CHECK1STR("", GAAOPTID_http); GAA_CHECK1STR("", GAAOPTID_noticket); @@ -687,6 +692,7 @@ static int gaa_get_option_num(char *str, int status) GAA_CHECKSTR("x509cafile", GAAOPTID_x509cafile); GAA_CHECKSTR("x509fmtder", GAAOPTID_x509fmtder); GAA_CHECKSTR("dhparams", GAAOPTID_dhparams); + GAA_CHECKSTR("udp", GAAOPTID_udp); GAA_CHECKSTR("echo", GAAOPTID_echo); GAA_CHECKSTR("http", GAAOPTID_http); GAA_CHECKSTR("noticket", GAAOPTID_noticket); @@ -747,21 +753,21 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) { case GAAOPTID_version: OK = 0; -#line 94 "serv.gaa" +#line 97 "serv.gaa" { serv_version(); exit(0); ;}; return GAA_OK; break; case GAAOPTID_help: OK = 0; -#line 92 "serv.gaa" +#line 95 "serv.gaa" { gaa_help(); exit(0); ;}; return GAA_OK; break; case GAAOPTID_list: OK = 0; -#line 91 "serv.gaa" +#line 94 "serv.gaa" { print_list(0); exit(0); ;}; return GAA_OK; @@ -771,7 +777,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_priority.arg1, gaa_getstr, GAATMP_priority.size1); gaa_index++; -#line 89 "serv.gaa" +#line 92 "serv.gaa" { gaaval->priorities = GAATMP_priority.arg1 ;}; return GAA_OK; @@ -781,7 +787,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_srppasswdconf.arg1, gaa_getstr, GAATMP_srppasswdconf.size1); gaa_index++; -#line 86 "serv.gaa" +#line 89 "serv.gaa" { gaaval->srp_passwd_conf = GAATMP_srppasswdconf.arg1 ;}; return GAA_OK; @@ -791,7 +797,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_srppasswd.arg1, gaa_getstr, GAATMP_srppasswd.size1); gaa_index++; -#line 83 "serv.gaa" +#line 86 "serv.gaa" { gaaval->srp_passwd = GAATMP_srppasswd.arg1 ;}; return GAA_OK; @@ -801,7 +807,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_pskhint.arg1, gaa_getstr, GAATMP_pskhint.size1); gaa_index++; -#line 80 "serv.gaa" +#line 83 "serv.gaa" { gaaval->psk_hint = GAATMP_pskhint.arg1 ;}; return GAA_OK; @@ -811,21 +817,21 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_pskpasswd.arg1, gaa_getstr, GAATMP_pskpasswd.size1); gaa_index++; -#line 77 "serv.gaa" +#line 80 "serv.gaa" { gaaval->psk_passwd = GAATMP_pskpasswd.arg1 ;}; return GAA_OK; break; case GAAOPTID_disable_client_cert: OK = 0; -#line 74 "serv.gaa" +#line 77 "serv.gaa" { gaaval->disable_client_cert = 1 ;}; return GAA_OK; break; case GAAOPTID_require_cert: OK = 0; -#line 71 "serv.gaa" +#line 74 "serv.gaa" { gaaval->require_cert = 1 ;}; return GAA_OK; @@ -835,7 +841,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_x509dsacertfile.arg1, gaa_getstr, GAATMP_x509dsacertfile.size1); gaa_index++; -#line 68 "serv.gaa" +#line 71 "serv.gaa" { gaaval->x509_dsacertfile = GAATMP_x509dsacertfile.arg1 ;}; return GAA_OK; @@ -845,7 +851,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_x509dsakeyfile.arg1, gaa_getstr, GAATMP_x509dsakeyfile.size1); gaa_index++; -#line 65 "serv.gaa" +#line 68 "serv.gaa" { gaaval->x509_dsakeyfile = GAATMP_x509dsakeyfile.arg1 ;}; return GAA_OK; @@ -855,7 +861,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_x509certfile.arg1, gaa_getstr, GAATMP_x509certfile.size1); gaa_index++; -#line 62 "serv.gaa" +#line 65 "serv.gaa" { gaaval->x509_certfile = GAATMP_x509certfile.arg1 ;}; return GAA_OK; @@ -865,7 +871,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_x509keyfile.arg1, gaa_getstr, GAATMP_x509keyfile.size1); gaa_index++; -#line 59 "serv.gaa" +#line 62 "serv.gaa" { gaaval->x509_keyfile = GAATMP_x509keyfile.arg1 ;}; return GAA_OK; @@ -875,7 +881,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_pgpsubkey.arg1, gaa_getstr, GAATMP_pgpsubkey.size1); gaa_index++; -#line 56 "serv.gaa" +#line 59 "serv.gaa" { gaaval->pgp_subkey = GAATMP_pgpsubkey.arg1 ;}; return GAA_OK; @@ -885,7 +891,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_pgpcertfile.arg1, gaa_getstr, GAATMP_pgpcertfile.size1); gaa_index++; -#line 53 "serv.gaa" +#line 56 "serv.gaa" { gaaval->pgp_certfile = GAATMP_pgpcertfile.arg1 ;}; return GAA_OK; @@ -895,7 +901,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_pgpkeyfile.arg1, gaa_getstr, GAATMP_pgpkeyfile.size1); gaa_index++; -#line 50 "serv.gaa" +#line 53 "serv.gaa" { gaaval->pgp_keyfile = GAATMP_pgpkeyfile.arg1 ;}; return GAA_OK; @@ -905,7 +911,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_pgpkeyring.arg1, gaa_getstr, GAATMP_pgpkeyring.size1); gaa_index++; -#line 47 "serv.gaa" +#line 50 "serv.gaa" { gaaval->pgp_keyring = GAATMP_pgpkeyring.arg1 ;}; return GAA_OK; @@ -915,7 +921,7 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_x509crlfile.arg1, gaa_getstr, GAATMP_x509crlfile.size1); gaa_index++; -#line 44 "serv.gaa" +#line 47 "serv.gaa" { gaaval->x509_crlfile = GAATMP_x509crlfile.arg1 ;}; return GAA_OK; @@ -925,14 +931,14 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_x509cafile.arg1, gaa_getstr, GAATMP_x509cafile.size1); gaa_index++; -#line 41 "serv.gaa" +#line 44 "serv.gaa" { gaaval->x509_cafile = GAATMP_x509cafile.arg1 ;}; return GAA_OK; break; case GAAOPTID_x509fmtder: OK = 0; -#line 38 "serv.gaa" +#line 41 "serv.gaa" { gaaval->fmtder = 1 ;}; return GAA_OK; @@ -942,11 +948,18 @@ static int gaa_try(int gaa_num, int gaa_index, gaainfo *gaaval, char *opt_list) GAA_TESTMOREARGS; GAA_FILL(GAATMP_dhparams.arg1, gaa_getstr, GAATMP_dhparams.size1); gaa_index++; -#line 35 "serv.gaa" +#line 38 "serv.gaa" { gaaval->dh_params_file = GAATMP_dhparams.arg1 ;}; return GAA_OK; break; + case GAAOPTID_udp: + OK = 0; +#line 35 "serv.gaa" +{ gaaval->udp = 1 ;}; + + return GAA_OK; + break; case GAAOPTID_echo: OK = 0; #line 32 "serv.gaa" @@ -1033,7 +1046,7 @@ int gaa(int argc, char **argv, gaainfo *gaaval) if(inited == 0) { -#line 98 "serv.gaa" +#line 101 "serv.gaa" { gaaval->generate=0; gaaval->port=5556; gaaval->http=0; gaaval->nodb = 0; gaaval->noticket = 0; gaaval->x509_cafile = NULL; gaaval->pgp_keyfile=NULL; gaaval->pgp_certfile=NULL; gaaval->x509_keyfile=NULL; gaaval->x509_certfile=NULL; gaaval->x509_crlfile = NULL; @@ -1042,7 +1055,7 @@ int gaa(int argc, char **argv, gaainfo *gaaval) gaaval->pgp_keyring=NULL; gaaval->fmtder = 0; gaaval->disable_client_cert = 0; gaaval->priorities = NULL; gaaval->dh_params_file=NULL; gaaval->debug=0; gaaval->require_cert = 0; gaaval->psk_passwd = 0; - gaaval->pgp_subkey = NULL;;}; + gaaval->pgp_subkey = NULL; gaaval->udp = 0; ;}; } inited = 1; diff --git a/src/serv-gaa.h b/src/serv-gaa.h index 2a4a08ab43..0a1d560866 100644 --- a/src/serv-gaa.h +++ b/src/serv-gaa.h @@ -8,44 +8,46 @@ typedef struct _gaainfo gaainfo; struct _gaainfo { -#line 88 "serv.gaa" +#line 91 "serv.gaa" char *priorities; -#line 85 "serv.gaa" +#line 88 "serv.gaa" char *srp_passwd_conf; -#line 82 "serv.gaa" +#line 85 "serv.gaa" char *srp_passwd; -#line 79 "serv.gaa" +#line 82 "serv.gaa" char *psk_hint; -#line 76 "serv.gaa" +#line 79 "serv.gaa" char *psk_passwd; -#line 73 "serv.gaa" +#line 76 "serv.gaa" int disable_client_cert; -#line 70 "serv.gaa" +#line 73 "serv.gaa" int require_cert; -#line 67 "serv.gaa" +#line 70 "serv.gaa" char *x509_dsacertfile; -#line 64 "serv.gaa" +#line 67 "serv.gaa" char *x509_dsakeyfile; -#line 61 "serv.gaa" +#line 64 "serv.gaa" char *x509_certfile; -#line 58 "serv.gaa" +#line 61 "serv.gaa" char *x509_keyfile; -#line 55 "serv.gaa" +#line 58 "serv.gaa" char *pgp_subkey; -#line 52 "serv.gaa" +#line 55 "serv.gaa" char *pgp_certfile; -#line 49 "serv.gaa" +#line 52 "serv.gaa" char *pgp_keyfile; -#line 46 "serv.gaa" +#line 49 "serv.gaa" char *pgp_keyring; -#line 43 "serv.gaa" +#line 46 "serv.gaa" char *x509_crlfile; -#line 40 "serv.gaa" +#line 43 "serv.gaa" char *x509_cafile; -#line 37 "serv.gaa" +#line 40 "serv.gaa" int fmtder; -#line 34 "serv.gaa" +#line 37 "serv.gaa" char *dh_params_file; +#line 34 "serv.gaa" + int udp; #line 30 "serv.gaa" int http; #line 27 "serv.gaa" diff --git a/src/serv.c b/src/serv.c index 068762438d..698ab5019d 100644 --- a/src/serv.c +++ b/src/serv.c @@ -49,6 +49,7 @@ #include "read-file.h" #include "minmax.h" #include "sockets.h" +#include "udp-serv.h" /* konqueror cannot handle sending the page in multiple * pieces. @@ -56,7 +57,6 @@ /* global stuff */ static int generate = 0; static int http = 0; -static int port = 0; static int x509ctype; static int debug; @@ -81,6 +81,7 @@ char *dh_params_file; char *x509_crlfile = NULL; gnutls_datum_t session_ticket_key; +static int tcp_server(const char* name, int port); /* end of globals */ @@ -326,13 +327,15 @@ generate_rsa_params (void) LIST_DECLARE_INIT (listener_list, listener_item, listener_free); -static gnutls_session_t -initialize_session (void) +gnutls_session_t initialize_session (int dtls) { gnutls_session_t session; const char *err; - gnutls_init (&session, GNUTLS_SERVER); + if (dtls) + gnutls_init_dtls (&session, GNUTLS_SERVER, 0); + else + gnutls_init (&session, GNUTLS_SERVER); /* allow the use of private ciphersuites. */ @@ -585,7 +588,7 @@ peer_print_info (gnutls_session_t session, int *ret_length, return http_buffer; } -static const char * +const char * human_addr (const struct sockaddr *sa, socklen_t salen, char *buf, size_t buflen) { @@ -633,8 +636,52 @@ human_addr (const struct sockaddr *sa, socklen_t salen, return save_buf; } -static int -listen_socket (const char *name, int listen_port) +int wait_for_connection(void) +{ + listener_item *j; + fd_set rd, wr; + int n, sock = -1; + + FD_ZERO (&rd); + FD_ZERO (&wr); + n = 0; + + lloopstart (listener_list, j) + { + if (j->listen_socket) + { + FD_SET (j->fd, &rd); + n = MAX (n, j->fd); + } + } + lloopend (listener_list, j); + + /* waiting part */ + n = select (n + 1, &rd, &wr, NULL, NULL); + if (n == -1 && errno == EINTR) + return -1; + if (n < 0) + { + perror ("select()"); + exit (1); + } + + /* find which one is ready */ + lloopstart (listener_list, j) + { + /* a new connection has arrived */ + if (FD_ISSET (j->fd, &rd) && j->listen_socket) + { + sock = j->fd; + break; + } + } + lloopend (listener_list, j); + return sock; +} + +int +listen_socket (const char *name, int listen_port, int socktype) { struct addrinfo hints, *res, *ptr; char portname[6]; @@ -644,7 +691,7 @@ listen_socket (const char *name, int listen_port) snprintf (portname, sizeof (portname), "%d", listen_port); memset (&hints, 0, sizeof (hints)); - hints.ai_socktype = SOCK_STREAM; + hints.ai_socktype = socktype; hints.ai_flags = AI_PASSIVE; if ((s = getaddrinfo (NULL, portname, &hints, &res)) != 0) @@ -671,26 +718,32 @@ listen_socket (const char *name, int listen_port) continue; } - yes = 1; - if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, - (const void *) &yes, sizeof (yes)) < 0) + if (socktype == SOCK_STREAM) { - perror ("setsockopt() failed"); - failed: - close (s); - continue; + yes = 1; + if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, + (const void *) &yes, sizeof (yes)) < 0) + { + perror ("setsockopt() failed"); + close (s); + continue; + } } if (bind (s, ptr->ai_addr, ptr->ai_addrlen) < 0) { perror ("bind() failed"); - goto failed; + close (s); + continue; } - if (listen (s, 10) < 0) + if (socktype == SOCK_STREAM) { - perror ("listen() failed"); - goto failed; + if (listen (s, 10) < 0) + { + perror ("listen() failed"); + exit(1); + } } /* new list entry for the connection */ @@ -706,10 +759,8 @@ listen_socket (const char *name, int listen_port) fflush (stderr); freeaddrinfo (res); - if (!j) - return -1; - return 0; + return s; } /* strips \r\n from the end of the string @@ -824,12 +875,8 @@ static void gaa_parser (int argc, char **argv); int main (int argc, char **argv) { - int ret, n; - char topbuf[512]; + int ret; char name[256]; - int accept_fd; - struct sockaddr_storage client_address; - socklen_t calen; set_program_name (argv[0]); @@ -1049,7 +1096,22 @@ main (int argc, char **argv) gnutls_session_ticket_key_generate (&session_ticket_key); #endif - if (listen_socket (name, port) < 0) + if (info.udp) + return udp_server(name, info.port); + else + return tcp_server(name, info.port); +} + +static int tcp_server(const char* name, int port) +{ + int n, s; + char topbuf[512]; + int accept_fd; + struct sockaddr_storage client_address; + socklen_t calen; + + s = listen_socket (name, port, SOCK_STREAM); + if (s < 0) exit (1); for (;;) @@ -1112,7 +1174,7 @@ main (int argc, char **argv) { gnutls_session_t tls_session; - tls_session = initialize_session (); + tls_session = initialize_session (0); calen = sizeof (client_address); memset (&client_address, 0, calen); @@ -1462,8 +1524,6 @@ gaa_parser (int argc, char **argv) dh_params_file = info.dh_params_file; - port = info.port; - x509_certfile = info.x509_certfile; x509_keyfile = info.x509_keyfile; x509_dsacertfile = info.x509_dsacertfile; diff --git a/src/serv.gaa b/src/serv.gaa index 0d886a54c0..93cc85d98e 100644 --- a/src/serv.gaa +++ b/src/serv.gaa @@ -31,6 +31,9 @@ option (noticket) { $noticket = 1 } "Does not issue session tickets." option (http) { $http = 1 } "Act as an HTTP Server." option (echo) { $http = 0 } "Act as an Echo Server." +#int udp; +option (udp) { $udp = 1 } "Enable UDP server instead of TCP." + #char *dh_params_file; option (dhparams) STR "FILE" { $dh_params_file = $1 } "DH params file to use." @@ -103,4 +106,4 @@ init { $generate=0; $port=5556; $http=0; $nodb = 0; $noticket = 0; $pgp_keyring=NULL; $fmtder = 0; $disable_client_cert = 0; $priorities = NULL; $dh_params_file=NULL; $debug=0; $require_cert = 0; $psk_passwd = 0; - $pgp_subkey = NULL;} + $pgp_subkey = NULL; $udp = 0; } diff --git a/src/udp-serv.c b/src/udp-serv.c new file mode 100644 index 0000000000..332917ea8b --- /dev/null +++ b/src/udp-serv.c @@ -0,0 +1,174 @@ +#include <stdio.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include "udp-serv.h" +#include "list.h" + +typedef struct { + gnutls_session_t session; + int fd; + struct sockaddr * cli_addr; + socklen_t cli_addr_size; +} priv_data_st; + +static int pull_timeout_func(gnutls_transport_ptr ptr, void* data, size_t data_size, unsigned int ms); +static ssize_t push_func (gnutls_transport_ptr_t p, const void * data, size_t size); +static ssize_t pull_func(gnutls_transport_ptr_t p, void * data, size_t size); + +#define MAX_BUFFER 255 /* Longest string to echo */ + +int udp_server(const char* name, int port) +{ + int sock, ret; + struct sockaddr_in cli_addr; + socklen_t cli_addr_size; + char buffer[MAX_BUFFER]; + priv_data_st priv; + gnutls_session_t session; + + ret = listen_socket (name, port, SOCK_DGRAM); + if (ret < 0) + exit (1); + + for (;;) + { + printf("Waiting for connection...\n"); + sock = wait_for_connection(); + if (sock < 0) + continue; + + cli_addr_size = sizeof(cli_addr); + ret = recvfrom(sock, buffer, 1, MSG_PEEK, (struct sockaddr*)&cli_addr, &cli_addr_size); + if (ret == 1) + printf ("Accepted connection from %s\n", + human_addr ((struct sockaddr *) + &cli_addr, sizeof(cli_addr), buffer, + sizeof (buffer))); + else + continue; + + session = initialize_session(1); + + priv.session = session; + priv.fd = sock; + priv.cli_addr = (struct sockaddr *)&cli_addr; + priv.cli_addr_size = sizeof(cli_addr); + + gnutls_transport_set_ptr (session, &priv); + gnutls_transport_set_push_function (session, push_func); + gnutls_transport_set_pull_function (session, pull_func); + gnutls_transport_set_pull_timeout_function (session, pull_timeout_func); + + ret = gnutls_handshake(session); + if (ret < 0) + { + fprintf(stderr, "Error in handshake(): %s\n", gnutls_strerror(ret)); + continue; + } + + for(;;) + { + ret = gnutls_record_recv(session, buffer, MAX_BUFFER); + if (ret < 0) + { + fprintf(stderr, "Error in recv(): %s\n", gnutls_strerror(ret)); + break; + } + if (ret == 0) + { + printf("EOF\n\n"); + break; + } + buffer[ret] = 0; + printf("received[%d]: %s\n", ret, buffer); + + /* reply back */ + ret = gnutls_record_send(session, buffer, ret); + if (ret < 0) + { + fprintf(stderr, "Error in send(): %s\n", gnutls_strerror(ret)); + break; + } + } + } + gnutls_deinit(session); +} + +/* Wait for data to be received within a timeout period in milliseconds + */ +static int pull_timeout_func(gnutls_transport_ptr ptr, void* data, size_t data_size, unsigned int ms) +{ +fd_set rfds; +struct timeval tv; +priv_data_st *priv = ptr; +struct sockaddr_in cli_addr; +socklen_t cli_addr_size; +int ret; +char c; + + FD_ZERO(&rfds); + FD_SET(priv->fd, &rfds); + + tv.tv_sec = 0; + tv.tv_usec = ms * 1000; + + ret = select(priv->fd+1, &rfds, NULL, NULL, &tv); + + if (ret <= 0) + return ret; + + if (data_size == 0) + { + data = &c; + data_size = 1; + } + + /* only report ok if the next message is from the peer we expect + * from + */ + cli_addr_size = sizeof(cli_addr); + ret = recvfrom(priv->fd, data, data_size, MSG_PEEK, (struct sockaddr*)&cli_addr, &cli_addr_size); + if (ret > 0) + { + if (cli_addr_size == priv->cli_addr_size && memcmp(&cli_addr, priv->cli_addr, sizeof(cli_addr))==0) + return 1; + } + + return 0; +} + +static ssize_t push_func (gnutls_transport_ptr_t p, const void * data, size_t size) +{ +priv_data_st *priv = p; + + return sendto(priv->fd, data, size, 0, priv->cli_addr, priv->cli_addr_size); +} + +static ssize_t pull_func(gnutls_transport_ptr_t p, void * data, size_t size) +{ +priv_data_st *priv = p; +struct sockaddr_in cli_addr; +socklen_t cli_addr_size; +char buffer[64]; +int ret; + + cli_addr_size = sizeof(cli_addr); + ret = recvfrom(priv->fd, data, size, 0, (struct sockaddr*)&cli_addr, &cli_addr_size); + if (ret == -1) + return ret; + + if (cli_addr_size == priv->cli_addr_size && memcmp(&cli_addr, priv->cli_addr, sizeof(cli_addr))==0) + return ret; + + printf ("Denied connection from %s\n", + human_addr ((struct sockaddr *) + &cli_addr, sizeof(cli_addr), buffer, + sizeof (buffer))); + + gnutls_transport_set_errno(priv->session, EAGAIN); + return -1; +} diff --git a/src/udp-serv.h b/src/udp-serv.h new file mode 100644 index 0000000000..7a481b93a9 --- /dev/null +++ b/src/udp-serv.h @@ -0,0 +1,8 @@ +#include <gnutls/dtls.h> + +int udp_server(const char* name, int port); +gnutls_session_t initialize_session (int dtls); +const char * human_addr (const struct sockaddr *sa, socklen_t salen, + char *buf, size_t buflen); +int wait_for_connection(void); +int listen_socket (const char *name, int listen_port, int socktype); |