Add DTLS support.
[openssl.git] / crypto / bio / bss_dgram.c
1 /* crypto/bio/bio_dgram.c */
2 /* 
3  * DTLS implementation written by Nagendra Modadugu
4  * (nagendra@cs.stanford.edu) for the OpenSSL project 2005.  
5  */
6 /* ====================================================================
7  * Copyright (c) 1999-2005 The OpenSSL Project.  All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer. 
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in
18  *    the documentation and/or other materials provided with the
19  *    distribution.
20  *
21  * 3. All advertising materials mentioning features or use of this
22  *    software must display the following acknowledgment:
23  *    "This product includes software developed by the OpenSSL Project
24  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
25  *
26  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
27  *    endorse or promote products derived from this software without
28  *    prior written permission. For written permission, please contact
29  *    openssl-core@OpenSSL.org.
30  *
31  * 5. Products derived from this software may not be called "OpenSSL"
32  *    nor may "OpenSSL" appear in their names without prior written
33  *    permission of the OpenSSL Project.
34  *
35  * 6. Redistributions of any form whatsoever must retain the following
36  *    acknowledgment:
37  *    "This product includes software developed by the OpenSSL Project
38  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
39  *
40  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
41  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
44  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51  * OF THE POSSIBILITY OF SUCH DAMAGE.
52  * ====================================================================
53  *
54  * This product includes cryptographic software written by Eric Young
55  * (eay@cryptsoft.com).  This product includes software written by Tim
56  * Hudson (tjh@cryptsoft.com).
57  *
58  */
59
60 #ifndef OPENSSL_NO_DGRAM
61
62 #include <stdio.h>
63 #include <errno.h>
64 #define USE_SOCKETS
65 #include "cryptlib.h"
66
67 #include <sys/socket.h>
68
69 #include <openssl/bio.h>
70
71 #define IP_MTU      14 /* linux is lame */
72
73 #ifdef WATT32
74 #define sock_write SockWrite  /* Watt-32 uses same names */
75 #define sock_read  SockRead
76 #define sock_puts  SockPuts
77 #endif
78
79 static int dgram_write(BIO *h, const char *buf, int num);
80 static int dgram_read(BIO *h, char *buf, int size);
81 static int dgram_puts(BIO *h, const char *str);
82 static long dgram_ctrl(BIO *h, int cmd, long arg1, void *arg2);
83 static int dgram_new(BIO *h);
84 static int dgram_free(BIO *data);
85 static int dgram_clear(BIO *bio);
86
87 int BIO_dgram_should_retry(int s);
88
89 static BIO_METHOD methods_dgramp=
90         {
91         BIO_TYPE_DGRAM,
92         "datagram socket",
93         dgram_write,
94         dgram_read,
95         dgram_puts,
96         NULL, /* dgram_gets, */
97         dgram_ctrl,
98         dgram_new,
99         dgram_free,
100         NULL,
101         };
102
103 typedef struct bio_dgram_data_st
104         {
105         struct sockaddr peer;
106         unsigned int connected;
107         unsigned int _errno;
108         unsigned int mtu;
109         } bio_dgram_data;
110
111 BIO_METHOD *BIO_s_datagram(void)
112         {
113         return(&methods_dgramp);
114         }
115
116 BIO *BIO_new_dgram(int fd, int close_flag)
117         {
118         BIO *ret;
119
120         ret=BIO_new(BIO_s_datagram());
121         if (ret == NULL) return(NULL);
122         BIO_set_fd(ret,fd,close_flag);
123         return(ret);
124         }
125
126 static int dgram_new(BIO *bi)
127         {
128         bio_dgram_data *data = NULL;
129
130         bi->init=0;
131         bi->num=0;
132         data = OPENSSL_malloc(sizeof(bio_dgram_data));
133         if (data == NULL)
134                 return 0;
135         memset(data, 0x00, sizeof(bio_dgram_data));
136     bi->ptr = data;
137
138         bi->flags=0;
139         return(1);
140         }
141
142 static int dgram_free(BIO *a)
143         {
144         bio_dgram_data *data;
145
146         if (a == NULL) return(0);
147         if ( ! dgram_clear(a))
148                 return 0;
149
150         data = (bio_dgram_data *)a->ptr;
151         if(data != NULL) OPENSSL_free(data);
152
153         return(1);
154         }
155
156 static int dgram_clear(BIO *a)
157         {
158         if (a == NULL) return(0);
159         if (a->shutdown)
160                 {
161                 if (a->init)
162                         {
163                         SHUTDOWN2(a->num);
164                         }
165                 a->init=0;
166                 a->flags=0;
167                 }
168         return(1);
169         }
170         
171 static int dgram_read(BIO *b, char *out, int outl)
172         {
173         int ret=0;
174         bio_dgram_data *data = (bio_dgram_data *)b->ptr;
175
176         struct sockaddr peer;
177         socklen_t peerlen = sizeof(peer);
178
179         if (out != NULL)
180                 {
181                 clear_socket_error();
182                 memset(&peer, 0x00, peerlen);
183                 ret=recvfrom(b->num,out,outl,0,&peer,&peerlen);
184
185                 if ( ! data->connected  && ret > 0)
186                         BIO_ctrl(b, BIO_CTRL_DGRAM_CONNECT, 0, &peer);
187
188                 BIO_clear_retry_flags(b);
189                 if (ret <= 0)
190                         {
191                         if (BIO_dgram_should_retry(ret))
192                                 {
193                                 BIO_set_retry_read(b);
194                                 data->_errno = get_last_socket_error();
195                                 }
196                         }
197                 }
198         return(ret);
199         }
200
201 static int dgram_write(BIO *b, const char *in, int inl)
202         {
203         int ret;
204         bio_dgram_data *data = (bio_dgram_data *)b->ptr;
205         clear_socket_error();
206
207     if ( data->connected )
208         ret=send(b->num,in,inl,0);
209     else
210         ret=sendto(b->num, in, inl, 0, &data->peer, sizeof(data->peer));
211
212         BIO_clear_retry_flags(b);
213         if (ret <= 0)
214                 {
215                 if (BIO_sock_should_retry(ret))
216                         {
217                         BIO_set_retry_write(b);  
218                         data->_errno = get_last_socket_error();
219
220 #if 0 /* higher layers are responsible for querying MTU, if necessary */
221                         if ( data->_errno == EMSGSIZE)
222                                 /* retrieve the new MTU */
223                                 BIO_ctrl(b, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
224 #endif
225                         }
226                 }
227         return(ret);
228         }
229
230 static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
231         {
232         long ret=1;
233         int *ip;
234         struct sockaddr *to = NULL;
235         bio_dgram_data *data = NULL;
236         long sockopt_val = 0;
237         unsigned int sockopt_len = 0;
238
239         data = (bio_dgram_data *)b->ptr;
240
241         switch (cmd)
242                 {
243         case BIO_CTRL_RESET:
244                 num=0;
245         case BIO_C_FILE_SEEK:
246                 ret=0;
247                 break;
248         case BIO_C_FILE_TELL:
249         case BIO_CTRL_INFO:
250                 ret=0;
251                 break;
252         case BIO_C_SET_FD:
253                 dgram_clear(b);
254                 b->num= *((int *)ptr);
255                 b->shutdown=(int)num;
256                 b->init=1;
257                 break;
258         case BIO_C_GET_FD:
259                 if (b->init)
260                         {
261                         ip=(int *)ptr;
262                         if (ip != NULL) *ip=b->num;
263                         ret=b->num;
264                         }
265                 else
266                         ret= -1;
267                 break;
268         case BIO_CTRL_GET_CLOSE:
269                 ret=b->shutdown;
270                 break;
271         case BIO_CTRL_SET_CLOSE:
272                 b->shutdown=(int)num;
273                 break;
274         case BIO_CTRL_PENDING:
275         case BIO_CTRL_WPENDING:
276                 ret=0;
277                 break;
278         case BIO_CTRL_DUP:
279         case BIO_CTRL_FLUSH:
280                 ret=1;
281                 break;
282         case BIO_CTRL_DGRAM_CONNECT:
283                 to = (struct sockaddr *)ptr;
284 #if 0
285                 if (connect(b->num, to, sizeof(struct sockaddr)) < 0)
286                         { perror("connect"); ret = 0; }
287                 else
288                         {
289 #endif
290                         memcpy(&(data->peer),to, sizeof(struct sockaddr));
291 #if 0
292                         }
293 #endif
294                 break;
295                 /* (Linux)kernel sets DF bit on outgoing IP packets */
296 #ifdef IP_MTU_DISCOVER
297         case BIO_CTRL_DGRAM_MTU_DISCOVER:
298                 sockopt_val = IP_PMTUDISC_DO;
299                 if ((ret = setsockopt(b->num, IPPROTO_IP, IP_MTU_DISCOVER,
300                         &sockopt_val, sizeof(sockopt_val))) < 0)
301                         perror("setsockopt");
302                 break;
303 #endif
304         case BIO_CTRL_DGRAM_QUERY_MTU:
305          sockopt_len = sizeof(sockopt_val);
306                 if ((ret = getsockopt(b->num, IPPROTO_IP, IP_MTU, &sockopt_val,
307                         &sockopt_len)) < 0 || sockopt_val < 0)
308                         { ret = 0; }
309                 else
310                         {
311                         data->mtu = sockopt_val;
312                         ret = data->mtu;
313                         }
314                 break;
315         case BIO_CTRL_DGRAM_GET_MTU:
316                 return data->mtu;
317                 break;
318         case BIO_CTRL_DGRAM_SET_MTU:
319                 data->mtu = num;
320                 ret = num;
321                 break;
322         case BIO_CTRL_DGRAM_SET_CONNECTED:
323                 to = (struct sockaddr *)ptr;
324
325                 if ( to != NULL)
326                         {
327                         data->connected = 1;
328                         memcpy(&(data->peer),to, sizeof(struct sockaddr));
329                         }
330                 else
331                         {
332                         data->connected = 0;
333                         memset(&(data->peer), 0x00, sizeof(struct sockaddr));
334                         }
335                 break;
336     case BIO_CTRL_DGRAM_SET_PEER:
337         to = (struct sockaddr *) ptr;
338
339         memcpy(&(data->peer), to, sizeof(struct sockaddr));
340         break;
341         case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT:
342                 if ( setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, ptr,
343                         sizeof(struct timeval)) < 0)
344                         { perror("setsockopt"); ret = -1; }
345                 break;
346         case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT:
347                 if ( getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, 
348                         ptr, (socklen_t *)&ret) < 0)
349                         { perror("getsockopt"); ret = -1; }
350                 break;
351         case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT:
352                 if ( setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, ptr,
353                         sizeof(struct timeval)) < 0)
354                         { perror("setsockopt"); ret = -1; }
355                 break;
356         case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT:
357                 if ( getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, 
358                         ptr, (socklen_t *)&ret) < 0)
359                         { perror("getsockopt"); ret = -1; }
360                 break;
361         case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP:
362                 /* fall-through */
363         case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP:
364                 if ( data->_errno == EAGAIN)
365                         {
366                         ret = 1;
367                         data->_errno = 0;
368                         }
369                 else
370                         ret = 0;
371                 break;
372         case BIO_CTRL_DGRAM_MTU_EXCEEDED:
373                 if ( data->_errno == EMSGSIZE)
374                         {
375                         ret = 1;
376                         data->_errno = 0;
377                         }
378                 else
379                         ret = 0;
380                 break;
381         default:
382                 ret=0;
383                 break;
384                 }
385         return(ret);
386         }
387
388 static int dgram_puts(BIO *bp, const char *str)
389         {
390         int n,ret;
391
392         n=strlen(str);
393         ret=dgram_write(bp,str,n);
394         return(ret);
395         }
396
397 int BIO_dgram_should_retry(int i)
398         {
399         int err;
400
401         if ((i == 0) || (i == -1))
402                 {
403                 err=get_last_socket_error();
404
405 #if defined(OPENSSL_SYS_WINDOWS) && 0 /* more microsoft stupidity? perhaps not? Ben 4/1/99 */
406                 if ((i == -1) && (err == 0))
407                         return(1);
408 #endif
409
410                 return(BIO_dgram_non_fatal_error(err));
411                 }
412         return(0);
413         }
414
415 int BIO_dgram_non_fatal_error(int err)
416         {
417         switch (err)
418                 {
419 #if defined(OPENSSL_SYS_WINDOWS)
420 # if defined(WSAEWOULDBLOCK)
421         case WSAEWOULDBLOCK:
422 # endif
423
424 # if 0 /* This appears to always be an error */
425 #  if defined(WSAENOTCONN)
426         case WSAENOTCONN:
427 #  endif
428 # endif
429 #endif
430
431 #ifdef EWOULDBLOCK
432 # ifdef WSAEWOULDBLOCK
433 #  if WSAEWOULDBLOCK != EWOULDBLOCK
434         case EWOULDBLOCK:
435 #  endif
436 # else
437         case EWOULDBLOCK:
438 # endif
439 #endif
440
441 #if defined(ENOTCONN)
442         case ENOTCONN:
443 #endif
444
445 #ifdef EINTR
446         case EINTR:
447 #endif
448
449 #ifdef EAGAIN
450 #if EWOULDBLOCK != EAGAIN
451         case EAGAIN:
452 # endif
453 #endif
454
455 #ifdef EPROTO
456         case EPROTO:
457 #endif
458
459 #ifdef EINPROGRESS
460         case EINPROGRESS:
461 #endif
462
463 #ifdef EALREADY
464         case EALREADY:
465 #endif
466
467 /* DF bit set, and packet larger than MTU */
468 #ifdef EMSGSIZE
469         case EMSGSIZE:
470 #endif
471
472                 return(1);
473                 /* break; */
474         default:
475                 break;
476                 }
477         return(0);
478         }
479 #endif