diff options
Diffstat (limited to 'apps/s_socket.c')
-rw-r--r-- | apps/s_socket.c | 698 |
1 files changed, 169 insertions, 529 deletions
diff --git a/apps/s_socket.c b/apps/s_socket.c index 75053e9591..b816cee37d 100644 --- a/apps/s_socket.c +++ b/apps/s_socket.c @@ -121,573 +121,213 @@ typedef unsigned int u_int; #endif -#define USE_SOCKETS -#include "apps.h" -#undef USE_SOCKETS -#include "s_apps.h" -#include <openssl/ssl.h> - -#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 +# define USE_SOCKETS +# include "apps.h" +# undef USE_SOCKETS +# include "s_apps.h" -# 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, const 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(const char *str, unsigned char ip[4]); -# ifndef NO_SYS_UN_H -static int init_server_unix(int *sock, const char *path); -static int do_accept_unix(int acc_sock, int *sock); -# endif +# include <openssl/bio.h> +# include <openssl/err.h> -# 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; - -# 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) +/* + * init_client - helper routine to set up socket communication + * @sock: pointer to storage of resulting socket. + * @host: the host name or path (for AF_UNIX) to connect to. + * @port: the port to connect to (ignored for AF_UNIX). + * @family: desired socket family, may be AF_INET, AF_INET6, AF_UNIX or + * AF_UNSPEC + * @type: socket type, must be SOCK_STREAM or SOCK_DGRAM + * + * This will create a socket and use it to connect to a host:port, or if + * family == AF_UNIX, to the path found in host. + * + * If the host has more than one address, it will try them one by one until + * a successful connection is established. The resulting socket will be + * found in *sock on success, it will be given INVALID_SOCKET otherwise. + * + * Returns 1 on success, 0 on failure. + */ +int init_client(int *sock, const char *host, const char *port, + int family, int type) { - if (wsa_init_done) { - wsa_init_done = 0; - WSACleanup(); - } -} -# endif + BIO_ADDRINFO *res = NULL; + const BIO_ADDRINFO *ai = NULL; + int ret; -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; + if (!BIO_sock_init()) + return 0; -# 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); - } + ret = BIO_lookup(host, port, BIO_LOOKUP_CLIENT, family, type, &res); + if (ret == 0) { + ERR_print_errors(bio_err); + return 0; } -# 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); - } - } + ret = 0; + for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) { + int protocol = (type == SOCK_STREAM ? IPPROTO_TCP : IPPROTO_UDP); +# ifdef AF_UNIX + if (BIO_ADDRINFO_family(ai) == AF_UNIX) + protocol = 0; # endif - return (1); -} - -int init_client(int *sock, const char *host, int port, int type) -{ - unsigned char ip[4]; - - ip[0] = ip[1] = ip[2] = ip[3] = 0; - if (!host_ip(host, &(ip[0]))) - return 0; - return init_client_ip(sock, ip, port, type); -} - -static int init_client_ip(int *sock, const 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(&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, IPPROTO_TCP); - else /* ( type == SOCK_DGRAM) */ - s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - - if (s == (int)INVALID_SOCKET) { - perror("socket"); - return (0); - } -# if defined(SO_KEEPALIVE) - if (type == SOCK_STREAM) { - i = 0; - i = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&i, sizeof(i)); - if (i < 0) { - closesocket(s); - perror("keepalive"); - return (0); + /* Admitedly, these checks are quite paranoid, we should + not get anything in the BIO_ADDRINFO chain that we haven't + asked for */ + OPENSSL_assert((family == AF_UNSPEC || family == BIO_ADDRINFO_family(res)) + && (type == 0 || type == BIO_ADDRINFO_socktype(res)) + && (protocol == BIO_ADDRINFO_protocol(res))); + + *sock = BIO_socket(BIO_ADDRINFO_family(ai), BIO_ADDRINFO_socktype(ai), + BIO_ADDRINFO_protocol(ai), 0); + if (*sock == INVALID_SOCKET) { + /* Maybe the kernel doesn't support the socket family, even if + * BIO_lookup() added it in the returned result... + */ + continue; + } + if (!BIO_connect(*sock, BIO_ADDRINFO_address(ai), 0)) { + BIO_closesocket(*sock); + *sock = INVALID_SOCKET; + continue; } - } -# endif - - if (connect(s, (struct sockaddr *)&them, sizeof(them)) == -1) { - closesocket(s); - perror("connect"); - return (0); - } - *sock = s; - return (1); -} - -# ifndef NO_SYS_UN_H -int init_client_unix(int *sock, const char *server) -{ - struct sockaddr_un them; - int s; - - if (strlen(server) > (UNIX_PATH_MAX + 1)) - return (0); - if (!ssl_sock_init()) - return (0); - s = socket(AF_UNIX, SOCK_STREAM, 0); - if (s == (int)INVALID_SOCKET) { - perror("socket"); - return (0); + /* Success, don't try any more addresses */ + break; } - memset(&them, 0, sizeof(them)); - them.sun_family = AF_UNIX; - strcpy(them.sun_path, server); - - if (connect(s, (struct sockaddr *)&them, sizeof(them)) == -1) { - closesocket(s); - perror("connect"); - return (0); + if (*sock == INVALID_SOCKET) { + ERR_print_errors(bio_err); + } else { + ret = 1; } - *sock = s; - return (1); + BIO_ADDRINFO_free(res); + return ret; } -# endif -int do_server(int port, int type, int *ret, - int (*cb) (char *hostname, int s, int stype, +/* + * do_server - helper routine to perform a server operation + * @accept_sock: pointer to storage of resulting socket. + * @host: the host name or path (for AF_UNIX) to connect to. + * @port: the port to connect to (ignored for AF_UNIX). + * @family: desired socket family, may be AF_INET, AF_INET6, AF_UNIX or + * AF_UNSPEC + * @type: socket type, must be SOCK_STREAM or SOCK_DGRAM + * @cb: pointer to a function that receives the accepted socket and + * should perform the communication with the connecting client. + * @context: pointer to memory that's passed verbatim to the cb function. + * @naccept: number of times an incoming connect should be accepted. If -1, + * unlimited number. + * + * This will create a socket and use it to listen to a host:port, or if + * family == AF_UNIX, to the path found in host, then start accepting + * incoming connections and run cb on the resulting socket. + * + * 0 on failure, something other on success. + */ +int do_server(int *accept_sock, const char *host, const char *port, + int family, int type, + int (*cb) (const char *hostname, int s, int stype, unsigned char *context), unsigned char *context, int naccept) { + int asock = 0; int sock; - char *name = NULL; - int accept_socket = 0; int i; + BIO_ADDRINFO *res = NULL; + int ret = 0; + int protocol = (type == SOCK_STREAM ? IPPROTO_TCP : IPPROTO_UDP); + + if (!BIO_sock_init()) + return 0; + + if (!BIO_lookup(host, port, BIO_LOOKUP_SERVER, family, type, &res)) { + ERR_print_errors(bio_err); + return 0; + } - if (!init_server(&accept_socket, port, type)) - return (0); +# ifdef AF_UNIX + if (BIO_ADDRINFO_family(res) == AF_UNIX) + protocol = 0; +# endif + /* Admitedly, these checks are quite paranoid, we should + not get anything in the BIO_ADDRINFO chain that we haven't + asked for */ + OPENSSL_assert((family == AF_UNSPEC || family == BIO_ADDRINFO_family(res)) + && (type == 0 || type == BIO_ADDRINFO_socktype(res)) + && (protocol == BIO_ADDRINFO_protocol(res))); + + asock = BIO_socket(BIO_ADDRINFO_family(res), BIO_ADDRINFO_socktype(res), + BIO_ADDRINFO_protocol(res), 0); + if (asock == INVALID_SOCKET + || !BIO_listen(asock, BIO_ADDRINFO_address(res), BIO_SOCK_REUSEADDR)) { + BIO_ADDRINFO_free(res); + ERR_print_errors(bio_err); + if (asock != INVALID_SOCKET) + BIO_closesocket(asock); + goto end; + } - if (ret != NULL) { - *ret = accept_socket; - /* return(1); */ + BIO_ADDRINFO_free(res); + + if (accept_sock != NULL) { + *accept_sock = asock; } for (;;) { + BIO_ADDR *accepted_addr = NULL; + char *name = NULL; if (type == SOCK_STREAM) { -# ifdef OPENSSL_SSL_DEBUG_BROKEN_PROTOCOL - if (do_accept(accept_socket, &sock, NULL) == 0) -# else - if (do_accept(accept_socket, &sock, &name) == 0) -# endif - { - SHUTDOWN(accept_socket); - return (0); + if ((accepted_addr = BIO_ADDR_new()) == NULL) { + BIO_closesocket(asock); + return 0; + } + redoit: + sock = BIO_accept_ex(asock, accepted_addr, 0); + if (sock < 0) { + if (BIO_sock_should_retry(ret)) { + goto redoit; + } else { + ERR_print_errors(bio_err); + BIO_ADDR_free(accepted_addr); + SHUTDOWN(asock); + break; + } } - } else - sock = accept_socket; + } else { + sock = asock; + } + + /* accepted_addr is NULL if we're dealing with SOCK_DGRAM + * this means that for SOCK_DGRAM, name will be NULL + */ + if (accepted_addr != NULL) { +#ifdef AF_UNIX + if (family == AF_UNIX) + name = BIO_ADDR_path_string(accepted_addr); + else +#endif + name = BIO_ADDR_hostname_string(accepted_addr, 0); + } i = (*cb) (name, sock, type, context); OPENSSL_free(name); + BIO_ADDR_free(accepted_addr); if (type == SOCK_STREAM) SHUTDOWN2(sock); if (naccept != -1) naccept--; if (i < 0 || naccept == 0) { - SHUTDOWN2(accept_socket); - return (i); - } - } -} - -# ifndef NO_SYS_UN_H -int do_server_unix(const char *path, int *ret, - int (*cb) (char *hostname, int s, int stype, - unsigned char *context), unsigned char *context, - int naccept) -{ - int sock; - int accept_socket = 0; - int i; - - if (!init_server_unix(&accept_socket, path)) - return (0); - - if (ret != NULL) - *ret = accept_socket; - for (;;) { - if (do_accept_unix(accept_socket, &sock) == 0) { - SHUTDOWN(accept_socket); - i = 0; - goto out; - } - i = (*cb) (NULL, sock, 0, context); - SHUTDOWN2(sock); - if (naccept != -1) - naccept--; - if (i < 0 || naccept == 0) { - SHUTDOWN2(accept_socket); - goto out; - } - } - out: - unlink(path); - return (i); -} -# endif - -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(&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, IPPROTO_TCP); - else /* type == SOCK_DGRAM */ - s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - - if (s == (int)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)); -} - -# ifndef NO_SYS_UN_H -static int init_server_unix(int *sock, const char *path) -{ - int ret = 0; - struct sockaddr_un server; - int s = -1; - - if (strlen(path) > (UNIX_PATH_MAX + 1)) - return (0); - if (!ssl_sock_init()) - return (0); - - s = socket(AF_UNIX, SOCK_STREAM, 0); - if (s == (int)INVALID_SOCKET) - goto err; - - memset(&server, 0, sizeof(server)); - server.sun_family = AF_UNIX; - strcpy(server.sun_path, path); - - if (bind(s, (struct sockaddr *)&server, sizeof(server)) == -1) { -# ifndef OPENSSL_SYS_WINDOWS - perror("bind"); -# endif - goto err; - } - /* Make it 128 for linux */ - if (listen(s, 128) == -1) { -# ifndef OPENSSL_SYS_WINDOWS - perror("listen"); -# endif - unlink(path); - goto err; - } - *sock = s; - ret = 1; - err: - if ((ret == 0) && (s != -1)) { - SHUTDOWN(s); - } - return (ret); -} -# endif - -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(&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 == (int)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; - } - BIO_printf(bio_err, "accept errno=%d, %s\n", errno, strerror(errno)); -# endif - 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 { - *host = app_malloc(strlen(h1->h_name) + 1, "copy hostname"); - OPENSSL_strlcpy(*host, h1->h_name, strlen(h1->h_name) + 1); - - h2 = gethostbyname(*host); - if (h2 == NULL) { - BIO_printf(bio_err, "gethostbyname failure\n"); - closesocket(ret); - return (0); - } - if (h2->h_addrtype != AF_INET) { - BIO_printf(bio_err, "gethostbyname addr is not AF_INET\n"); - closesocket(ret); - return (0); + SHUTDOWN2(asock); + ret = i; + break; } } end: - *sock = ret; - return (1); -} - -# ifndef NO_SYS_UN_H -static int do_accept_unix(int acc_sock, int *sock) -{ - int ret; - - if (!ssl_sock_init()) - return (0); - - redoit: - ret = accept(acc_sock, NULL, NULL); - if (ret == (int)INVALID_SOCKET) { - if (errno == EINTR) { - /* - * check_timeout(); - */ - goto redoit; - } - BIO_printf(bio_err, "accept errno=%d, %s\n", errno, strerror(errno)); - return (0); - } - - *sock = ret; - return (1); -} +# ifdef AF_UNIX + if (family == AF_UNIX) + unlink(host); # endif - -int extract_host_port(char *str, char **host_ptr, unsigned char *ip, - unsigned 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(const 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; - } - if (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); + return ret; } -int extract_port(const char *str, unsigned 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); -} - -#endif +#endif /* OPENSSL_NO_SOCK */ |