/* apps/s_socket.c - socket-related functions used by s_client and s_server */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@cryptsoft.com). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@cryptsoft.com). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * "This product includes cryptographic software written by * Eric Young (eay@cryptsoft.com)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] */ #include #include #include #include #include #ifdef FLAT_INC #include "e_os2.h" #else #include "../e_os2.h" #endif #define USE_SOCKETS #define NON_MAIN #include "apps.h" #undef USE_SOCKETS #undef NON_MAIN #include "s_apps.h" #include #ifdef FLAT_INC #include "e_os.h" #else #include "../e_os.h" #endif #ifndef OPENSSL_NO_SOCK static struct hostent *GetHostByName(char *name); static int ssl_sock_init(void); static int init_server(int *sock, int port, int type); static int init_server_long(int *sock, int port, char *ip, int type); static int do_accept(int acc_sock, int *sock, char **host); #define SOCKET_PROTOCOL IPPROTO_TCP static int ssl_sock_init(void) { return (1); } int init_client(int *sock, char *host, char *port, int type, int af) { struct addrinfo hints, *ai_top, *ai; int i, s; if (!ssl_sock_init()) return (0); memset(&hints, '\0', sizeof(hints)); hints.ai_family = af; hints.ai_socktype = type; if ((i = getaddrinfo(host, port, &hints, &ai_top)) != 0) { BIO_printf(bio_err, "getaddrinfo: %s\n", gai_strerror(i)); return (0); } if (ai_top == NULL || ai_top->ai_addr == NULL) { BIO_printf(bio_err, "getaddrinfo returned no addresses\n"); if (ai_top != NULL) { freeaddrinfo(ai_top); } return (0); } for (ai = ai_top; ai != NULL; ai = ai->ai_next) { s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (s == -1) { continue; } if (type == SOCK_STREAM) { i = 0; i = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&i, sizeof(i)); if (i < 0) { perror("keepalive"); return (0); } } if ((i = connect(s, ai->ai_addr, ai->ai_addrlen)) == 0) { *sock = s; freeaddrinfo(ai_top); return (1); } close(s); } perror("connect"); close(s); freeaddrinfo(ai_top); return (0); } int do_server(int port, int type, int *ret, int (*cb)(char *hostname, int s, unsigned char *context), unsigned char *context) { int sock; char *name = NULL; int accept_socket = 0; int i; if (!init_server(&accept_socket, port, type)) return (0); if (ret != NULL) { *ret = accept_socket; /* return(1);*/ } for (;;) { if (type == SOCK_STREAM) { if (do_accept(accept_socket, &sock, &name) == 0) { shutdown(accept_socket, SHUT_RD); close(accept_socket); return (0); } } else sock = accept_socket; i = (*cb)(name, sock, context); if (name != NULL) free(name); if (type == SOCK_STREAM) { shutdown(sock, SHUT_RDWR); close(sock); } if (i < 0) { shutdown(accept_socket, SHUT_RDWR); close(accept_socket); return (i); } } } static int init_server_long(int *sock, int port, char *ip, int type) { int ret = 0; struct sockaddr_in server; int s = -1; if (!ssl_sock_init()) return (0); memset((char *)&server, 0,sizeof(server)); server.sin_family = AF_INET; server.sin_port = htons((unsigned short)port); if (ip == NULL) server.sin_addr.s_addr = INADDR_ANY; else /* Added for T3E, address-of fails on bit field (beckman@acl.lanl.gov) */ #ifndef BIT_FIELD_LIMITS memcpy(&server.sin_addr.s_addr, ip, 4); #else memcpy(&server.sin_addr, ip, 4); #endif if (type == SOCK_STREAM) s = socket(AF_INET, SOCK_STREAM, SOCKET_PROTOCOL); else /* type == SOCK_DGRAM */ s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (s == -1) goto err; #if defined SOL_SOCKET && defined SO_REUSEADDR { int j = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *) &j, sizeof j); } #endif if (bind(s, (struct sockaddr *)&server, sizeof(server)) == -1) { perror("bind"); goto err; } /* Make it 128 for linux */ if (type == SOCK_STREAM && listen(s, 128) == -1) goto err; *sock = s; ret = 1; err: if ((ret == 0) && (s != -1)) { shutdown(s, SHUT_RD); close(s); } return (ret); } static int init_server(int *sock, int port, int type) { return (init_server_long(sock, port, NULL, type)); } static int do_accept(int acc_sock, int *sock, char **host) { int ret; struct hostent *h1, *h2; static struct sockaddr_in from; int len; /* struct linger ling; */ if (!ssl_sock_init()) return (0); redoit: memset((char *)&from, 0, sizeof(from)); len = sizeof(from); /* Note: under VMS with SOCKETSHR the fourth parameter is currently * of type (int *) whereas under other systems it is (void *) if * you don't have a cast it will choke the compiler: if you do * have a cast then you can either go for (int *) or (void *). */ ret = accept(acc_sock, (struct sockaddr *)&from, (void *)&len); if (ret == -1) { if (errno == EINTR) { /*check_timeout(); */ goto redoit; } fprintf(stderr,"errno=%d ",errno); perror("accept"); return (0); } /* ling.l_onoff=1; ling.l_linger=0; i=setsockopt(ret,SOL_SOCKET,SO_LINGER,(char *)&ling,sizeof(ling)); if (i < 0) { perror("linger"); return(0); } i=0; i=setsockopt(ret,SOL_SOCKET,SO_KEEPALIVE,(char *)&i,sizeof(i)); if (i < 0) { perror("keepalive"); return(0); } */ if (host == NULL) goto end; #ifndef BIT_FIELD_LIMITS /* I should use WSAAsyncGetHostByName() under windows */ h1 = gethostbyaddr((char *)&from.sin_addr.s_addr, sizeof(from.sin_addr.s_addr), AF_INET); #else h1 = gethostbyaddr((char *)&from.sin_addr, sizeof(struct in_addr), AF_INET); #endif if (h1 == NULL) { BIO_printf(bio_err, "bad gethostbyaddr\n"); *host = NULL; /* return(0); */ } else { if ((*host = (char *)malloc(strlen(h1->h_name) + 1)) == NULL) { perror("malloc"); return (0); } BUF_strlcpy(*host, h1->h_name, strlen(h1->h_name) + 1); h2 = GetHostByName(*host); if (h2 == NULL) { BIO_printf(bio_err, "gethostbyname failure\n"); return (0); } if (h2->h_addrtype != AF_INET) { BIO_printf(bio_err, "gethostbyname addr is not AF_INET\n"); return (0); } } end: *sock = ret; return (1); } int extract_host_port(char *str, char **host_ptr, unsigned char *ip, char **port_ptr) { char *h, *p; h = str; p = strrchr(str, '/'); /* IPv6 host/port */ if (p == NULL) { p = strrchr(str, ':'); } if (p == NULL) { BIO_printf(bio_err, "no port defined\n"); return (0); } *(p++) = '\0'; if (host_ptr != NULL) *host_ptr = h; if (port_ptr != NULL && p != NULL && *p != '\0') *port_ptr = p; return (1); err: return (0); } int extract_port(char *str, short *port_ptr) { int i; struct servent *s; i = atoi(str); if (i != 0) *port_ptr = (unsigned short)i; else { s = getservbyname(str, "tcp"); if (s == NULL) { BIO_printf(bio_err, "getservbyname failure for %s\n", str); return (0); } *port_ptr = ntohs((unsigned short)s->s_port); } return (1); } #define GHBN_NUM 4 static struct ghbn_cache_st { char name[128]; struct hostent ent; unsigned long order; } ghbn_cache[GHBN_NUM]; static unsigned long ghbn_hits = 0L; static unsigned long ghbn_miss = 0L; static struct hostent * GetHostByName(char *name) { struct hostent *ret; int i, lowi = 0; unsigned long low = (unsigned long) - 1; for (i = 0; i < GHBN_NUM; i++) { if (low > ghbn_cache[i].order) { low = ghbn_cache[i].order; lowi = i; } if (ghbn_cache[i].order > 0) { if (strncmp(name, ghbn_cache[i].name, 128) == 0) break; } } if (i == GHBN_NUM) /* no hit*/ { ghbn_miss++; ret = gethostbyname(name); if (ret == NULL) return (NULL); /* else add to cache */ if (strlen(name) < sizeof ghbn_cache[0].name) { strlcpy(ghbn_cache[lowi].name, name, sizeof(ghbn_cache[0].name)); memcpy((char *)&(ghbn_cache[lowi].ent), ret, sizeof(struct hostent)); ghbn_cache[lowi].order = ghbn_miss + ghbn_hits; } return (ret); } else { ghbn_hits++; ret = &(ghbn_cache[i].ent); ghbn_cache[i].order = ghbn_miss + ghbn_hits; return (ret); } } #endif