This patch adds the function dtls1_listen(SSL *s, struct sockaddr *client), as well as the user accessible macro DTLSv1_listen(). It is intended to be called with an SSL object with a listening socket. Every ClientHello arriving will be answered with a HelloVerifyRequest without allocating any memory. When a ClientHello is repeated with a valid cookie attached, dtls1_listen() returns after entering the peer's address into the given sockaddr structure. The application can then create an UDP socket connected to that peer, assign it to the SSL object and continue the connection (with a SSL_accept() to complete the handshake, since the dtls1_listen returns before sending the ServerHello) in a new thread. Then a new SSL object has to be created and the listening socket has to be assigned to it, so that following connections can be handled.
Here's an example how the server can look like: --------------------------------------------- /* Create listening socket */ fd = socket(AF_INET, SOCK_DGRAM, 0); setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void*) &on, (socklen_t) sizeof(on)); setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (const void*) &on, (socklen_t) sizeof(on)); bind(fd, (const struct sockaddr *) &server_addr, sizeof(server_addr)); while (1) { memset(&client_addr, 0, sizeof(struct sockaddr)); /* Create new BIO with the listening socket and new SSL object */ bio = BIO_new_dgram(fd, BIO_NOCLOSE); ssl = SSL_new(ctx); /* Assign BIO and activate cookie exchange */ SSL_set_bio(ssl, bio, bio); SSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE); /* Wait for ClientHello with valid cookie (blocking) */ while (!DTLSv1_listen(ssl, &client_addr)); /* Pass SSL object and peer info to a new thread */ info = (struct pass_info*) malloc (sizeof(struct pass_info)); memcpy(&info->server_addr, &server_addr, sizeof(struct sockaddr)); memcpy(&info->client_addr, &client_addr, sizeof(struct sockaddr)); info->ssl = ssl; pthread_create( &tid, NULL, connection_thread, info); } --------------------------------------------- The new thread can then create a connected socket with the passed information and assign the new socket to the existing SSL object to continue with a one-to-one connection. --------------------------------------------- fd = socket(AF_INET, SOCK_DGRAM, 0); setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void*) &on, (socklen_t) sizeof(on)); setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (const void*) &on, (socklen_t) sizeof(on)); bind(fd, (const struct sockaddr *) server_addr, sizeof(struct sockaddr)); connect(fd, (struct sockaddr *) client_addr, sizeof(struct sockaddr)); /* Set new fd and set BIO to connected */ BIO_set_fd(SSL_get_rbio(ssl), fd, BIO_NOCLOSE); BIO_ctrl(SSL_get_rbio(ssl), BIO_CTRL_DGRAM_SET_CONNECTED, 0, client_addr); /* Finish handshake */ SSL_accept(ssl); --------------------------------------------- This allows clean multi-threaded applications with UDP, since accept() can't be used and a continuous listening socket is necessary to avoid the loss of messages until a new listening socket has been created after connecting the previous one. It also allows to use the HelloVerifyRequest functionallity to avoid denial of service attacks, because otherwise resources (connected socket, thread) have to be allocated for every new connection to perform a handshake, maybe just to await the timeout because the peer doesn't answer. --- ssl/d1_lib.c 12 Aug 2009 17:30:36 -0000 1.16 +++ ssl/d1_lib.c 3 Sep 2009 09:59:22 -0000 @@ -203,6 +203,9 @@ case DTLS_CTRL_HANDLE_TIMEOUT: ret = dtls1_handle_timeout(s); break; + case DTLS_CTRL_LISTEN: + ret = dtls1_listen(s, parg); + break; default: ret = ssl3_ctrl(s, cmd, larg, parg); @@ -364,3 +367,17 @@ gettimeofday(t, NULL); #endif } + +int dtls1_listen(SSL *s, struct sockaddr *client) + { + int ret; + + SSL_set_options(s, SSL_OP_COOKIE_EXCHANGE); + s->d1->listen = 1; + + ret = SSL_accept(s); + if (ret <= 0) return 0; + + BIO_dgram_get_peer(SSL_get_rbio(s), client); + return 1; + } --- ssl/d1_srvr.c 5 Jun 2009 14:59:26 -0000 1.25 +++ ssl/d1_srvr.c 3 Sep 2009 09:59:22 -0000 @@ -279,6 +279,15 @@ s->state = SSL3_ST_SW_SRVR_HELLO_A; s->init_num=0; + + /* If we're just listening, stop here */ + if (s->d1->listen && s->state == SSL3_ST_SW_SRVR_HELLO_A) + { + ret = 2; + s->d1->listen = 0; + goto end; + } + break; case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A: --- ssl/dtls1.h 17 Jun 2009 11:37:44 -0000 1.21 +++ ssl/dtls1.h 3 Sep 2009 09:59:22 -0000 @@ -212,6 +212,9 @@ */ record_pqueue buffered_app_data; + /* Is set when listening for new connections with dtls1_listen() */ + unsigned int listen; + unsigned int mtu; /* max DTLS packet size */ struct hm_header_st w_msg_hdr; --- ssl/ssl.h 26 Aug 2009 11:51:57 -0000 1.231 +++ ssl/ssl.h 3 Sep 2009 09:59:22 -0000 @@ -1398,11 +1398,14 @@ #define DTLS_CTRL_GET_TIMEOUT 73 #define DTLS_CTRL_HANDLE_TIMEOUT 74 +#define DTLS_CTRL_LISTEN 75 #define DTLSv1_get_timeout(ssl, arg) \ SSL_ctrl(ssl,DTLS_CTRL_GET_TIMEOUT,0, (void *)arg) #define DTLSv1_handle_timeout(ssl) \ SSL_ctrl(ssl,DTLS_CTRL_HANDLE_TIMEOUT,0, NULL) +#define DTLSv1_listen(ssl, peer) \ + SSL_ctrl(ssl,DTLS_CTRL_LISTEN,0, (void *)peer) #define SSL_session_reused(ssl) \ SSL_ctrl((ssl),SSL_CTRL_GET_SESSION_REUSED,0,NULL)
dtls-listen.patch
Description: Binary data