/* * 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 /* * With IPv6, it looks like Digital has mixed up the proper order of * recursive header file inclusion, resulting in the compiler complaining * that u_int isn't defined, but only if _POSIX_C_SOURCE is defined, which is * needed to have fileno() declared correctly... So let's define u_int */ #if defined(OPENSSL_SYS_VMS_DECC) && !defined(__U_INT) # define __U_INT typedef unsigned int u_int; #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 # if defined(OPENSSL_SYS_NETWARE) && defined(NETWARE_BSDSOCK) # include "netdb.h" # endif static struct hostent *GetHostByName(char *name); # if defined(OPENSSL_SYS_WINDOWS) || (defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK)) static void ssl_sock_cleanup(void); # endif static int ssl_sock_init(void); static int init_client_ip(int *sock, unsigned char ip[4], int port, int type); 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); static int host_ip(char *str, unsigned char ip[4]); # ifdef OPENSSL_SYS_WIN16 # define SOCKET_PROTOCOL 0 /* more microsoft stupidity */ # else # define SOCKET_PROTOCOL IPPROTO_TCP # endif # if defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK) static int wsa_init_done = 0; # endif # ifdef OPENSSL_SYS_WINDOWS static struct WSAData wsa_state; static int wsa_init_done = 0; # ifdef OPENSSL_SYS_WIN16 static HWND topWnd = 0; static FARPROC lpTopWndProc = NULL; static FARPROC lpTopHookProc = NULL; extern HINSTANCE _hInstance; /* nice global CRT provides */ static LONG FAR PASCAL topHookProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { if (hwnd == topWnd) { switch (message) { case WM_DESTROY: case WM_CLOSE: SetWindowLong(topWnd, GWL_WNDPROC, (LONG) lpTopWndProc); ssl_sock_cleanup(); break; } } return CallWindowProc(lpTopWndProc, hwnd, message, wParam, lParam); } static BOOL CALLBACK enumproc(HWND hwnd, LPARAM lParam) { topWnd = hwnd; return (FALSE); } # endif /* OPENSSL_SYS_WIN32 */ # endif /* OPENSSL_SYS_WINDOWS */ # ifdef OPENSSL_SYS_WINDOWS static void ssl_sock_cleanup(void) { if (wsa_init_done) { wsa_init_done = 0; # ifndef OPENSSL_SYS_WINCE WSACancelBlockingCall(); # endif WSACleanup(); } } # elif defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK) static void sock_cleanup(void) { if (wsa_init_done) { wsa_init_done = 0; WSACleanup(); } } # endif static int ssl_sock_init(void) { # ifdef WATT32 extern int _watt_do_exit; _watt_do_exit = 0; if (sock_init()) return (0); # elif defined(OPENSSL_SYS_WINDOWS) if (!wsa_init_done) { int err; # ifdef SIGINT signal(SIGINT, (void (*)(int))ssl_sock_cleanup); # endif wsa_init_done = 1; memset(&wsa_state, 0, sizeof(wsa_state)); if (WSAStartup(0x0101, &wsa_state) != 0) { err = WSAGetLastError(); BIO_printf(bio_err, "unable to start WINSOCK, error code=%d\n", err); return (0); } # ifdef OPENSSL_SYS_WIN16 EnumTaskWindows(GetCurrentTask(), enumproc, 0L); lpTopWndProc = (FARPROC) GetWindowLong(topWnd, GWL_WNDPROC); lpTopHookProc = MakeProcInstance((FARPROC) topHookProc, _hInstance); SetWindowLong(topWnd, GWL_WNDPROC, (LONG) lpTopHookProc); # endif /* OPENSSL_SYS_WIN16 */ } # elif defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK) WORD wVerReq; WSADATA wsaData; int err; if (!wsa_init_done) { # ifdef SIGINT signal(SIGINT, (void (*)(int))sock_cleanup); # endif wsa_init_done = 1; wVerReq = MAKEWORD(2, 0); err = WSAStartup(wVerReq, &wsaData); if (err != 0) { BIO_printf(bio_err, "unable to start WINSOCK2, error code=%d\n", err); return (0); } } # endif /* OPENSSL_SYS_WINDOWS */ return (1); } int init_client(int *sock, char *host, int port, int type) { unsigned char ip[4]; short p = 0; if (!host_ip(host, &(ip[0]))) { return (0); } if (p != 0) port = p; return (init_client_ip(sock, ip, port, type)); } static int init_client_ip(int *sock, unsigned char ip[4], int port, int type) { unsigned long addr; struct sockaddr_in them; int s, i; if (!ssl_sock_init()) return (0); memset((char *)&them, 0, sizeof(them)); them.sin_family = AF_INET; them.sin_port = htons((unsigned short)port); addr = (unsigned long) ((unsigned long)ip[0] << 24L) | ((unsigned long)ip[1] << 16L) | ((unsigned long)ip[2] << 8L) | ((unsigned long)ip[3]); them.sin_addr.s_addr = htonl(addr); 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 == INVALID_SOCKET) { perror("socket"); return (0); } # ifndef OPENSSL_SYS_MPE if (type == SOCK_STREAM) { i = 0; i = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&i, sizeof(i)); if (i < 0) { perror("keepalive"); return (0); } } # endif if (connect(s, (struct sockaddr *)&them, sizeof(them)) == -1) { close(s); perror("connect"); return (0); } *sock = s; return (1); } 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; 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); return (0); } } else sock = accept_socket; i = (*cb) (name, sock, context); if (name != NULL) OPENSSL_free(name); if (type == SOCK_STREAM) SHUTDOWN2(sock); if (i < 0) { SHUTDOWN2(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 == INVALID_SOCKET) 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) { # ifndef OPENSSL_SYS_WINDOWS perror("bind"); # endif 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); } 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); # ifndef OPENSSL_SYS_WINDOWS redoit: # endif 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 == INVALID_SOCKET) { # if defined(OPENSSL_SYS_WINDOWS) || (defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK)) int i; i = WSAGetLastError(); BIO_printf(bio_err, "accept error %d\n", i); # else if (errno == EINTR) { /* * check_timeout(); */ goto redoit; } fprintf(stderr, "errno=%d ", errno); perror("accept"); # endif 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 *)OPENSSL_malloc(strlen(h1->h_name) + 1)) == NULL) { perror("OPENSSL_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, short *port_ptr) { char *h, *p; h = str; p = strchr(str, ':'); if (p == NULL) { BIO_printf(bio_err, "no port defined\n"); return (0); } *(p++) = '\0'; if ((ip != NULL) && !host_ip(str, ip)) goto err; if (host_ptr != NULL) *host_ptr = h; if (!extract_port(p, port_ptr)) goto err; return (1); err: return (0); } static int host_ip(char *str, unsigned char ip[4]) { unsigned int in[4]; int i; if (sscanf(str, "%u.%u.%u.%u", &(in[0]), &(in[1]), &(in[2]), &(in[3])) == 4) { for (i = 0; i < 4; i++) if (in[i] > 255) { BIO_printf(bio_err, "invalid IP address\n"); goto err; } ip[0] = in[0]; ip[1] = in[1]; ip[2] = in[2]; ip[3] = in[3]; } else { /* do a gethostbyname */ struct hostent *he; if (!ssl_sock_init()) return (0); he = GetHostByName(str); if (he == NULL) { BIO_printf(bio_err, "gethostbyname failure\n"); goto err; } /* cast to short because of win16 winsock definition */ if ((short)he->h_addrtype != AF_INET) { BIO_printf(bio_err, "gethostbyname addr is not AF_INET\n"); return (0); } ip[0] = he->h_addr_list[0][0]; ip[1] = he->h_addr_list[0][1]; ip[2] = he->h_addr_list[0][2]; ip[3] = he->h_addr_list[0][3]; } 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) { strcpy(ghbn_cache[lowi].name, 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