X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=blobdiff_plain;f=crypto%2Fbio%2Fbss_dgram.c;h=cddabe1aafbf547f44859e5e097e612f248868f5;hp=286972217f2a28d0f8a1e943ce530bce85a32a73;hb=2335e8a9ccdaf62eabc37c8b9b80195285ed887d;hpb=eef0c1f34c7b7d6a38e02f3294eb509aef081749 diff --git a/crypto/bio/bss_dgram.c b/crypto/bio/bss_dgram.c index 286972217f..cddabe1aaf 100644 --- a/crypto/bio/bss_dgram.c +++ b/crypto/bio/bss_dgram.c @@ -66,7 +66,13 @@ #include +#if defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_VMS) +#include +#endif + +#ifdef OPENSSL_SYS_LINUX #define IP_MTU 14 /* linux is lame */ +#endif #ifdef WATT32 #define sock_write SockWrite /* Watt-32 uses same names */ @@ -84,6 +90,8 @@ static int dgram_clear(BIO *bio); static int BIO_dgram_should_retry(int s); +static void get_current_time(struct timeval *t); + static BIO_METHOD methods_dgramp= { BIO_TYPE_DGRAM, @@ -100,10 +108,16 @@ static BIO_METHOD methods_dgramp= typedef struct bio_dgram_data_st { - struct sockaddr peer; +#if OPENSSL_USE_IPV6 + struct sockaddr_storage peer; +#else + struct sockaddr_in peer; +#endif 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,13 +179,110 @@ static int dgram_clear(BIO *a) } 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 + 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 */ + get_current_time(&timenow); + + /* 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; + } + + if (timeleft.tv_sec < 0) + { + timeleft.tv_sec = 0; + timeleft.tv_usec = 1; + } + + /* Adjust socket timeout if next handhake message timer + * will expire earlier. + */ + if ((data->socket_timeout.tv_sec == 0 && data->socket_timeout.tv_usec == 0) || + (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; + + /* Is a timer active? */ + if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) + { +#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; bio_dgram_data *data = (bio_dgram_data *)b->ptr; - struct sockaddr peer; +#if OPENSSL_USE_IPV6 + struct sockaddr_storage peer; +#else + struct sockaddr_in peer; +#endif int peerlen = sizeof(peer); if (out != NULL) @@ -183,13 +294,15 @@ static int dgram_read(BIO *b, char *out, int outl) * but this is not universal. Cast to (void *) to avoid * compiler warnings. */ - ret=recvfrom(b->num,out,outl,0,&peer,(void *)&peerlen); + dgram_adjust_rcv_timeout(b); + ret=recvfrom(b->num,out,outl,0,(struct sockaddr *)&peer,(void *)&peerlen); + dgram_reset_rcv_timeout(b); - if ( ! data->connected && ret > 0) - BIO_ctrl(b, BIO_CTRL_DGRAM_CONNECT, 0, &peer); + if ( ! data->connected && ret >= 0) + BIO_ctrl(b, BIO_CTRL_DGRAM_SET_PEER, 0, &peer); BIO_clear_retry_flags(b); - if (ret <= 0) + if (ret < 0) { if (BIO_dgram_should_retry(ret)) { @@ -207,19 +320,34 @@ static int dgram_write(BIO *b, const char *in, int inl) bio_dgram_data *data = (bio_dgram_data *)b->ptr; clear_socket_error(); - if ( data->connected ) - ret=writesocket(b->num,in,inl); - else + if ( data->connected ) + ret=writesocket(b->num,in,inl); + else +#if OPENSSL_USE_IPV6 + if (data->peer.ss_family == AF_INET) #if defined(NETWARE_CLIB) && defined(NETWARE_BSDSOCK) - ret=sendto(b->num, (char *)in, inl, 0, &data->peer, sizeof(data->peer)); + ret=sendto(b->num, (char *)in, inl, 0, (const struct sockaddr *)&data->peer, sizeof(struct sockaddr_in)); #else - ret=sendto(b->num, in, inl, 0, &data->peer, sizeof(data->peer)); + ret=sendto(b->num, in, inl, 0, (const struct sockaddr *)&data->peer, sizeof(struct sockaddr_in)); +#endif + else +#if defined(NETWARE_CLIB) && defined(NETWARE_BSDSOCK) + ret=sendto(b->num, (char *)in, inl, 0, (const struct sockaddr *)&data->peer, sizeof(struct sockaddr_in6)); +#else + ret=sendto(b->num, in, inl, 0, (const struct sockaddr *)&data->peer, sizeof(struct sockaddr_in6)); +#endif +#else +#if defined(NETWARE_CLIB) && defined(NETWARE_BSDSOCK) + ret=sendto(b->num, (char *)in, inl, 0, (const struct sockaddr *)&data->peer, sizeof(struct sockaddr_in)); +#else + ret=sendto(b->num, in, inl, 0, (const struct sockaddr *)&data->peer, sizeof(struct sockaddr_in)); +#endif #endif BIO_clear_retry_flags(b); if (ret <= 0) { - if (BIO_sock_should_retry(ret)) + if (BIO_dgram_should_retry(ret)) { BIO_set_retry_write(b); data->_errno = get_last_socket_error(); @@ -240,8 +368,14 @@ static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr) int *ip; struct sockaddr *to = NULL; bio_dgram_data *data = NULL; +#if defined(IP_MTU_DISCOVER) || defined(IP_MTU) long sockopt_val = 0; unsigned int sockopt_len = 0; +#endif +#ifdef OPENSSL_SYS_LINUX + socklen_t addr_len; + struct sockaddr_storage addr; +#endif data = (bio_dgram_data *)b->ptr; @@ -294,30 +428,101 @@ static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr) else { #endif - memcpy(&(data->peer),to, sizeof(struct sockaddr)); +#if OPENSSL_USE_IPV6 + memcpy(&(data->peer),to, sizeof(struct sockaddr_storage)); +#else + memcpy(&(data->peer),to, sizeof(struct sockaddr_in)); +#endif #if 0 } #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; +#if OPENSSL_USE_IPV6 + 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; +#endif + 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) { - data->mtu = sockopt_val; - ret = data->mtu; + ret = 0; + break; } + sockopt_len = sizeof(sockopt_val); + switch (addr.ss_family) + { + 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; +#if OPENSSL_USE_IPV6 + 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; +#endif + default: + ret = 0; + break; + } +#else + ret = 0; +#endif break; case BIO_CTRL_DGRAM_GET_MTU: return data->mtu; @@ -332,47 +537,129 @@ static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr) if ( to != NULL) { data->connected = 1; - memcpy(&(data->peer),to, sizeof(struct sockaddr)); +#if OPENSSL_USE_IPV6 + memcpy(&(data->peer),to, sizeof(struct sockaddr_storage)); +#else + memcpy(&(data->peer),to, sizeof(struct sockaddr_in)); +#endif } else { data->connected = 0; - memset(&(data->peer), 0x00, sizeof(struct sockaddr)); +#if OPENSSL_USE_IPV6 + memset(&(data->peer), 0x00, sizeof(struct sockaddr_storage)); +#else + memset(&(data->peer), 0x00, sizeof(struct sockaddr_in)); +#endif } break; - case BIO_CTRL_DGRAM_SET_PEER: - to = (struct sockaddr *) ptr; + case BIO_CTRL_DGRAM_GET_PEER: + to = (struct sockaddr *) ptr; + +#if OPENSSL_USE_IPV6 + memcpy(to, &(data->peer), sizeof(struct sockaddr_storage)); + ret = sizeof(struct sockaddr_storage); +#else + memcpy(to, &(data->peer), sizeof(struct sockaddr_in)); + ret = sizeof(struct sockaddr_in); +#endif + break; + case BIO_CTRL_DGRAM_SET_PEER: + to = (struct sockaddr *) ptr; - memcpy(&(data->peer), to, sizeof(struct sockaddr)); - break; +#if OPENSSL_USE_IPV6 + memcpy(&(data->peer), to, sizeof(struct sockaddr_storage)); +#else + memcpy(&(data->peer), to, sizeof(struct sockaddr_in)); +#endif + 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 + { + struct timeval *tv = (struct timeval *)ptr; + int timeout = tv->tv_sec * 1000 + tv->tv_usec/1000; + if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, + (void*)&timeout, sizeof(timeout)) < 0) + { perror("setsockopt"); ret = -1; } + } +#else if ( setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, ptr, sizeof(struct timeval)) < 0) { perror("setsockopt"); ret = -1; } +#endif break; case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT: +#ifdef OPENSSL_SYS_WINDOWS + { + int timeout, sz = sizeof(timeout); + struct timeval *tv = (struct timeval *)ptr; + if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, + (void*)&timeout, &sz) < 0) + { perror("getsockopt"); ret = -1; } + else + { + tv->tv_sec = timeout / 1000; + tv->tv_usec = (timeout % 1000) * 1000; + ret = sizeof(*tv); + } + } +#else if ( getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, ptr, (void *)&ret) < 0) { perror("getsockopt"); ret = -1; } +#endif break; #endif #if defined(SO_SNDTIMEO) case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT: +#ifdef OPENSSL_SYS_WINDOWS + { + struct timeval *tv = (struct timeval *)ptr; + int timeout = tv->tv_sec * 1000 + tv->tv_usec/1000; + if (setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, + (void*)&timeout, sizeof(timeout)) < 0) + { perror("setsockopt"); ret = -1; } + } +#else if ( setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, ptr, sizeof(struct timeval)) < 0) { perror("setsockopt"); ret = -1; } +#endif break; case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT: +#ifdef OPENSSL_SYS_WINDOWS + { + int timeout, sz = sizeof(timeout); + struct timeval *tv = (struct timeval *)ptr; + if (getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, + (void*)&timeout, &sz) < 0) + { perror("getsockopt"); ret = -1; } + else + { + tv->tv_sec = timeout / 1000; + tv->tv_usec = (timeout % 1000) * 1000; + ret = sizeof(*tv); + } + } +#else if ( getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, ptr, (void *)&ret) < 0) { perror("getsockopt"); ret = -1; } +#endif break; #endif case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP: /* fall-through */ case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP: +#ifdef OPENSSL_SYS_WINDOWS + if ( data->_errno == WSAETIMEDOUT) +#else if ( data->_errno == EAGAIN) +#endif { ret = 1; data->_errno = 0; @@ -451,10 +738,6 @@ int BIO_dgram_non_fatal_error(int err) # endif #endif -#if defined(ENOTCONN) - case ENOTCONN: -#endif - #ifdef EINTR case EINTR: #endif @@ -477,11 +760,6 @@ int BIO_dgram_non_fatal_error(int err) case EALREADY: #endif -/* DF bit set, and packet larger than MTU */ -#ifdef EMSGSIZE - case EMSGSIZE: -#endif - return(1); /* break; */ default: @@ -490,3 +768,20 @@ int BIO_dgram_non_fatal_error(int err) return(0); } #endif + +static void get_current_time(struct timeval *t) + { +#ifdef OPENSSL_SYS_WIN32 + struct _timeb tb; + _ftime(&tb); + t->tv_sec = (long)tb.time; + t->tv_usec = (long)tb.millitm * 1000; +#elif defined(OPENSSL_SYS_VMS) + struct timeb tb; + ftime(&tb); + t->tv_sec = (long)tb.time; + t->tv_usec = (long)tb.millitm * 1000; +#else + gettimeofday(t, NULL); +#endif + }