diff --git a/cyassl/ctaocrypt/types.h b/cyassl/ctaocrypt/types.h index 786a82b8d..cfe2feac8 100644 --- a/cyassl/ctaocrypt/types.h +++ b/cyassl/ctaocrypt/types.h @@ -220,7 +220,8 @@ enum { DYNAMIC_TYPE_CIPHER = 31, DYNAMIC_TYPE_RNG = 32, DYNAMIC_TYPE_ARRAYS = 33, - DYNAMIC_TYPE_DTLS_POOL = 34 + DYNAMIC_TYPE_DTLS_POOL = 34, + DYNAMIC_TYPE_SOCKADDR = 35 }; /* stack protection */ diff --git a/cyassl/internal.h b/cyassl/internal.h index e6e276e8f..6aa119da4 100644 --- a/cyassl/internal.h +++ b/cyassl/internal.h @@ -671,6 +671,10 @@ int SetCipherList(Suites*, const char* list); #endif #ifdef CYASSL_DTLS + CYASSL_LOCAL + int EmbedReceiveFrom(CYASSL *ssl, char *buf, int sz, void *ctx); + CYASSL_LOCAL + int EmbedSendTo(CYASSL *ssl, char *buf, int sz, void *ctx); CYASSL_LOCAL int EmbedGenerateCookie(byte *buf, int sz, void *ctx); CYASSL_LOCAL @@ -789,6 +793,18 @@ struct CYASSL_CERT_MANAGER { }; +/* CyaSSL Sock Addr */ +struct CYASSL_SOCKADDR { + unsigned int sz; /* sockaddr size */ + void* sa; /* pointer to the sockaddr_in or sockaddr_in6 */ +}; + +typedef struct CYASSL_DTLS_CTX { + CYASSL_SOCKADDR peer; + int fd; +} CYASSL_DTLS_CTX; + + /* CyaSSL context type */ struct CYASSL_CTX { CYASSL_METHOD* method; @@ -1116,6 +1132,7 @@ typedef struct Buffers { buffer dtlsHandshake; /* DTLS handshake defragment buf */ word32 dtlsUsed; /* DTLS bytes used in buffer */ byte dtlsType; /* DTLS handshake frag type */ + CYASSL_DTLS_CTX dtlsCtx; /* DTLS connection context */ #endif } Buffers; diff --git a/cyassl/ssl.h b/cyassl/ssl.h index 0072028f9..1d1adde5d 100644 --- a/cyassl/ssl.h +++ b/cyassl/ssl.h @@ -66,6 +66,7 @@ typedef struct CYASSL_X509_NAME CYASSL_X509_NAME; typedef struct CYASSL_X509_CHAIN CYASSL_X509_CHAIN; typedef struct CYASSL_CERT_MANAGER CYASSL_CERT_MANAGER; +typedef struct CYASSL_SOCKADDR CYASSL_SOCKADDR; /* redeclare guard */ #define CYASSL_TYPES_DEFINED @@ -222,6 +223,9 @@ CYASSL_API int CyaSSL_dtls_get_current_timeout(CYASSL* ssl); CYASSL_API int CyaSSL_dtls_got_timeout(CYASSL* ssl); CYASSL_API int CyaSSL_dtls(CYASSL* ssl); +CYASSL_API int CyaSSL_dtls_set_peer(CYASSL*, void*, unsigned int); +CYASSL_API int CyaSSL_dtls_get_peer(CYASSL*, void*, unsigned int*); + CYASSL_API int CyaSSL_ERR_GET_REASON(int err); CYASSL_API char* CyaSSL_ERR_error_string(unsigned long,char*); CYASSL_API void CyaSSL_ERR_error_string_n(unsigned long e, char* buf, diff --git a/cyassl/test.h b/cyassl/test.h index f73f68c61..84e66fbf0 100644 --- a/cyassl/test.h +++ b/cyassl/test.h @@ -307,8 +307,8 @@ static INLINE void showPeer(CYASSL* ssl) } -static INLINE void tcp_socket(SOCKET_T* sockfd, SOCKADDR_IN_T* addr, - const char* peer, word16 port, int udp) +static INLINE void build_addr(SOCKADDR_IN_T* addr, + const char* peer, word16 port) { #ifndef TEST_IPV6 const char* host = peer; @@ -329,10 +329,6 @@ static INLINE void tcp_socket(SOCKET_T* sockfd, SOCKADDR_IN_T* addr, } #endif - if (udp) - *sockfd = socket(AF_INET_V, SOCK_DGRAM, 0); - else - *sockfd = socket(AF_INET_V, SOCK_STREAM, 0); memset(addr, 0, sizeof(SOCKADDR_IN_T)); #ifndef TEST_IPV6 @@ -347,6 +343,15 @@ static INLINE void tcp_socket(SOCKET_T* sockfd, SOCKADDR_IN_T* addr, addr->sin6_port = htons(port); addr->sin6_addr = in6addr_loopback; #endif +} + + +static INLINE void tcp_socket(SOCKET_T* sockfd, int udp) +{ + if (udp) + *sockfd = socket(AF_INET_V, SOCK_DGRAM, 0); + else + *sockfd = socket(AF_INET_V, SOCK_STREAM, 0); #ifndef USE_WINDOWS_API #ifdef SO_NOSIGPIPE @@ -379,9 +384,19 @@ static INLINE void tcp_connect(SOCKET_T* sockfd, const char* ip, word16 port, int udp) { SOCKADDR_IN_T addr; - tcp_socket(sockfd, &addr, ip, port, udp); + build_addr(&addr, ip, port); + tcp_socket(sockfd, udp); - if (connect(*sockfd, (const struct sockaddr*)&addr, sizeof(addr)) != 0) + if (!udp) { + if (connect(*sockfd, (const struct sockaddr*)&addr, sizeof(addr)) != 0) + err_sys("tcp connect failed"); + } +} + + +static INLINE void udp_connect(SOCKET_T* sockfd, void* addr, int addrSz) +{ + if (connect(*sockfd, (const struct sockaddr*)addr, addrSz) != 0) err_sys("tcp connect failed"); } @@ -393,10 +408,8 @@ static INLINE void tcp_listen(SOCKET_T* sockfd, int port, int useAnyAddr, /* don't use INADDR_ANY by default, firewall may block, make user switch on */ - if (useAnyAddr) - tcp_socket(sockfd, &addr, INADDR_ANY, port, udp); - else - tcp_socket(sockfd, &addr, yasslIP, port, udp); + build_addr(&addr, (useAnyAddr ? INADDR_ANY : yasslIP), port); + tcp_socket(sockfd, udp); #ifndef USE_WINDOWS_API { @@ -439,8 +452,9 @@ static INLINE void udp_accept(SOCKET_T* sockfd, int* clientfd, func_args* args) { SOCKADDR_IN_T addr; - (void)args; - tcp_socket(sockfd, &addr, yasslIP, yasslPort, 1); + (void)args; + build_addr(&addr, yasslIP, yasslPort); + tcp_socket(sockfd, 1); #ifndef USE_WINDOWS_API diff --git a/examples/client/client.c b/examples/client/client.c index 6fc1023d8..d2e9c7ce4 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -343,10 +343,18 @@ void client_test(void* args) exit(EXIT_SUCCESS); } - tcp_connect(&sockfd, host, port, doDTLS); ssl = CyaSSL_new(ctx); if (ssl == NULL) err_sys("unable to get SSL object"); + if (doDTLS) { + SOCKADDR_IN_T addr; + build_addr(&addr, host, port); + CyaSSL_dtls_set_peer(ssl, &addr, sizeof(addr)); + tcp_socket(&sockfd, 1); + } + else { + tcp_connect(&sockfd, host, port, 0); + } CyaSSL_set_fd(ssl, sockfd); #ifdef HAVE_CRL if (CyaSSL_EnableCRL(ssl, CYASSL_CRL_CHECKALL) != SSL_SUCCESS) @@ -422,19 +430,25 @@ void client_test(void* args) #ifdef TEST_RESUME if (doDTLS) { + SOCKADDR_IN_T addr; #ifdef USE_WINDOWS_API Sleep(500); #else sleep(1); #endif + build_addr(&addr, host, port); + CyaSSL_dtls_set_peer(sslResume, &addr, sizeof(addr)); + tcp_socket(&sockfd, 1); + } + else { + tcp_connect(&sockfd, host, port, 0); } - tcp_connect(&sockfd, host, port, doDTLS); CyaSSL_set_fd(sslResume, sockfd); CyaSSL_set_session(sslResume, session); showPeer(sslResume); #ifdef NON_BLOCKING - CyaSSL_using_nonblock(sslResume); + CyaSSL_set_using_nonblock(sslResume, 1); tcp_set_nonblocking(&sockfd); NonBlockingSSL_Connect(sslResume); #else diff --git a/examples/echoclient/echoclient.c b/examples/echoclient/echoclient.c index 1b26571e0..59a75c9eb 100644 --- a/examples/echoclient/echoclient.c +++ b/examples/echoclient/echoclient.c @@ -70,8 +70,6 @@ void echoclient_test(void* args) doDTLS = 1; #endif - tcp_connect(&sockfd, yasslIP, yasslPort, doDTLS); - #if defined(CYASSL_DTLS) method = DTLSv1_client_method(); #elif !defined(NO_TLS) @@ -102,6 +100,16 @@ void echoclient_test(void* args) #endif ssl = SSL_new(ctx); + if (doDTLS) { + SOCKADDR_IN_T addr; + build_addr(&addr, yasslIP, yasslPort); + CyaSSL_dtls_set_peer(ssl, &addr, sizeof(addr)); + tcp_socket(&sockfd, 1); + } + else { + tcp_connect(&sockfd, yasslIP, yasslPort, 0); + } + SSL_set_fd(ssl, sockfd); #if defined(USE_WINDOWS_API) && defined(CYASSL_DTLS) && defined(NO_MAIN_DRIVER) /* let echoserver bind first, TODO: add Windows signal like pthreads does */ diff --git a/src/internal.c b/src/internal.c index 775ec9e66..58c42d1d6 100644 --- a/src/internal.c +++ b/src/internal.c @@ -344,6 +344,13 @@ int InitSSL_Ctx(CYASSL_CTX* ctx, CYASSL_METHOD* method) #ifndef CYASSL_USER_IO ctx->CBIORecv = EmbedReceive; ctx->CBIOSend = EmbedSend; + #ifdef CYASSL_DTLS + if (method->version.major == DTLS_MAJOR + && method->version.minor == DTLS_MINOR) { + ctx->CBIORecv = EmbedReceiveFrom; + ctx->CBIOSend = EmbedSendTo; + } + #endif #else /* user will set */ ctx->CBIORecv = NULL; @@ -1018,7 +1025,10 @@ int InitSSL(CYASSL* ssl, CYASSL_CTX* ctx) ssl->heap = ctx->heap; /* defaults to self */ ssl->options.tls = 0; ssl->options.tls1_1 = 0; - ssl->options.dtls = 0; + if (ssl->version.major == DTLS_MAJOR && ssl->version.minor == DTLS_MINOR) + ssl->options.dtls = 1; + else + ssl->options.dtls = 0; ssl->options.partialWrite = ctx->partialWrite; ssl->options.quietShutdown = ctx->quietShutdown; ssl->options.certOnly = 0; @@ -1042,6 +1052,9 @@ int InitSSL(CYASSL* ssl, CYASSL_CTX* ctx) ssl->buffers.dtlsHandshake.length = 0; ssl->buffers.dtlsHandshake.buffer = NULL; ssl->buffers.dtlsType = 0; + ssl->buffers.dtlsCtx.fd = -1; + ssl->buffers.dtlsCtx.peer.sa = NULL; + ssl->buffers.dtlsCtx.peer.sz = 0; #endif #ifdef OPENSSL_EXTRA @@ -1199,6 +1212,8 @@ void SSL_ResourceFree(CYASSL* ssl) DtlsPoolReset(ssl); XFREE(ssl->dtls_pool, ssl->heap, DYNAMIC_TYPE_NONE); } + XFREE(ssl->buffers.dtlsCtx.peer.sa, ssl->heap, DYNAMIC_TYPE_SOCKADDR); + ssl->buffers.dtlsCtx.peer.sa = NULL; #endif #if defined(OPENSSL_EXTRA) || defined(GOAHEAD_WS) XFREE(ssl->peerCert.derCert.buffer, ssl->heap, DYNAMIC_TYPE_CERT); @@ -1608,7 +1623,7 @@ retry: case IO_ERR_CONN_RST: /* connection reset */ #ifdef USE_WINDOWS_API if (ssl->options.dtls) { - return WANT_READ; + goto retry; } #endif ssl->options.connReset = 1; diff --git a/src/io.c b/src/io.c index d80848210..7ed6f9e85 100644 --- a/src/io.c +++ b/src/io.c @@ -86,7 +86,7 @@ #define WSAEPIPE -12345 #endif #define SOCKET_EWOULDBLOCK WSAEWOULDBLOCK - #define SOCKET_EAGAIN WSAEWOULDBLOCK + #define SOCKET_EAGAIN WSAETIMEDOUT #define SOCKET_ECONNRESET WSAECONNRESET #define SOCKET_EINTR WSAEINTR #define SOCKET_EPIPE WSAEPIPE @@ -150,7 +150,7 @@ int EmbedReceive(CYASSL *ssl, char *buf, int sz, void *ctx) && !CyaSSL_get_using_nonblock(ssl) && dtls_timeout != 0) { #ifdef USE_WINDOWS_API - DWORD timeout = dtls_timeout; + DWORD timeout = dtls_timeout * 1000; #else struct timeval timeout = {dtls_timeout, 0}; #endif @@ -255,6 +255,128 @@ int EmbedSend(CYASSL* ssl, char *buf, int sz, void *ctx) #define XSOCKLENT socklen_t #endif +#define SENDTO_FUNCTION sendto +#define RECVFROM_FUNCTION recvfrom + + +/* The receive embedded callback + * return : nb bytes read, or error + */ +int EmbedReceiveFrom(CYASSL *ssl, char *buf, int sz, void *ctx) +{ + CYASSL_DTLS_CTX* dtlsCtx = (CYASSL_DTLS_CTX*)ctx; + int recvd; + int err; + int sd = dtlsCtx->fd; + int dtls_timeout = CyaSSL_dtls_get_current_timeout(ssl); + struct sockaddr_in peer; + XSOCKLENT peerSz = sizeof(peer); + + CYASSL_ENTER("EmbedReceiveFrom()"); + if (!CyaSSL_get_using_nonblock(ssl) && dtls_timeout != 0) { + #ifdef USE_WINDOWS_API + DWORD timeout = dtls_timeout * 1000; + #else + struct timeval timeout = {dtls_timeout, 0}; + #endif + setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, + (char*)&timeout, sizeof(timeout)); + } + + recvd = RECVFROM_FUNCTION(sd, (char *)buf, sz, 0, + (struct sockaddr*)&peer, &peerSz); + + if (recvd < 0) { + err = LastError(); + CYASSL_MSG("Embed Receive From error"); + + if (err == SOCKET_EWOULDBLOCK || err == SOCKET_EAGAIN) { + if (CyaSSL_get_using_nonblock(ssl)) { + CYASSL_MSG(" Would block"); + return IO_ERR_WANT_READ; + } + else { + CYASSL_MSG(" Socket timeout"); + return IO_ERR_TIMEOUT; + } + } + else if (err == SOCKET_ECONNRESET) { + CYASSL_MSG(" Connection reset"); + return IO_ERR_CONN_RST; + } + else if (err == SOCKET_EINTR) { + CYASSL_MSG(" Socket interrupted"); + return IO_ERR_ISR; + } + else if (err == SOCKET_ECONNREFUSED) { + CYASSL_MSG(" Connection refused"); + return IO_ERR_WANT_READ; + } + else { + CYASSL_MSG(" General error"); + return IO_ERR_GENERAL; + } + } + else { + if (dtlsCtx != NULL + && dtlsCtx->peer.sz > 0 + && peerSz != dtlsCtx->peer.sz + && memcmp(&peer, dtlsCtx->peer.sa, peerSz) != 0) { + CYASSL_MSG(" Ignored packet from invalid peer"); + return IO_ERR_WANT_READ; + } + } + + return recvd; +} + + +/* The send embedded callback + * return : nb bytes sent, or error + */ +int EmbedSendTo(CYASSL* ssl, char *buf, int sz, void *ctx) +{ + CYASSL_DTLS_CTX* dtlsCtx = (CYASSL_DTLS_CTX*)ctx; + int sd = dtlsCtx->fd; + int sent; + int len = sz; + int err; + + (void)ssl; + + CYASSL_ENTER("EmbedSendTo()"); + sent = SENDTO_FUNCTION(sd, &buf[sz - len], len, 0, + dtlsCtx->peer.sa, dtlsCtx->peer.sz); + + if (sent < 0) { + err = LastError(); + CYASSL_MSG("Embed Send To error"); + + if (err == SOCKET_EWOULDBLOCK || err == SOCKET_EAGAIN) { + CYASSL_MSG(" Would Block"); + return IO_ERR_WANT_WRITE; + } + else if (err == SOCKET_ECONNRESET) { + CYASSL_MSG(" Connection reset"); + return IO_ERR_CONN_RST; + } + else if (err == SOCKET_EINTR) { + CYASSL_MSG(" Socket interrupted"); + return IO_ERR_ISR; + } + else if (err == SOCKET_EPIPE) { + CYASSL_MSG(" Socket EPIPE"); + return IO_ERR_CONN_CLOSE; + } + else { + CYASSL_MSG(" General error"); + return IO_ERR_GENERAL; + } + } + + return sent; +} + /* The DTLS Generate Cookie callback * return : number of bytes copied into buf, or error diff --git a/src/ssl.c b/src/ssl.c index 0d7622929..e4b53df1e 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -172,6 +172,14 @@ int CyaSSL_set_fd(CYASSL* ssl, int fd) ssl->IOCB_ReadCtx = &ssl->rfd; ssl->IOCB_WriteCtx = &ssl->wfd; + #ifdef CYASSL_DTLS + if (ssl->options.dtls) { + ssl->IOCB_ReadCtx = &ssl->buffers.dtlsCtx; + ssl->IOCB_WriteCtx = &ssl->buffers.dtlsCtx; + ssl->buffers.dtlsCtx.fd = fd; + } + #endif + CYASSL_LEAVE("SSL_set_fd", SSL_SUCCESS); return SSL_SUCCESS; } @@ -206,6 +214,44 @@ int CyaSSL_dtls(CYASSL* ssl) } +int CyaSSL_dtls_set_peer(CYASSL* ssl, void* peer, unsigned int peerSz) +{ +#ifdef CYASSL_DTLS + void* sa = (void*)XMALLOC(peerSz, ssl->heap, DYNAMIC_TYPE_SOCKADDR); + if (sa != NULL) { + XMEMCPY(sa, peer, peerSz); + ssl->buffers.dtlsCtx.peer.sa = sa; + ssl->buffers.dtlsCtx.peer.sz = peerSz; + return SSL_SUCCESS; + } + return SSL_FAILURE; +#else + (void)ssl; + (void)peer; + (void)peerSz; + return SSL_NOT_IMPLEMENTED; +#endif +} + +int CyaSSL_dtls_get_peer(CYASSL* ssl, void* peer, unsigned int* peerSz) +{ +#ifdef CYASSL_DTLS + if (peer != NULL && peerSz != NULL + && *peerSz >= ssl->buffers.dtlsCtx.peer.sz) { + *peerSz = ssl->buffers.dtlsCtx.peer.sz; + XMEMCPY(peer, ssl->buffers.dtlsCtx.peer.sa, *peerSz); + return SSL_SUCCESS; + } + return SSL_FAILURE; +#else + (void)ssl; + (void)peer; + (void)peerSz; + return SSL_NOT_IMPLEMENTED; +#endif +} + + int CyaSSL_negotiate(CYASSL* ssl) { int err = SSL_FATAL_ERROR;