--- apps/s_client.c 2008-12-20 18:04:08.000000000 +0100 +++ apps/s_client.c 2009-05-18 12:06:35.000000000 +0200 @@ -226,7 +226,7 @@ BIO_printf(bio_err," -ssl3 - just use SSLv3\n"); BIO_printf(bio_err," -tls1 - just use TLSv1\n"); BIO_printf(bio_err," -dtls1 - just use DTLSv1\n"); - BIO_printf(bio_err," -mtu - set the MTU\n"); + BIO_printf(bio_err," -mtu - set the link layer MTU\n"); BIO_printf(bio_err," -no_tls1/-no_ssl3/-no_ssl2 - turn off that protocol\n"); BIO_printf(bio_err," -bugs - Switch on all SSL implementation bug workarounds\n"); BIO_printf(bio_err," -serverpref - Use server's cipher preferences (only SSLv2)\n"); @@ -843,10 +843,10 @@ BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, 0, &timeout); } - if ( mtu > 0) + if ( mtu > 28) { SSL_set_options(con, SSL_OP_NO_QUERY_MTU); - SSL_set_mtu(con, mtu); + SSL_set_mtu(con, mtu - 28); } else /* want to do MTU discovery */ --- apps/s_server.c 2008-12-20 18:04:08.000000000 +0100 +++ apps/s_server.c 2009-05-18 12:06:35.000000000 +0200 @@ -375,7 +375,7 @@ BIO_printf(bio_err," -tls1 - Just talk TLSv1\n"); BIO_printf(bio_err," -dtls1 - Just talk DTLSv1\n"); BIO_printf(bio_err," -timeout - Enable timeouts\n"); - BIO_printf(bio_err," -mtu - Set MTU\n"); + BIO_printf(bio_err," -mtu - Set link layer MTU\n"); BIO_printf(bio_err," -chain - Read a certificate chain\n"); BIO_printf(bio_err," -no_ssl2 - Just disable SSLv2\n"); BIO_printf(bio_err," -no_ssl3 - Just disable SSLv3\n"); @@ -1660,10 +1660,10 @@ } - if ( mtu > 0) + if ( mtu > 28) { SSL_set_options(con, SSL_OP_NO_QUERY_MTU); - SSL_set_mtu(con, mtu); + SSL_set_mtu(con, mtu - 28); } else /* want to do MTU discovery */ --- crypto/bio/bio.h 2008-04-02 13:37:23.000000000 +0200 +++ crypto/bio/bio.h 2009-05-18 12:06:31.000000000 +0200 @@ -158,6 +158,8 @@ #define BIO_CTRL_DGRAM_SET_PEER 44 /* Destination for the data */ +#define BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT 45 /* Next DTLS handshake timeout to + * adjust socket timeouts */ /* modifiers */ #define BIO_FP_READ 0x02 --- crypto/bio/bss_dgram.c 2008-09-15 07:45:36.000000000 +0200 +++ crypto/bio/bss_dgram.c 2009-05-18 12:06:35.000000000 +0200 @@ -66,7 +66,9 @@ #include +#ifdef OPENSSL_SYS_LINUX #define IP_MTU 14 /* linux is lame */ +#endif #ifdef WATT32 #define sock_write SockWrite /* Watt-32 uses same names */ @@ -104,6 +106,8 @@ unsigned int connected; unsigned int _errno; unsigned int mtu; + struct timeval next_timeout; + struct timeval socket_timeout; } bio_dgram_data; BIO_METHOD *BIO_s_datagram(void) @@ -165,7 +169,95 @@ } return(1); } - + +static void dgram_adjust_rcv_timeout(BIO *b) + { +#if defined(SO_RCVTIMEO) + bio_dgram_data *data = (bio_dgram_data *)b->ptr; + int sz = sizeof(int); + + /* Is a timer active? */ + if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) + { + struct timeval timenow, timeleft; + + /* Read current socket timeout */ +#ifdef OPENSSL_SYS_WINDOWS + struct _timeb tb; + int timeout; + if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, + (void*)&timeout, &sz) < 0) + { perror("getsockopt"); } + else + { + data->socket_timeout.tv_sec = timeout / 1000; + data->socket_timeout.tv_usec = (timeout % 1000) * 1000; + } +#else + if ( getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, + &(data->socket_timeout), (void *)&sz) < 0) + { perror("getsockopt"); } +#endif + + /* Get current time */ +#ifdef OPENSSL_SYS_WIN32 + _ftime(&tb); + timenow.tv_sec = (long) tb.time; + timenow.tv_usec = (long) tb.millitm * 1000; +#else + gettimeofday(&timenow, NULL); +#endif + + /* Calculate time left until timer expires */ + memcpy(&timeleft, &(data->next_timeout), sizeof(struct timeval)); + timeleft.tv_sec -= timenow.tv_sec; + timeleft.tv_usec -= timenow.tv_usec; + if (timeleft.tv_usec < 0) + { + timeleft.tv_sec--; + timeleft.tv_usec += 1000000; + } + + /* Adjust socket timeout if next handhake message timer + * will expire earlier. + */ + if (data->socket_timeout.tv_sec < timeleft.tv_sec || + (data->socket_timeout.tv_sec == timeleft.tv_sec && + data->socket_timeout.tv_usec <= timeleft.tv_usec)) + { +#ifdef OPENSSL_SYS_WINDOWS + timeout = timeleft.tv_sec * 1000 + timeleft.tv_usec / 1000; + if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, + (void*)&timeout, sizeof(timeout)) < 0) + { perror("setsockopt"); } +#else + if ( setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, &timeleft, + sizeof(struct timeval)) < 0) + { perror("setsockopt"); } +#endif + } + } +#endif + } + +static void dgram_reset_rcv_timeout(BIO *b) + { +#if defined(SO_RCVTIMEO) + bio_dgram_data *data = (bio_dgram_data *)b->ptr; +#ifdef OPENSSL_SYS_WINDOWS + int timeout = data->socket_timeout.tv_sec * 1000 + + data->socket_timeout.tv_usec / 1000; + if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, + (void*)&timeout, sizeof(timeout)) < 0) + { perror("setsockopt"); } +#else + if ( setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, &(data->socket_timeout), + sizeof(struct timeval)) < 0) + { perror("setsockopt"); } +#endif +#endif + } + static int dgram_read(BIO *b, char *out, int outl) { int ret=0; @@ -183,7 +275,9 @@ * but this is not universal. Cast to (void *) to avoid * compiler warnings. */ + dgram_adjust_rcv_timeout(b); ret=recvfrom(b->num,out,outl,0,&peer,(void *)&peerlen); + dgram_reset_rcv_timeout(b); if ( ! data->connected && ret > 0) BIO_ctrl(b, BIO_CTRL_DGRAM_CONNECT, 0, &peer); @@ -242,6 +336,10 @@ bio_dgram_data *data = NULL; long sockopt_val = 0; unsigned int sockopt_len = 0; +#ifdef OPENSSL_SYS_LINUX + socklen_t addr_len; + struct sockaddr_storage addr; +#endif data = (bio_dgram_data *)b->ptr; @@ -300,24 +398,87 @@ #endif break; /* (Linux)kernel sets DF bit on outgoing IP packets */ -#ifdef IP_MTU_DISCOVER case BIO_CTRL_DGRAM_MTU_DISCOVER: - sockopt_val = IP_PMTUDISC_DO; - if ((ret = setsockopt(b->num, IPPROTO_IP, IP_MTU_DISCOVER, - &sockopt_val, sizeof(sockopt_val))) < 0) - perror("setsockopt"); +#ifdef OPENSSL_SYS_LINUX + addr_len = (socklen_t)sizeof(struct sockaddr_storage); + memset((void *)&addr, 0, sizeof(struct sockaddr_storage)); + if (getsockname(b->num, (void *)&addr, &addr_len) < 0) + { + ret = 0; + break; + } + sockopt_len = sizeof(sockopt_val); + switch (addr.ss_family) + { + case AF_INET: + sockopt_val = IP_PMTUDISC_DO; + if ((ret = setsockopt(b->num, IPPROTO_IP, IP_MTU_DISCOVER, + &sockopt_val, sizeof(sockopt_val))) < 0) + perror("setsockopt"); + break; + case AF_INET6: + sockopt_val = IPV6_PMTUDISC_DO; + if ((ret = setsockopt(b->num, IPPROTO_IPV6, IPV6_MTU_DISCOVER, + &sockopt_val, sizeof(sockopt_val))) < 0) + perror("setsockopt"); + break; + default: + ret = -1; + break; + } + ret = -1; +#else break; #endif case BIO_CTRL_DGRAM_QUERY_MTU: - sockopt_len = sizeof(sockopt_val); - if ((ret = getsockopt(b->num, IPPROTO_IP, IP_MTU, (void *)&sockopt_val, - &sockopt_len)) < 0 || sockopt_val < 0) - { ret = 0; } - else +#ifdef OPENSSL_SYS_LINUX + addr_len = (socklen_t)sizeof(struct sockaddr_storage); + memset((void *)&addr, 0, sizeof(struct sockaddr_storage)); + if (getsockname(b->num, (void *)&addr, &addr_len) < 0) + { + ret = 0; + break; + } + sockopt_len = sizeof(sockopt_val); + switch (addr.ss_family) { - data->mtu = sockopt_val; - ret = data->mtu; + case AF_INET: + if ((ret = getsockopt(b->num, IPPROTO_IP, IP_MTU, (void *)&sockopt_val, + &sockopt_len)) < 0 || sockopt_val < 0) + { + ret = 0; + } + else + { + /* we assume that the transport protocol is UDP and no + * IP options are used. + */ + data->mtu = sockopt_val - 8 - 20; + ret = data->mtu; + } + break; + case AF_INET6: + if ((ret = getsockopt(b->num, IPPROTO_IPV6, IPV6_MTU, (void *)&sockopt_val, + &sockopt_len)) < 0 || sockopt_val < 0) + { + ret = 0; + } + else + { + /* we assume that the transport protocol is UDP and no + * IPV6 options are used. + */ + data->mtu = sockopt_val - 8 - 40; + ret = data->mtu; + } + break; + default: + ret = 0; + break; } +#else + ret = 0; +#endif break; case BIO_CTRL_DGRAM_GET_MTU: return data->mtu; @@ -345,6 +506,9 @@ memcpy(&(data->peer), to, sizeof(struct sockaddr)); break; + case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT: + memcpy(&(data->next_timeout), ptr, sizeof(struct timeval)); + break; #if defined(SO_RCVTIMEO) case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT: #ifdef OPENSSL_SYS_WINDOWS --- crypto/pqueue/pqueue.c 2005-06-28 14:53:33.000000000 +0200 +++ crypto/pqueue/pqueue.c 2009-05-18 12:06:31.000000000 +0200 @@ -234,3 +234,17 @@ return ret; } + +int +pqueue_size(pqueue_s *pq) +{ + pitem *item = pq->items; + int count = 0; + + while(item != NULL) + { + count++; + item = item->next; + } + return count; +} --- crypto/pqueue/pqueue.h 2005-05-31 00:34:27.000000000 +0200 +++ crypto/pqueue/pqueue.h 2009-05-18 12:06:31.000000000 +0200 @@ -91,5 +91,6 @@ pitem *pqueue_next(piterator *iter); void pqueue_print(pqueue pq); +int pqueue_size(pqueue pq); #endif /* ! HEADER_PQUEUE_H */ --- ssl/d1_both.c 2007-10-17 23:17:49.000000000 +0200 +++ ssl/d1_both.c 2009-05-18 12:06:31.000000000 +0200 @@ -136,7 +136,6 @@ static void dtls1_set_message_header_int(SSL *s, unsigned char mt, unsigned long len, unsigned short seq_num, unsigned long frag_off, unsigned long frag_len); -static int dtls1_retransmit_buffered_messages(SSL *s); static long dtls1_get_message_fragment(SSL *s, int st1, int stn, long max, int *ok); @@ -519,6 +518,8 @@ if ( s->d1->handshake_read_seq == frag->msg_header.seq) { + unsigned long frag_len = frag->msg_header.frag_len; + pqueue_pop(s->d1->buffered_messages); al=dtls1_preprocess_fragment(s,&frag->msg_header,max); @@ -536,7 +537,7 @@ if (al==0) { *ok = 1; - return frag->msg_header.frag_len; + return frag_len; } ssl3_send_alert(s,SSL3_AL_FATAL,al); @@ -561,7 +562,16 @@ if ((msg_hdr->frag_off+frag_len) > msg_hdr->msg_len) goto err; - if (msg_hdr->seq <= s->d1->handshake_read_seq) + /* Try to find item in queue, to prevent duplicate entries */ + pq_64bit_init(&seq64); + pq_64bit_assign_word(&seq64, msg_hdr->seq); + item = pqueue_find(s->d1->buffered_messages, seq64); + pq_64bit_free(&seq64); + + /* Discard the message if sequence number was already there, is + * too far in the future or the fragment is already in the queue */ + if (msg_hdr->seq <= s->d1->handshake_read_seq || + msg_hdr->seq > s->d1->handshake_read_seq + 10 || item != NULL) { unsigned char devnull [256]; @@ -575,30 +585,31 @@ } } - frag = dtls1_hm_fragment_new(frag_len); - if ( frag == NULL) - goto err; + if (frag_len) + { + frag = dtls1_hm_fragment_new(frag_len); + if ( frag == NULL) + goto err; - memcpy(&(frag->msg_header), msg_hdr, sizeof(*msg_hdr)); + memcpy(&(frag->msg_header), msg_hdr, sizeof(*msg_hdr)); - if (frag_len) - { - /* read the body of the fragment (header has already been read */ + /* read the body of the fragment (header has already been read) */ i = s->method->ssl_read_bytes(s,SSL3_RT_HANDSHAKE, frag->fragment,frag_len,0); if (i<=0 || (unsigned long)i!=frag_len) goto err; - } - pq_64bit_init(&seq64); - pq_64bit_assign_word(&seq64, msg_hdr->seq); + pq_64bit_init(&seq64); + pq_64bit_assign_word(&seq64, msg_hdr->seq); - item = pitem_new(seq64, frag); - pq_64bit_free(&seq64); - if ( item == NULL) - goto err; + item = pitem_new(seq64, frag); + pq_64bit_free(&seq64); + if ( item == NULL) + goto err; + + pqueue_insert(s->d1->buffered_messages, item); + } - pqueue_insert(s->d1->buffered_messages, item); return DTLS1_HM_FRAGMENT_RETRY; err: @@ -884,7 +895,6 @@ int dtls1_read_failed(SSL *s, int code) { DTLS1_STATE *state; - BIO *bio; int send_alert = 0; if ( code > 0) @@ -893,8 +903,7 @@ return 1; } - bio = SSL_get_rbio(s); - if ( ! BIO_dgram_recv_timedout(bio)) + if (!dtls1_is_timer_expired(s)) { /* not a timeout, none of our business, let higher layers handle this. in fact it's probably an error */ @@ -907,6 +916,7 @@ return code; } + dtls1_double_timeout(s); state = s->d1; state->timeout.num_alerts++; if ( state->timeout.num_alerts > DTLS1_TMO_ALERT_COUNT) @@ -942,8 +952,21 @@ return dtls1_retransmit_buffered_messages(s) ; } +int +dtls1_get_queue_priority(unsigned short seq, int is_ccs) + { + /* The index of the retransmission queue actually is the message sequence number, + * since the queue only contains messages of a single handshake. However, the + * ChangeCipherSpec has no message sequence number and so using only the sequence + * will result in the CCS and Finished having the same index. To prevent this, + * the sequence number is multiplied by 2. In case of a CCS 1 is subtracted. + * This does not only differ CSS and Finished, it also maintains the order of the + * index (important for priority queues) and fits in the unsigned short variable. + */ + return seq * 2 - is_ccs; + } -static int +int dtls1_retransmit_buffered_messages(SSL *s) { pqueue sent = s->d1->sent_messages; @@ -957,8 +980,9 @@ for ( item = pqueue_next(&iter); item != NULL; item = pqueue_next(&iter)) { frag = (hm_fragment *)item->data; - if ( dtls1_retransmit_message(s, frag->msg_header.seq, 0, &found) <= 0 && - found) + if ( dtls1_retransmit_message(s, + dtls1_get_queue_priority(frag->msg_header.seq, frag->msg_header.is_ccs), + 0, &found) <= 0 && found) { fprintf(stderr, "dtls1_retransmit_message() failed\n"); return -1; @@ -974,7 +998,6 @@ pitem *item; hm_fragment *frag; PQ_64BIT seq64; - unsigned int epoch = s->d1->w_epoch; /* this function is called immediately after a message has * been serialized */ @@ -988,7 +1011,6 @@ { OPENSSL_assert(s->d1->w_msg_hdr.msg_len + DTLS1_CCS_HEADER_LENGTH <= (unsigned int)s->init_num); - epoch++; } else { @@ -1003,9 +1025,19 @@ frag->msg_header.frag_len = s->d1->w_msg_hdr.msg_len; frag->msg_header.is_ccs = is_ccs; + /* save current state*/ + frag->msg_header.saved_retransmit_state.enc_write_ctx = s->enc_write_ctx; + frag->msg_header.saved_retransmit_state.write_hash = s->write_hash; + frag->msg_header.saved_retransmit_state.compress = s->compress; + frag->msg_header.saved_retransmit_state.session = s->session; + frag->msg_header.saved_retransmit_state.epoch = s->d1->w_epoch; + pq_64bit_init(&seq64); - pq_64bit_assign_word(&seq64, epoch<<16 | frag->msg_header.seq); + pq_64bit_assign_word(&seq64, + dtls1_get_queue_priority(frag->msg_header.seq, + frag->msg_header.is_ccs)); + item = pitem_new(seq64, frag); pq_64bit_free(&seq64); if ( item == NULL) @@ -1034,6 +1066,8 @@ hm_fragment *frag ; unsigned long header_length; PQ_64BIT seq64; + struct dtls1_retransmit_state saved_state; + unsigned char save_write_sequence[8]; /* OPENSSL_assert(s->init_num == 0); @@ -1069,9 +1103,45 @@ frag->msg_header.msg_len, frag->msg_header.seq, 0, frag->msg_header.frag_len); + /* save current state */ + saved_state.enc_write_ctx = s->enc_write_ctx; + saved_state.write_hash = s->write_hash; + saved_state.compress = s->compress; + saved_state.session = s->session; + saved_state.epoch = s->d1->w_epoch; + saved_state.epoch = s->d1->w_epoch; + s->d1->retransmitting = 1; + + /* restore state in which the message was originally sent */ + s->enc_write_ctx = frag->msg_header.saved_retransmit_state.enc_write_ctx; + s->write_hash = frag->msg_header.saved_retransmit_state.write_hash; + s->compress = frag->msg_header.saved_retransmit_state.compress; + s->session = frag->msg_header.saved_retransmit_state.session; + s->d1->w_epoch = frag->msg_header.saved_retransmit_state.epoch; + + if (frag->msg_header.saved_retransmit_state.epoch == saved_state.epoch - 1) + { + memcpy(save_write_sequence, s->s3->write_sequence, sizeof(s->s3->write_sequence)); + memcpy(s->s3->write_sequence, s->d1->last_write_sequence, sizeof(s->s3->write_sequence)); + } + ret = dtls1_do_write(s, frag->msg_header.is_ccs ? - SSL3_RT_CHANGE_CIPHER_SPEC : SSL3_RT_HANDSHAKE); + SSL3_RT_CHANGE_CIPHER_SPEC : SSL3_RT_HANDSHAKE); + + /* restore current state */ + s->enc_write_ctx = saved_state.enc_write_ctx; + s->write_hash = saved_state.write_hash; + s->compress = saved_state.compress; + s->session = saved_state.session; + s->d1->w_epoch = saved_state.epoch; + + if (frag->msg_header.saved_retransmit_state.epoch == saved_state.epoch - 1) + { + memcpy(s->d1->last_write_sequence, s->s3->write_sequence, sizeof(s->s3->write_sequence)); + memcpy(s->s3->write_sequence, save_write_sequence, sizeof(s->s3->write_sequence)); + } + s->d1->retransmitting = 0; (void)BIO_flush(SSL_get_wbio(s)); --- ssl/d1_clnt.c 2008-06-04 20:35:25.000000000 +0200 +++ ssl/d1_clnt.c 2009-05-18 12:06:31.000000000 +0200 @@ -229,6 +229,7 @@ /* every DTLS ClientHello resets Finished MAC */ ssl3_init_finished_mac(s); + dtls1_start_timer(s); ret=dtls1_client_hello(s); if (ret <= 0) goto end; @@ -254,6 +255,7 @@ if (ret <= 0) goto end; else { + dtls1_stop_timer(s); if (s->hit) s->state=SSL3_ST_CR_FINISHED_A; else @@ -268,6 +270,7 @@ ret = dtls1_get_hello_verify(s); if ( ret <= 0) goto end; + dtls1_stop_timer(s); if ( s->d1->send_cookie) /* start again, with a cookie */ s->state=SSL3_ST_CW_CLNT_HELLO_A; else @@ -329,6 +332,7 @@ case SSL3_ST_CW_CERT_B: case SSL3_ST_CW_CERT_C: case SSL3_ST_CW_CERT_D: + dtls1_start_timer(s); ret=dtls1_send_client_certificate(s); if (ret <= 0) goto end; s->state=SSL3_ST_CW_KEY_EXCH_A; @@ -337,6 +341,7 @@ case SSL3_ST_CW_KEY_EXCH_A: case SSL3_ST_CW_KEY_EXCH_B: + dtls1_start_timer(s); ret=dtls1_send_client_key_exchange(s); if (ret <= 0) goto end; l=s->s3->tmp.new_cipher->algorithms; @@ -359,6 +364,7 @@ case SSL3_ST_CW_CERT_VRFY_A: case SSL3_ST_CW_CERT_VRFY_B: + dtls1_start_timer(s); ret=dtls1_send_client_verify(s); if (ret <= 0) goto end; s->state=SSL3_ST_CW_CHANGE_A; @@ -368,6 +374,7 @@ case SSL3_ST_CW_CHANGE_A: case SSL3_ST_CW_CHANGE_B: + dtls1_start_timer(s); ret=dtls1_send_change_cipher_spec(s, SSL3_ST_CW_CHANGE_A,SSL3_ST_CW_CHANGE_B); if (ret <= 0) goto end; @@ -402,6 +409,7 @@ case SSL3_ST_CW_FINISHED_A: case SSL3_ST_CW_FINISHED_B: + dtls1_start_timer(s); ret=dtls1_send_finished(s, SSL3_ST_CW_FINISHED_A,SSL3_ST_CW_FINISHED_B, s->method->ssl3_enc->client_finished_label, @@ -426,8 +434,6 @@ s->s3->tmp.next_state=SSL3_ST_CR_FINISHED_A; } s->init_num=0; - /* mark client_random uninitialized */ - memset (s->s3->client_random,0,sizeof(s->s3->client_random)); break; @@ -437,6 +443,7 @@ ret=ssl3_get_finished(s,SSL3_ST_CR_FINISHED_A, SSL3_ST_CR_FINISHED_B); if (ret <= 0) goto end; + dtls1_stop_timer(s); if (s->hit) s->state=SSL3_ST_CW_CHANGE_A; @@ -492,6 +499,7 @@ /* done with handshaking */ s->d1->handshake_read_seq = 0; + s->d1->next_handshake_write_seq = 0; goto end; /* break; */ --- ssl/d1_lib.c 2008-10-13 08:43:05.000000000 +0200 +++ ssl/d1_lib.c 2009-05-18 12:06:31.000000000 +0200 @@ -61,6 +61,10 @@ #include #include "ssl_locl.h" +#ifdef OPENSSL_SYS_WIN32 +#include +#endif + const char dtls1_version_str[]="DTLSv1" OPENSSL_VERSION_PTEXT; SSL3_ENC_METHOD DTLSv1_enc_data={ @@ -114,6 +118,7 @@ d1->processed_rcds.q=pqueue_new(); d1->buffered_messages = pqueue_new(); d1->sent_messages=pqueue_new(); + d1->buffered_app_data.q=pqueue_new(); if ( s->server) { @@ -121,12 +126,13 @@ } if( ! d1->unprocessed_rcds.q || ! d1->processed_rcds.q - || ! d1->buffered_messages || ! d1->sent_messages) + || ! d1->buffered_messages || ! d1->sent_messages || ! d1->buffered_app_data.q) { if ( d1->unprocessed_rcds.q) pqueue_free(d1->unprocessed_rcds.q); if ( d1->processed_rcds.q) pqueue_free(d1->processed_rcds.q); if ( d1->buffered_messages) pqueue_free(d1->buffered_messages); if ( d1->sent_messages) pqueue_free(d1->sent_messages); + if ( d1->buffered_app_data.q) pqueue_free(d1->buffered_app_data.q); OPENSSL_free(d1); return (0); } @@ -175,6 +181,15 @@ } pqueue_free(s->d1->sent_messages); + while ( (item = pqueue_pop(s->d1->buffered_app_data.q)) != NULL) + { + frag = (hm_fragment *)item->data; + OPENSSL_free(frag->fragment); + OPENSSL_free(frag); + pitem_free(item); + } + pqueue_free(s->d1->buffered_app_data.q); + pq_64bit_free(&(s->d1->bitmap.map)); pq_64bit_free(&(s->d1->bitmap.max_seq_num)); @@ -209,3 +224,110 @@ return ciph; } + +void dtls1_start_timer(SSL *s) + { +#ifdef OPENSSL_SYS_WIN32 + struct _timeb tb; +#endif + + /* If timer is not set, initialize duration with 1 second */ + if (s->d1->next_timeout.tv_sec == 0 && s->d1->next_timeout.tv_usec == 0) + { + s->d1->timeout_duration = 1; + } + + /* Set timeout to current time */ +#ifdef OPENSSL_SYS_WIN32 + _ftime(&tb); + s->d1->next_timeout.tv_sec = (long) tb.time; + s->d1->next_timeout.tv_usec = (long) tb.millitm * 1000; +#else + gettimeofday(&(s->d1->next_timeout), NULL); +#endif + + /* Add duration to current time */ + s->d1->next_timeout.tv_sec += s->d1->timeout_duration; + BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0, &(s->d1->next_timeout)); + } + +struct timeval* dtls1_get_timeout(SSL *s, struct timeval* timeleft) + { +#ifdef OPENSSL_SYS_WIN32 + struct _timeb tb; +#endif + struct timeval timenow; + + /* If no timeout is set, just return NULL */ + if (s->d1->next_timeout.tv_sec == 0 && s->d1->next_timeout.tv_usec == 0) + { + return NULL; + } + + /* Get current time */ +#ifdef OPENSSL_SYS_WIN32 + _ftime(&tb); + timenow.tv_sec = (long) tb.time; + timenow.tv_usec = (long) tb.millitm * 1000; +#else + gettimeofday(&timenow, NULL); +#endif + + /* If timer already expired, set remaining time to 0 */ + if (s->d1->next_timeout.tv_sec < timenow.tv_sec || + (s->d1->next_timeout.tv_sec == timenow.tv_sec && + s->d1->next_timeout.tv_usec <= timenow.tv_usec)) + { + memset(timeleft, 0, sizeof(struct timeval)); + return timeleft; + } + + /* Calculate time left until timer expires */ + memcpy(timeleft, &(s->d1->next_timeout), sizeof(struct timeval)); + timeleft->tv_sec -= timenow.tv_sec; + timeleft->tv_usec -= timenow.tv_usec; + if (timeleft->tv_usec < 0) + { + timeleft->tv_sec--; + timeleft->tv_usec += 1000000; + } + + return timeleft; + } + +int dtls1_is_timer_expired(SSL *s) + { + struct timeval timeleft; + + /* Get time left until timeout, return false if no timer running */ + if (dtls1_get_timeout(s, &timeleft) == NULL) + { + return 0; + } + + /* Return false if timer is not expired yet */ + if (timeleft.tv_sec > 0 || timeleft.tv_usec > 0) + { + return 0; + } + + /* Timer expired, so return true */ + return 1; + } + +void dtls1_double_timeout(SSL *s) + { + s->d1->timeout_duration *= 2; + if (s->d1->timeout_duration > 60) + s->d1->timeout_duration = 60; + dtls1_start_timer(s); + } + +void dtls1_stop_timer(SSL *s) + { + /* Reset everything */ + memset(&(s->d1->next_timeout), 0, sizeof(struct timeval)); + s->d1->timeout_duration = 1; + BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0, &(s->d1->next_timeout)); + } + --- ssl/d1_pkt.c 2008-10-13 08:43:06.000000000 +0200 +++ ssl/d1_pkt.c 2009-05-18 12:06:31.000000000 +0200 @@ -167,6 +167,10 @@ DTLS1_RECORD_DATA *rdata; pitem *item; + /* Limit the size of the queue to prevent DOS attacks */ + if (pqueue_size(queue->q) >= 100) + return 0; + rdata = OPENSSL_malloc(sizeof(DTLS1_RECORD_DATA)); item = pitem_new(priority, rdata); if (rdata == NULL || item == NULL) @@ -703,6 +707,30 @@ * s->s3->rrec.length, - number of bytes. */ rr = &(s->s3->rrec); + /* We are not handshaking and have no data yet, + * so process data buffered during the last handshake + * in advance, if any. + */ + if (s->state == SSL_ST_OK && rr->length == 0) + { + pitem *item; + item = pqueue_pop(s->d1->buffered_app_data.q); + if (item) + { + dtls1_copy_record(s, item); + + OPENSSL_free(item->data); + pitem_free(item); + } + } + + /* Check for timeout */ + if (dtls1_is_timer_expired(s)) + { + if (dtls1_read_failed(s, -1) > 0); + goto start; + } + /* get new packet if necessary */ if ((rr->length == 0) || (s->rstate == SSL_ST_READ_BODY)) { @@ -724,9 +752,14 @@ * reset by ssl3_get_finished */ && (rr->type != SSL3_RT_HANDSHAKE)) { - al=SSL_AD_UNEXPECTED_MESSAGE; - SSLerr(SSL_F_DTLS1_READ_BYTES,SSL_R_DATA_BETWEEN_CCS_AND_FINISHED); - goto err; + /* We now have application data between CCS and Finished. + * Most likely the packets were reordered on their way, so + * buffer the application data for later processing rather + * than dropping the connection. + */ + dtls1_buffer_record(s, &(s->d1->buffered_app_data), 0); + rr->length = 0; + goto start; } /* If the other end has shut down, throw anything we read away @@ -796,15 +829,28 @@ dest = s->d1->alert_fragment; dest_len = &s->d1->alert_fragment_len; } - /* else it's a CCS message, or it's wrong */ - else if (rr->type != SSL3_RT_CHANGE_CIPHER_SPEC) - { - /* Not certain if this is the right error handling */ - al=SSL_AD_UNEXPECTED_MESSAGE; - SSLerr(SSL_F_DTLS1_READ_BYTES,SSL_R_UNEXPECTED_RECORD); - goto f_err; - } + /* else it's a CCS message, or application data or wrong */ + else if (rr->type != SSL3_RT_CHANGE_CIPHER_SPEC) + { + /* Application data while renegotiating + * is allowed. Try again reading. + */ + if (rr->type == SSL3_RT_APPLICATION_DATA) + { + BIO *bio; + s->s3->in_read_app_data=2; + bio=SSL_get_rbio(s); + s->rwstate=SSL_READING; + BIO_clear_retry_flags(bio); + BIO_set_retry_read(bio); + return(-1); + } + /* Not certain if this is the right error handling */ + al=SSL_AD_UNEXPECTED_MESSAGE; + SSLerr(SSL_F_DTLS1_READ_BYTES,SSL_R_UNEXPECTED_RECORD); + goto f_err; + } if (dest_maxlen > 0) { @@ -942,7 +988,9 @@ n2s(p, seq); n2l3(p, frag_off); - dtls1_retransmit_message(s, seq, frag_off, &found); + dtls1_retransmit_message(s, + dtls1_get_queue_priority(frag->msg_header.seq, 0), + frag_off, &found); if ( ! found && SSL_in_init(s)) { /* fprintf( stderr,"in init = %d\n", SSL_in_init(s)); */ @@ -1035,6 +1083,16 @@ goto start; } + /* If we are server, we may have a repeated FINISHED of the + * client here, then retransmit our CCS and FINISHED. + */ + if (msg_hdr.type == SSL3_MT_FINISHED) + { + dtls1_retransmit_buffered_messages(s); + rr->length = 0; + goto start; + } + if (((s->state&SSL_ST_MASK) == SSL_ST_OK) && !(s->s3->flags & SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS)) { @@ -1758,6 +1816,7 @@ else { seq = s->s3->write_sequence; + memcpy(s->d1->last_write_sequence, seq, sizeof(s->s3->write_sequence)); s->d1->w_epoch++; } --- ssl/d1_srvr.c 2008-09-14 16:02:01.000000000 +0200 +++ ssl/d1_srvr.c 2009-05-18 12:06:31.000000000 +0200 @@ -247,6 +247,7 @@ case SSL3_ST_SW_HELLO_REQ_B: s->shutdown=0; + dtls1_start_timer(s); ret=dtls1_send_hello_request(s); if (ret <= 0) goto end; s->s3->tmp.next_state=SSL3_ST_SW_HELLO_REQ_C; @@ -267,6 +268,7 @@ s->shutdown=0; ret=ssl3_get_client_hello(s); if (ret <= 0) goto end; + dtls1_stop_timer(s); s->new_session = 2; if ( s->d1->send_cookie) @@ -280,6 +282,7 @@ case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A: case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B: + dtls1_start_timer(s); ret = dtls1_send_hello_verify_request(s); if ( ret <= 0) goto end; s->d1->send_cookie = 0; @@ -293,6 +296,7 @@ case SSL3_ST_SW_SRVR_HELLO_A: case SSL3_ST_SW_SRVR_HELLO_B: + dtls1_start_timer(s); ret=dtls1_send_server_hello(s); if (ret <= 0) goto end; @@ -308,6 +312,7 @@ /* Check if it is anon DH */ if (!(s->s3->tmp.new_cipher->algorithms & SSL_aNULL)) { + dtls1_start_timer(s); ret=dtls1_send_server_certificate(s); if (ret <= 0) goto end; } @@ -349,6 +354,7 @@ ) ) { + dtls1_start_timer(s); ret=dtls1_send_server_key_exchange(s); if (ret <= 0) goto end; } @@ -385,6 +391,7 @@ else { s->s3->tmp.cert_request=1; + dtls1_start_timer(s); ret=dtls1_send_certificate_request(s); if (ret <= 0) goto end; #ifndef NETSCAPE_HANG_BUG @@ -399,6 +406,7 @@ case SSL3_ST_SW_SRVR_DONE_A: case SSL3_ST_SW_SRVR_DONE_B: + dtls1_start_timer(s); ret=dtls1_send_server_done(s); if (ret <= 0) goto end; s->s3->tmp.next_state=SSL3_ST_SR_CERT_A; @@ -426,6 +434,7 @@ ret = ssl3_check_client_hello(s); if (ret <= 0) goto end; + dtls1_stop_timer(s); if (ret == 2) s->state = SSL3_ST_SR_CLNT_HELLO_C; else { @@ -433,6 +442,7 @@ * have not asked for it :-) */ ret=ssl3_get_client_certificate(s); if (ret <= 0) goto end; + dtls1_stop_timer(s); s->init_num=0; s->state=SSL3_ST_SR_KEY_EXCH_A; } @@ -442,6 +452,7 @@ case SSL3_ST_SR_KEY_EXCH_B: ret=ssl3_get_client_key_exchange(s); if (ret <= 0) goto end; + dtls1_stop_timer(s); s->state=SSL3_ST_SR_CERT_VRFY_A; s->init_num=0; @@ -462,6 +473,7 @@ /* we should decide if we expected this one */ ret=ssl3_get_cert_verify(s); if (ret <= 0) goto end; + dtls1_stop_timer(s); s->state=SSL3_ST_SR_FINISHED_A; s->init_num=0; @@ -472,6 +484,7 @@ ret=ssl3_get_finished(s,SSL3_ST_SR_FINISHED_A, SSL3_ST_SR_FINISHED_B); if (ret <= 0) goto end; + dtls1_stop_timer(s); if (s->hit) s->state=SSL_ST_OK; else @@ -554,6 +567,7 @@ s->d1->handshake_read_seq = 0; /* next message is server hello */ s->d1->handshake_write_seq = 0; + s->d1->next_handshake_write_seq = 0; goto end; /* break; */ --- ssl/dtls1.h 2008-09-13 20:25:36.000000000 +0200 +++ ssl/dtls1.h 2009-05-18 12:06:35.000000000 +0200 @@ -101,6 +101,19 @@ PQ_64BIT max_seq_num; /* max record number seen so far */ } DTLS1_BITMAP; +struct dtls1_retransmit_state + { + EVP_CIPHER_CTX *enc_write_ctx; /* cryptographic state */ + const EVP_MD *write_hash; /* used for mac generation */ +#ifndef OPENSSL_NO_COMP + COMP_CTX *compress; /* compression */ +#else + char *compress; +#endif + SSL_SESSION *session; + unsigned short epoch; + }; + struct hm_header_st { unsigned char type; @@ -109,6 +122,7 @@ unsigned long frag_off; unsigned long frag_len; unsigned int is_ccs; + struct dtls1_retransmit_state saved_retransmit_state; }; struct ccs_header_st @@ -168,6 +182,9 @@ unsigned short handshake_read_seq; + /* save last sequence number for retransmissions */ + unsigned char last_write_sequence[8]; + /* Received handshake records (processed and unprocessed) */ record_pqueue unprocessed_rcds; record_pqueue processed_rcds; @@ -178,13 +195,26 @@ /* Buffered (sent) handshake records */ pqueue sent_messages; - unsigned int mtu; /* max wire packet size */ + /* Buffered application records. + * Only for records between CCS and Finished + * to prevent either protocol violation or + * unnecessary message loss. + */ + record_pqueue buffered_app_data; + + unsigned int mtu; /* max DTLS packet size */ struct hm_header_st w_msg_hdr; struct hm_header_st r_msg_hdr; struct dtls1_timeout_st timeout; - + + /* Indicates when the last handshake msg sent will timeout */ + struct timeval next_timeout; + + /* Timeout duration */ + unsigned short timeout_duration; + /* storage for Alert/Handshake protocol data received but not * yet processed by ssl3_read_bytes: */ unsigned char alert_fragment[DTLS1_AL_HEADER_LENGTH]; --- ssl/ssl_locl.h 2009-01-05 15:43:07.000000000 +0100 +++ ssl/ssl_locl.h 2009-05-18 12:06:31.000000000 +0200 @@ -862,11 +862,14 @@ int dtls1_buffer_message(SSL *s, int ccs); int dtls1_retransmit_message(SSL *s, unsigned short seq, unsigned long frag_off, int *found); +int dtls1_get_queue_priority(unsigned short seq, int is_ccs); +int dtls1_retransmit_buffered_messages(SSL *s); void dtls1_clear_record_buffer(SSL *s); void dtls1_get_message_header(unsigned char *data, struct hm_header_st *msg_hdr); void dtls1_get_ccs_header(unsigned char *data, struct ccs_header_st *ccs_hdr); void dtls1_reset_seq_numbers(SSL *s, int rw); long dtls1_default_timeout(void); +struct timeval* dtls1_get_timeout(SSL *s, struct timeval* timeleft); SSL_CIPHER *dtls1_get_cipher(unsigned int u);