This updated version includes the suggestions of Nagendra Modadugu.
--- ssl/d1_both.c 2007-10-17 23:17:49.000000000 +0200 +++ ssl/d1_both.c 2009-03-23 19:39:10.000000000 +0100 @@ -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); @@ -942,8 +941,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 +969,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 +987,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 +1000,6 @@ { OPENSSL_assert(s->d1->w_msg_hdr.msg_len + DTLS1_CCS_HEADER_LENGTH <= (unsigned int)s->init_num); - epoch++; } else { @@ -1003,9 +1014,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 +1055,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 +1092,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_pkt.c 2008-10-13 08:43:06.000000000 +0200 +++ ssl/d1_pkt.c 2009-03-23 19:45:04.000000000 +0100 @@ -942,7 +942,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 +1037,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 +1770,7 @@ else { seq = s->s3->write_sequence; + memcpy(s->d1->last_write_sequence, seq, sizeof(s->s3- >write_sequence)); s->d1->w_epoch++; } --- ssl/dtls1.h 2008-09-13 20:25:36.000000000 +0200 +++ ssl/dtls1.h 2009-03-23 19:31:39.000000000 +0100 @@ -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; --- ssl/ssl_locl.h 2009-01-05 15:43:07.000000000 +0100 +++ ssl/ssl_locl.h 2009-03-23 19:42:12.000000000 +0100 @@ -862,6 +862,8 @@ 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); ______________________________________________________________________ OpenSSL Project http://www.openssl.org Development Mailing List openssl-dev@openssl.org Automated List Manager majord...@openssl.org