sl@0: /* crypto/bio/bio_dgram.c */ sl@0: /* sl@0: * DTLS implementation written by Nagendra Modadugu sl@0: * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. sl@0: */ sl@0: /* ==================================================================== sl@0: * Copyright (c) 1999-2005 The OpenSSL Project. All rights reserved. sl@0: * sl@0: * Redistribution and use in source and binary forms, with or without sl@0: * modification, are permitted provided that the following conditions sl@0: * are met: sl@0: * sl@0: * 1. Redistributions of source code must retain the above copyright sl@0: * notice, this list of conditions and the following disclaimer. sl@0: * sl@0: * 2. Redistributions in binary form must reproduce the above copyright sl@0: * notice, this list of conditions and the following disclaimer in sl@0: * the documentation and/or other materials provided with the sl@0: * distribution. sl@0: * sl@0: * 3. All advertising materials mentioning features or use of this sl@0: * software must display the following acknowledgment: sl@0: * "This product includes software developed by the OpenSSL Project sl@0: * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" sl@0: * sl@0: * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to sl@0: * endorse or promote products derived from this software without sl@0: * prior written permission. For written permission, please contact sl@0: * openssl-core@OpenSSL.org. sl@0: * sl@0: * 5. Products derived from this software may not be called "OpenSSL" sl@0: * nor may "OpenSSL" appear in their names without prior written sl@0: * permission of the OpenSSL Project. sl@0: * sl@0: * 6. Redistributions of any form whatsoever must retain the following sl@0: * acknowledgment: sl@0: * "This product includes software developed by the OpenSSL Project sl@0: * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" sl@0: * sl@0: * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY sl@0: * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE sl@0: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR sl@0: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR sl@0: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, sl@0: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT sl@0: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; sl@0: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) sl@0: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, sl@0: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) sl@0: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED sl@0: * OF THE POSSIBILITY OF SUCH DAMAGE. sl@0: * ==================================================================== sl@0: * sl@0: * This product includes cryptographic software written by Eric Young sl@0: * (eay@cryptsoft.com). This product includes software written by Tim sl@0: * Hudson (tjh@cryptsoft.com). sl@0: * sl@0: */ sl@0: sl@0: /* sl@0: © Portions copyright (c) 2006 Nokia Corporation. All rights reserved. sl@0: */ sl@0: sl@0: #ifndef OPENSSL_NO_DGRAM sl@0: sl@0: #include sl@0: #include sl@0: #define USE_SOCKETS sl@0: #include "cryptlib.h" sl@0: sl@0: #include sl@0: #if (defined(SYMBIAN) && (defined(__WINSCW__) || defined(__WINS__))) sl@0: #include "libcrypto_wsd_macros.h" sl@0: #include "libcrypto_wsd.h" sl@0: #endif sl@0: sl@0: #define IP_MTU 14 /* linux is lame */ sl@0: sl@0: #ifdef WATT32 sl@0: #define sock_write SockWrite /* Watt-32 uses same names */ sl@0: #define sock_read SockRead sl@0: #define sock_puts SockPuts sl@0: #endif sl@0: sl@0: static int dgram_write(BIO *h, const char *buf, int num); sl@0: static int dgram_read(BIO *h, char *buf, int size); sl@0: static int dgram_puts(BIO *h, const char *str); sl@0: static long dgram_ctrl(BIO *h, int cmd, long arg1, void *arg2); sl@0: static int dgram_new(BIO *h); sl@0: static int dgram_free(BIO *data); sl@0: static int dgram_clear(BIO *bio); sl@0: sl@0: int BIO_dgram_should_retry(int s); sl@0: sl@0: #ifndef EMULATOR sl@0: static BIO_METHOD methods_dgramp= sl@0: { sl@0: BIO_TYPE_DGRAM, sl@0: "datagram socket", sl@0: dgram_write, sl@0: dgram_read, sl@0: dgram_puts, sl@0: NULL, /* dgram_gets, */ sl@0: dgram_ctrl, sl@0: dgram_new, sl@0: dgram_free, sl@0: NULL, sl@0: }; sl@0: #else sl@0: GET_STATIC_VAR_FROM_TLS(methods_dgramp,bss_dgram,BIO_METHOD) sl@0: #define methods_dgramp (*GET_WSD_VAR_NAME(methods_dgramp,bss_dgram,s)()) sl@0: const BIO_METHOD temp_s_methods_dgramp= sl@0: { sl@0: BIO_TYPE_DGRAM, sl@0: "datagram socket", sl@0: dgram_write, sl@0: dgram_read, sl@0: dgram_puts, sl@0: NULL, /* dgram_gets, */ sl@0: dgram_ctrl, sl@0: dgram_new, sl@0: dgram_free, sl@0: NULL, sl@0: }; sl@0: sl@0: #endif sl@0: sl@0: typedef struct bio_dgram_data_st sl@0: { sl@0: struct sockaddr peer; sl@0: unsigned int connected; sl@0: unsigned int _errno; sl@0: unsigned int mtu; sl@0: } bio_dgram_data; sl@0: sl@0: EXPORT_C BIO_METHOD *BIO_s_datagram(void) sl@0: { sl@0: return(&methods_dgramp); sl@0: } sl@0: sl@0: EXPORT_C BIO *BIO_new_dgram(int fd, int close_flag) sl@0: { sl@0: BIO *ret; sl@0: sl@0: ret=BIO_new(BIO_s_datagram()); sl@0: if (ret == NULL) return(NULL); sl@0: BIO_set_fd(ret,fd,close_flag); sl@0: return(ret); sl@0: } sl@0: sl@0: static int dgram_new(BIO *bi) sl@0: { sl@0: bio_dgram_data *data = NULL; sl@0: sl@0: bi->init=0; sl@0: bi->num=0; sl@0: data = OPENSSL_malloc(sizeof(bio_dgram_data)); sl@0: if (data == NULL) sl@0: return 0; sl@0: memset(data, 0x00, sizeof(bio_dgram_data)); sl@0: bi->ptr = data; sl@0: sl@0: bi->flags=0; sl@0: return(1); sl@0: } sl@0: sl@0: static int dgram_free(BIO *a) sl@0: { sl@0: bio_dgram_data *data; sl@0: sl@0: if (a == NULL) return(0); sl@0: if ( ! dgram_clear(a)) sl@0: return 0; sl@0: sl@0: data = (bio_dgram_data *)a->ptr; sl@0: if(data != NULL) OPENSSL_free(data); sl@0: sl@0: return(1); sl@0: } sl@0: sl@0: static int dgram_clear(BIO *a) sl@0: { sl@0: if (a == NULL) return(0); sl@0: if (a->shutdown) sl@0: { sl@0: if (a->init) sl@0: { sl@0: SHUTDOWN2(a->num); sl@0: } sl@0: a->init=0; sl@0: a->flags=0; sl@0: } sl@0: return(1); sl@0: } sl@0: sl@0: static int dgram_read(BIO *b, char *out, int outl) sl@0: { sl@0: int ret=0; sl@0: bio_dgram_data *data = (bio_dgram_data *)b->ptr; sl@0: sl@0: struct sockaddr peer; sl@0: int peerlen = sizeof(peer); sl@0: sl@0: if (out != NULL) sl@0: { sl@0: clear_socket_error(); sl@0: memset(&peer, 0x00, peerlen); sl@0: /* Last arg in recvfrom is signed on some platforms and sl@0: * unsigned on others. It is of type socklen_t on some sl@0: * but this is not universal. Cast to (void *) to avoid sl@0: * compiler warnings. sl@0: */ sl@0: ret=recvfrom(b->num,out,outl,0,&peer,(void *)&peerlen); sl@0: sl@0: if ( ! data->connected && ret > 0) sl@0: BIO_ctrl(b, BIO_CTRL_DGRAM_CONNECT, 0, &peer); sl@0: sl@0: BIO_clear_retry_flags(b); sl@0: if (ret <= 0) sl@0: { sl@0: if (BIO_dgram_should_retry(ret)) sl@0: { sl@0: BIO_set_retry_read(b); sl@0: data->_errno = get_last_socket_error(); sl@0: } sl@0: } sl@0: } sl@0: return(ret); sl@0: } sl@0: sl@0: static int dgram_write(BIO *b, const char *in, int inl) sl@0: { sl@0: int ret; sl@0: bio_dgram_data *data = (bio_dgram_data *)b->ptr; sl@0: clear_socket_error(); sl@0: sl@0: if ( data->connected ) sl@0: ret=send(b->num,in,inl,0); sl@0: else sl@0: ret=sendto(b->num, in, inl, 0, &data->peer, sizeof(data->peer)); sl@0: sl@0: BIO_clear_retry_flags(b); sl@0: if (ret <= 0) sl@0: { sl@0: if (BIO_sock_should_retry(ret)) sl@0: { sl@0: BIO_set_retry_write(b); sl@0: data->_errno = get_last_socket_error(); sl@0: sl@0: #if 0 /* higher layers are responsible for querying MTU, if necessary */ sl@0: if ( data->_errno == EMSGSIZE) sl@0: /* retrieve the new MTU */ sl@0: BIO_ctrl(b, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL); sl@0: #endif sl@0: } sl@0: } sl@0: return(ret); sl@0: } sl@0: sl@0: static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr) sl@0: { sl@0: long ret=1; sl@0: int *ip; sl@0: struct sockaddr *to = NULL; sl@0: bio_dgram_data *data = NULL; sl@0: long sockopt_val = 0; sl@0: unsigned int sockopt_len = 0; sl@0: sl@0: data = (bio_dgram_data *)b->ptr; sl@0: sl@0: switch (cmd) sl@0: { sl@0: case BIO_CTRL_RESET: sl@0: num=0; sl@0: case BIO_C_FILE_SEEK: sl@0: ret=0; sl@0: break; sl@0: case BIO_C_FILE_TELL: sl@0: case BIO_CTRL_INFO: sl@0: ret=0; sl@0: break; sl@0: case BIO_C_SET_FD: sl@0: dgram_clear(b); sl@0: b->num= *((int *)ptr); sl@0: b->shutdown=(int)num; sl@0: b->init=1; sl@0: break; sl@0: case BIO_C_GET_FD: sl@0: if (b->init) sl@0: { sl@0: ip=(int *)ptr; sl@0: if (ip != NULL) *ip=b->num; sl@0: ret=b->num; sl@0: } sl@0: else sl@0: ret= -1; sl@0: break; sl@0: case BIO_CTRL_GET_CLOSE: sl@0: ret=b->shutdown; sl@0: break; sl@0: case BIO_CTRL_SET_CLOSE: sl@0: b->shutdown=(int)num; sl@0: break; sl@0: case BIO_CTRL_PENDING: sl@0: case BIO_CTRL_WPENDING: sl@0: ret=0; sl@0: break; sl@0: case BIO_CTRL_DUP: sl@0: case BIO_CTRL_FLUSH: sl@0: ret=1; sl@0: break; sl@0: case BIO_CTRL_DGRAM_CONNECT: sl@0: to = (struct sockaddr *)ptr; sl@0: #if 0 sl@0: if (connect(b->num, to, sizeof(struct sockaddr)) < 0) sl@0: { perror("connect"); ret = 0; } sl@0: else sl@0: { sl@0: #endif sl@0: memcpy(&(data->peer),to, sizeof(struct sockaddr)); sl@0: #if 0 sl@0: } sl@0: #endif sl@0: break; sl@0: /* (Linux)kernel sets DF bit on outgoing IP packets */ sl@0: #ifdef IP_MTU_DISCOVER sl@0: case BIO_CTRL_DGRAM_MTU_DISCOVER: sl@0: sockopt_val = IP_PMTUDISC_DO; sl@0: if ((ret = setsockopt(b->num, IPPROTO_IP, IP_MTU_DISCOVER, sl@0: &sockopt_val, sizeof(sockopt_val))) < 0) sl@0: perror("setsockopt"); sl@0: break; sl@0: #endif sl@0: case BIO_CTRL_DGRAM_QUERY_MTU: sl@0: sockopt_len = sizeof(sockopt_val); sl@0: if ((ret = getsockopt(b->num, IPPROTO_IP, IP_MTU, (void *)&sockopt_val, sl@0: &sockopt_len)) < 0 || sockopt_val < 0) sl@0: { ret = 0; } sl@0: else sl@0: { sl@0: data->mtu = sockopt_val; sl@0: ret = data->mtu; sl@0: } sl@0: break; sl@0: case BIO_CTRL_DGRAM_GET_MTU: sl@0: return data->mtu; sl@0: break; sl@0: case BIO_CTRL_DGRAM_SET_MTU: sl@0: data->mtu = num; sl@0: ret = num; sl@0: break; sl@0: case BIO_CTRL_DGRAM_SET_CONNECTED: sl@0: to = (struct sockaddr *)ptr; sl@0: sl@0: if ( to != NULL) sl@0: { sl@0: data->connected = 1; sl@0: memcpy(&(data->peer),to, sizeof(struct sockaddr)); sl@0: } sl@0: else sl@0: { sl@0: data->connected = 0; sl@0: memset(&(data->peer), 0x00, sizeof(struct sockaddr)); sl@0: } sl@0: break; sl@0: case BIO_CTRL_DGRAM_SET_PEER: sl@0: to = (struct sockaddr *) ptr; sl@0: sl@0: memcpy(&(data->peer), to, sizeof(struct sockaddr)); sl@0: break; sl@0: case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT: sl@0: if ( setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, ptr, sl@0: sizeof(struct timeval)) < 0) sl@0: { perror("setsockopt"); ret = -1; } sl@0: break; sl@0: case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT: sl@0: if ( getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, sl@0: ptr, (void *)&ret) < 0) sl@0: { perror("getsockopt"); ret = -1; } sl@0: break; sl@0: case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT: sl@0: if ( setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, ptr, sl@0: sizeof(struct timeval)) < 0) sl@0: { perror("setsockopt"); ret = -1; } sl@0: break; sl@0: case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT: sl@0: if ( getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, sl@0: ptr, (void *)&ret) < 0) sl@0: { perror("getsockopt"); ret = -1; } sl@0: break; sl@0: case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP: sl@0: /* fall-through */ sl@0: case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP: sl@0: if ( data->_errno == EAGAIN) sl@0: { sl@0: ret = 1; sl@0: data->_errno = 0; sl@0: } sl@0: else sl@0: ret = 0; sl@0: break; sl@0: #ifdef EMSGSIZE sl@0: case BIO_CTRL_DGRAM_MTU_EXCEEDED: sl@0: if ( data->_errno == EMSGSIZE) sl@0: { sl@0: ret = 1; sl@0: data->_errno = 0; sl@0: } sl@0: else sl@0: ret = 0; sl@0: break; sl@0: #endif sl@0: default: sl@0: ret=0; sl@0: break; sl@0: } sl@0: return(ret); sl@0: } sl@0: sl@0: static int dgram_puts(BIO *bp, const char *str) sl@0: { sl@0: int n,ret; sl@0: sl@0: n=strlen(str); sl@0: ret=dgram_write(bp,str,n); sl@0: return(ret); sl@0: } sl@0: sl@0: int BIO_dgram_should_retry(int i) sl@0: { sl@0: int err; sl@0: sl@0: if ((i == 0) || (i == -1)) sl@0: { sl@0: err=get_last_socket_error(); sl@0: sl@0: #if defined(OPENSSL_SYS_WINDOWS) && 0 /* more microsoft stupidity? perhaps not? Ben 4/1/99 */ sl@0: if ((i == -1) && (err == 0)) sl@0: return(1); sl@0: #endif sl@0: sl@0: return(BIO_dgram_non_fatal_error(err)); sl@0: } sl@0: return(0); sl@0: } sl@0: sl@0: EXPORT_C int BIO_dgram_non_fatal_error(int err) sl@0: { sl@0: switch (err) sl@0: { sl@0: #if defined(OPENSSL_SYS_WINDOWS) sl@0: # if defined(WSAEWOULDBLOCK) sl@0: case WSAEWOULDBLOCK: sl@0: # endif sl@0: sl@0: # if 0 /* This appears to always be an error */ sl@0: # if defined(WSAENOTCONN) sl@0: case WSAENOTCONN: sl@0: # endif sl@0: # endif sl@0: #endif sl@0: sl@0: #ifdef EWOULDBLOCK sl@0: # ifdef WSAEWOULDBLOCK sl@0: # if WSAEWOULDBLOCK != EWOULDBLOCK sl@0: case EWOULDBLOCK: sl@0: # endif sl@0: # else sl@0: case EWOULDBLOCK: sl@0: # endif sl@0: #endif sl@0: sl@0: #if defined(ENOTCONN) sl@0: case ENOTCONN: sl@0: #endif sl@0: sl@0: #ifdef EINTR sl@0: case EINTR: sl@0: #endif sl@0: sl@0: #ifdef EAGAIN sl@0: #if EWOULDBLOCK != EAGAIN sl@0: case EAGAIN: sl@0: # endif sl@0: #endif sl@0: sl@0: #ifdef EPROTO sl@0: case EPROTO: sl@0: #endif sl@0: sl@0: #ifdef EINPROGRESS sl@0: case EINPROGRESS: sl@0: #endif sl@0: sl@0: #ifdef EALREADY sl@0: case EALREADY: sl@0: #endif sl@0: sl@0: /* DF bit set, and packet larger than MTU */ sl@0: #ifdef EMSGSIZE sl@0: case EMSGSIZE: sl@0: #endif sl@0: sl@0: return(1); sl@0: /* break; */ sl@0: default: sl@0: break; sl@0: } sl@0: return(0); sl@0: } sl@0: #endif