bss_acpt.c revision 296465
1130803Smarcel/* crypto/bio/bss_acpt.c */
2130803Smarcel/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
3130803Smarcel * All rights reserved.
4130803Smarcel *
5130803Smarcel * This package is an SSL implementation written
6130803Smarcel * by Eric Young (eay@cryptsoft.com).
7130803Smarcel * The implementation was written so as to conform with Netscapes SSL.
8130803Smarcel *
9130803Smarcel * This library is free for commercial and non-commercial use as long as
10130803Smarcel * the following conditions are aheared to.  The following conditions
11130803Smarcel * apply to all code found in this distribution, be it the RC4, RSA,
12130803Smarcel * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
13130803Smarcel * included with this distribution is covered by the same copyright terms
14130803Smarcel * except that the holder is Tim Hudson (tjh@cryptsoft.com).
15130803Smarcel *
16130803Smarcel * Copyright remains Eric Young's, and as such any Copyright notices in
17130803Smarcel * the code are not to be removed.
18130803Smarcel * If this package is used in a product, Eric Young should be given attribution
19130803Smarcel * as the author of the parts of the library used.
20130803Smarcel * This can be in the form of a textual message at program startup or
21130803Smarcel * in documentation (online or textual) provided with the package.
22130803Smarcel *
23130803Smarcel * Redistribution and use in source and binary forms, with or without
24130803Smarcel * modification, are permitted provided that the following conditions
25130803Smarcel * are met:
26130803Smarcel * 1. Redistributions of source code must retain the copyright
27130803Smarcel *    notice, this list of conditions and the following disclaimer.
28130803Smarcel * 2. Redistributions in binary form must reproduce the above copyright
29130803Smarcel *    notice, this list of conditions and the following disclaimer in the
30130803Smarcel *    documentation and/or other materials provided with the distribution.
31130803Smarcel * 3. All advertising materials mentioning features or use of this software
32130803Smarcel *    must display the following acknowledgement:
33130803Smarcel *    "This product includes cryptographic software written by
34 *     Eric Young (eay@cryptsoft.com)"
35 *    The word 'cryptographic' can be left out if the rouines from the library
36 *    being used are not cryptographic related :-).
37 * 4. If you include any Windows specific code (or a derivative thereof) from
38 *    the apps directory (application code) you must include an acknowledgement:
39 *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
40 *
41 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * SUCH DAMAGE.
52 *
53 * The licence and distribution terms for any publically available version or
54 * derivative of this code cannot be changed.  i.e. this code cannot simply be
55 * copied and put under another distribution licence
56 * [including the GNU Public Licence.]
57 */
58
59#include <stdio.h>
60#include <errno.h>
61#define USE_SOCKETS
62#include "cryptlib.h"
63#include <openssl/bio.h>
64
65#ifndef OPENSSL_NO_SOCK
66
67# ifdef OPENSSL_SYS_WIN16
68#  define SOCKET_PROTOCOL 0     /* more microsoft stupidity */
69# else
70#  define SOCKET_PROTOCOL IPPROTO_TCP
71# endif
72
73# if (defined(OPENSSL_SYS_VMS) && __VMS_VER < 70000000)
74/* FIONBIO used as a switch to enable ioctl, and that isn't in VMS < 7.0 */
75#  undef FIONBIO
76# endif
77
78typedef struct bio_accept_st {
79    int state;
80    char *param_addr;
81    int accept_sock;
82    int accept_nbio;
83    char *addr;
84    int nbio;
85    /*
86     * If 0, it means normal, if 1, do a connect on bind failure, and if
87     * there is no-one listening, bind with SO_REUSEADDR. If 2, always use
88     * SO_REUSEADDR.
89     */
90    int bind_mode;
91    BIO *bio_chain;
92} BIO_ACCEPT;
93
94static int acpt_write(BIO *h, const char *buf, int num);
95static int acpt_read(BIO *h, char *buf, int size);
96static int acpt_puts(BIO *h, const char *str);
97static long acpt_ctrl(BIO *h, int cmd, long arg1, void *arg2);
98static int acpt_new(BIO *h);
99static int acpt_free(BIO *data);
100static int acpt_state(BIO *b, BIO_ACCEPT *c);
101static void acpt_close_socket(BIO *data);
102BIO_ACCEPT *BIO_ACCEPT_new(void);
103void BIO_ACCEPT_free(BIO_ACCEPT *a);
104
105# define ACPT_S_BEFORE                   1
106# define ACPT_S_GET_ACCEPT_SOCKET        2
107# define ACPT_S_OK                       3
108
109static BIO_METHOD methods_acceptp = {
110    BIO_TYPE_ACCEPT,
111    "socket accept",
112    acpt_write,
113    acpt_read,
114    acpt_puts,
115    NULL,                       /* connect_gets, */
116    acpt_ctrl,
117    acpt_new,
118    acpt_free,
119    NULL,
120};
121
122BIO_METHOD *BIO_s_accept(void)
123{
124    return (&methods_acceptp);
125}
126
127static int acpt_new(BIO *bi)
128{
129    BIO_ACCEPT *ba;
130
131    bi->init = 0;
132    bi->num = INVALID_SOCKET;
133    bi->flags = 0;
134    if ((ba = BIO_ACCEPT_new()) == NULL)
135        return (0);
136    bi->ptr = (char *)ba;
137    ba->state = ACPT_S_BEFORE;
138    bi->shutdown = 1;
139    return (1);
140}
141
142BIO_ACCEPT *BIO_ACCEPT_new(void)
143{
144    BIO_ACCEPT *ret;
145
146    if ((ret = (BIO_ACCEPT *)OPENSSL_malloc(sizeof(BIO_ACCEPT))) == NULL)
147        return (NULL);
148
149    memset(ret, 0, sizeof(BIO_ACCEPT));
150    ret->accept_sock = INVALID_SOCKET;
151    ret->bind_mode = BIO_BIND_NORMAL;
152    return (ret);
153}
154
155void BIO_ACCEPT_free(BIO_ACCEPT *a)
156{
157    if (a == NULL)
158        return;
159
160    if (a->param_addr != NULL)
161        OPENSSL_free(a->param_addr);
162    if (a->addr != NULL)
163        OPENSSL_free(a->addr);
164    if (a->bio_chain != NULL)
165        BIO_free(a->bio_chain);
166    OPENSSL_free(a);
167}
168
169static void acpt_close_socket(BIO *bio)
170{
171    BIO_ACCEPT *c;
172
173    c = (BIO_ACCEPT *)bio->ptr;
174    if (c->accept_sock != INVALID_SOCKET) {
175        shutdown(c->accept_sock, 2);
176        closesocket(c->accept_sock);
177        c->accept_sock = INVALID_SOCKET;
178        bio->num = INVALID_SOCKET;
179    }
180}
181
182static int acpt_free(BIO *a)
183{
184    BIO_ACCEPT *data;
185
186    if (a == NULL)
187        return (0);
188    data = (BIO_ACCEPT *)a->ptr;
189
190    if (a->shutdown) {
191        acpt_close_socket(a);
192        BIO_ACCEPT_free(data);
193        a->ptr = NULL;
194        a->flags = 0;
195        a->init = 0;
196    }
197    return (1);
198}
199
200static int acpt_state(BIO *b, BIO_ACCEPT *c)
201{
202    BIO *bio = NULL, *dbio;
203    int s = -1;
204    int i;
205
206 again:
207    switch (c->state) {
208    case ACPT_S_BEFORE:
209        if (c->param_addr == NULL) {
210            BIOerr(BIO_F_ACPT_STATE, BIO_R_NO_ACCEPT_PORT_SPECIFIED);
211            return (-1);
212        }
213        s = BIO_get_accept_socket(c->param_addr, c->bind_mode);
214        if (s == INVALID_SOCKET)
215            return (-1);
216
217        if (c->accept_nbio) {
218            if (!BIO_socket_nbio(s, 1)) {
219                closesocket(s);
220                BIOerr(BIO_F_ACPT_STATE,
221                       BIO_R_ERROR_SETTING_NBIO_ON_ACCEPT_SOCKET);
222                return (-1);
223            }
224        }
225        c->accept_sock = s;
226        b->num = s;
227        c->state = ACPT_S_GET_ACCEPT_SOCKET;
228        return (1);
229        /* break; */
230    case ACPT_S_GET_ACCEPT_SOCKET:
231        if (b->next_bio != NULL) {
232            c->state = ACPT_S_OK;
233            goto again;
234        }
235        BIO_clear_retry_flags(b);
236        b->retry_reason = 0;
237        i = BIO_accept(c->accept_sock, &(c->addr));
238
239        /* -2 return means we should retry */
240        if (i == -2) {
241            BIO_set_retry_special(b);
242            b->retry_reason = BIO_RR_ACCEPT;
243            return -1;
244        }
245
246        if (i < 0)
247            return (i);
248
249        bio = BIO_new_socket(i, BIO_CLOSE);
250        if (bio == NULL)
251            goto err;
252
253        BIO_set_callback(bio, BIO_get_callback(b));
254        BIO_set_callback_arg(bio, BIO_get_callback_arg(b));
255
256        if (c->nbio) {
257            if (!BIO_socket_nbio(i, 1)) {
258                BIOerr(BIO_F_ACPT_STATE,
259                       BIO_R_ERROR_SETTING_NBIO_ON_ACCEPTED_SOCKET);
260                goto err;
261            }
262        }
263
264        /*
265         * If the accept BIO has an bio_chain, we dup it and put the new
266         * socket at the end.
267         */
268        if (c->bio_chain != NULL) {
269            if ((dbio = BIO_dup_chain(c->bio_chain)) == NULL)
270                goto err;
271            if (!BIO_push(dbio, bio))
272                goto err;
273            bio = dbio;
274        }
275        if (BIO_push(b, bio) == NULL)
276            goto err;
277
278        c->state = ACPT_S_OK;
279        return (1);
280 err:
281        if (bio != NULL)
282            BIO_free(bio);
283        else if (s >= 0)
284            closesocket(s);
285        return (0);
286        /* break; */
287    case ACPT_S_OK:
288        if (b->next_bio == NULL) {
289            c->state = ACPT_S_GET_ACCEPT_SOCKET;
290            goto again;
291        }
292        return (1);
293        /* break; */
294    default:
295        return (0);
296        /* break; */
297    }
298
299}
300
301static int acpt_read(BIO *b, char *out, int outl)
302{
303    int ret = 0;
304    BIO_ACCEPT *data;
305
306    BIO_clear_retry_flags(b);
307    data = (BIO_ACCEPT *)b->ptr;
308
309    while (b->next_bio == NULL) {
310        ret = acpt_state(b, data);
311        if (ret <= 0)
312            return (ret);
313    }
314
315    ret = BIO_read(b->next_bio, out, outl);
316    BIO_copy_next_retry(b);
317    return (ret);
318}
319
320static int acpt_write(BIO *b, const char *in, int inl)
321{
322    int ret;
323    BIO_ACCEPT *data;
324
325    BIO_clear_retry_flags(b);
326    data = (BIO_ACCEPT *)b->ptr;
327
328    while (b->next_bio == NULL) {
329        ret = acpt_state(b, data);
330        if (ret <= 0)
331            return (ret);
332    }
333
334    ret = BIO_write(b->next_bio, in, inl);
335    BIO_copy_next_retry(b);
336    return (ret);
337}
338
339static long acpt_ctrl(BIO *b, int cmd, long num, void *ptr)
340{
341    int *ip;
342    long ret = 1;
343    BIO_ACCEPT *data;
344    char **pp;
345
346    data = (BIO_ACCEPT *)b->ptr;
347
348    switch (cmd) {
349    case BIO_CTRL_RESET:
350        ret = 0;
351        data->state = ACPT_S_BEFORE;
352        acpt_close_socket(b);
353        b->flags = 0;
354        break;
355    case BIO_C_DO_STATE_MACHINE:
356        /* use this one to start the connection */
357        ret = (long)acpt_state(b, data);
358        break;
359    case BIO_C_SET_ACCEPT:
360        if (ptr != NULL) {
361            if (num == 0) {
362                b->init = 1;
363                if (data->param_addr != NULL)
364                    OPENSSL_free(data->param_addr);
365                data->param_addr = BUF_strdup(ptr);
366            } else if (num == 1) {
367                data->accept_nbio = (ptr != NULL);
368            } else if (num == 2) {
369                if (data->bio_chain != NULL)
370                    BIO_free(data->bio_chain);
371                data->bio_chain = (BIO *)ptr;
372            }
373        }
374        break;
375    case BIO_C_SET_NBIO:
376        data->nbio = (int)num;
377        break;
378    case BIO_C_SET_FD:
379        b->init = 1;
380        b->num = *((int *)ptr);
381        data->accept_sock = b->num;
382        data->state = ACPT_S_GET_ACCEPT_SOCKET;
383        b->shutdown = (int)num;
384        b->init = 1;
385        break;
386    case BIO_C_GET_FD:
387        if (b->init) {
388            ip = (int *)ptr;
389            if (ip != NULL)
390                *ip = data->accept_sock;
391            ret = data->accept_sock;
392        } else
393            ret = -1;
394        break;
395    case BIO_C_GET_ACCEPT:
396        if (b->init) {
397            if (ptr != NULL) {
398                pp = (char **)ptr;
399                *pp = data->param_addr;
400            } else
401                ret = -1;
402        } else
403            ret = -1;
404        break;
405    case BIO_CTRL_GET_CLOSE:
406        ret = b->shutdown;
407        break;
408    case BIO_CTRL_SET_CLOSE:
409        b->shutdown = (int)num;
410        break;
411    case BIO_CTRL_PENDING:
412    case BIO_CTRL_WPENDING:
413        ret = 0;
414        break;
415    case BIO_CTRL_FLUSH:
416        break;
417    case BIO_C_SET_BIND_MODE:
418        data->bind_mode = (int)num;
419        break;
420    case BIO_C_GET_BIND_MODE:
421        ret = (long)data->bind_mode;
422        break;
423    case BIO_CTRL_DUP:
424/*-     dbio=(BIO *)ptr;
425        if (data->param_port) EAY EAY
426                BIO_set_port(dbio,data->param_port);
427        if (data->param_hostname)
428                BIO_set_hostname(dbio,data->param_hostname);
429        BIO_set_nbio(dbio,data->nbio); */
430        break;
431
432    default:
433        ret = 0;
434        break;
435    }
436    return (ret);
437}
438
439static int acpt_puts(BIO *bp, const char *str)
440{
441    int n, ret;
442
443    n = strlen(str);
444    ret = acpt_write(bp, str, n);
445    return (ret);
446}
447
448BIO *BIO_new_accept(char *str)
449{
450    BIO *ret;
451
452    ret = BIO_new(BIO_s_accept());
453    if (ret == NULL)
454        return (NULL);
455    if (BIO_set_accept_port(ret, str))
456        return (ret);
457    else {
458        BIO_free(ret);
459        return (NULL);
460    }
461}
462
463#endif
464