From ab69ac00f3c7a04151662813794ac82bc591a89b Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Wed, 3 Feb 2016 00:47:42 +0100 Subject: [PATCH] Refactoring BIO: Adapt s_client and s_server s_socket.c gets brutally cleaned out and now consists of only two functions, one for client and the other for server. They both handle AF_INET, AF_INET6 and additionally AF_UNIX where supported. The rest is just easy adaptation. Both s_client and s_server get the new flags -4 and -6 to force the use of IPv4 or IPv6 only. Also, the default host "localhost" in s_client is removed. It's not certain that this host is set up for both IPv4 and IPv6. For example, Debian has "ip6-localhost" as the default hostname for [::1]. The better way is to default |host| to NULL and rely on BIO_lookup() to return a BIO_ADDRINFO with the appropriate loopback address for IPv4 or IPv6 as indicated by the |family| parameter. Reviewed-by: Kurt Roeckx --- apps/s_apps.h | 23 +- apps/s_client.c | 117 ++++++-- apps/s_server.c | 131 ++++++--- apps/s_socket.c | 698 ++++++++++++------------------------------------ 4 files changed, 364 insertions(+), 605 deletions(-) diff --git a/apps/s_apps.h b/apps/s_apps.h index 9339b41609..e9b6f40288 100644 --- a/apps/s_apps.h +++ b/apps/s_apps.h @@ -146,20 +146,14 @@ typedef fd_mask fd_set; # define FD_ZERO(p) memset((p), 0, sizeof(*(p))) #endif -#define PORT 4433 -#define PORT_STR "4433" +#define PORT "4433" #define PROTOCOL "tcp" -int do_server(int port, int type, int *ret, - int (*cb) (char *hostname, int s, int stype, +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); -#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); -#endif #ifdef HEADER_X509_H int verify_callback(int ok, X509_STORE_CTX *ctx); #endif @@ -172,14 +166,9 @@ int ssl_print_point_formats(BIO *out, SSL *s); int ssl_print_curves(BIO *out, SSL *s, int noshared); #endif int ssl_print_tmp_key(BIO *out, SSL *s); -int init_client(int *sock, const char *server, int port, int type); -#ifndef NO_SYS_UN_H -int init_client_unix(int *sock, const char *server); -#endif +int init_client(int *sock, const char *host, const char *port, + int family, int type); int should_retry(int i); -int extract_port(const char *str, unsigned short *port_ptr); -int extract_host_port(char *str, char **host_ptr, unsigned char *ip, - unsigned short *p); long bio_dump_callback(BIO *bio, int cmd, const char *argp, int argi, long argl, long ret); diff --git a/apps/s_client.c b/apps/s_client.c index fe402ae3a8..d6797f5066 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -173,8 +173,6 @@ typedef unsigned int u_int; # undef FIONBIO #endif -#define SSL_HOST_NAME "localhost" - #undef BUFSIZZ #define BUFSIZZ 1024*8 #define S_CLIENT_IRC_READ_TIMEOUT 8 @@ -634,7 +632,8 @@ static int tlsa_import_rrset(SSL *con, STACK_OF(OPENSSL_STRING) *rrset) typedef enum OPTION_choice { OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, - OPT_HOST, OPT_PORT, OPT_CONNECT, OPT_UNIX, OPT_XMPPHOST, OPT_VERIFY, + OPT_4, OPT_6, OPT_HOST, OPT_PORT, OPT_CONNECT, OPT_UNIX, + OPT_XMPPHOST, OPT_VERIFY, OPT_CERT, OPT_CRL, OPT_CRL_DOWNLOAD, OPT_SESS_OUT, OPT_SESS_IN, OPT_CERTFORM, OPT_CRLFORM, OPT_VERIFY_RET_ERROR, OPT_VERIFY_QUIET, OPT_BRIEF, OPT_PREXIT, OPT_CRLF, OPT_QUIET, OPT_NBIO, @@ -664,10 +663,14 @@ OPTIONS s_client_options[] = { {"host", OPT_HOST, 's', "Use -connect instead"}, {"port", OPT_PORT, 'p', "Use -connect instead"}, {"connect", OPT_CONNECT, 's', - "TCP/IP where to connect (default is " SSL_HOST_NAME ":" PORT_STR ")"}, + "TCP/IP where to connect (default is :" PORT ")"}, {"proxy", OPT_PROXY, 's', "Connect to via specified proxy to the real server"}, +#ifdef AF_UNIX {"unix", OPT_UNIX, 's', "Connect over unix domain sockets"}, +#endif + {"4", OPT_4, '-', "Use IPv4 only"}, + {"6", OPT_6, '-', "Use IPv6 only"}, {"verify", OPT_VERIFY, 'p', "Turn on peer certificate verification"}, {"cert", OPT_CERT, '<', "Certificate file to use, PEM format assumed"}, {"certform", OPT_CERTFORM, 'F', @@ -845,12 +848,12 @@ int s_client_main(int argc, char **argv) char *CApath = NULL, *CAfile = NULL, *cbuf = NULL, *sbuf = NULL; char *mbuf = NULL, *proxystr = NULL, *connectstr = NULL; char *cert_file = NULL, *key_file = NULL, *chain_file = NULL; - char *chCApath = NULL, *chCAfile = NULL, *host = SSL_HOST_NAME; + char *chCApath = NULL, *chCAfile = NULL, *host = NULL; + char *port = BUF_strdup(PORT); char *inrand = NULL; char *passarg = NULL, *pass = NULL, *vfyCApath = NULL, *vfyCAfile = NULL; char *sess_in = NULL, *sess_out = NULL, *crl_file = NULL, *p; char *jpake_secret = NULL, *xmpphost = NULL; - const char *unix_path = NULL; const char *ehlo = "mail.example.com"; struct sockaddr peer; struct timeval timeout, *timeoutp; @@ -862,12 +865,12 @@ int s_client_main(int argc, char **argv) int enable_timeouts = 0, sdebug = 0, peerlen = sizeof peer; int reconnect = 0, verify = SSL_VERIFY_NONE, vpmtouched = 0; int ret = 1, in_init = 1, i, nbio_test = 0, s = -1, k, width, state = 0; - int sbuf_len, sbuf_off, socket_type = SOCK_STREAM, cmdletters = 1; + int sbuf_len, sbuf_off, cmdletters = 1; + int socket_family = AF_UNSPEC, socket_type = SOCK_STREAM; int starttls_proto = PROTO_OFF, crl_format = FORMAT_PEM, crl_download = 0; int write_tty, read_tty, write_ssl, read_ssl, tty_on, ssl_pending; int fallback_scsv = 0; long socket_mtu = 0, randamt = 0; - unsigned short port = PORT; OPTION_CHOICE o; #ifndef OPENSSL_NO_ENGINE ENGINE *ssl_client_engine = NULL; @@ -926,22 +929,72 @@ int s_client_main(int argc, char **argv) opt_help(s_client_options); ret = 0; goto end; + case OPT_4: +#ifdef AF_UNIX + if (socket_family == AF_UNIX) { + OPENSSL_free(host); host = NULL; + OPENSSL_free(port); port = NULL; + } +#endif + socket_family = AF_INET; + break; + case OPT_6: + if (1) { +#ifdef AF_INET6 +#ifdef AF_UNIX + if (socket_family == AF_UNIX) { + OPENSSL_free(host); host = NULL; + OPENSSL_free(port); port = NULL; + } +#endif + socket_family = AF_INET6; + } else { +#endif + BIO_printf(bio_err, "%s: IPv6 domain sockets unsupported\n", prog); + goto end; + } + break; case OPT_HOST: - host = opt_arg(); +#ifdef AF_UNIX + if (socket_family == AF_UNIX) { + OPENSSL_free(host); host = NULL; + OPENSSL_free(port); port = NULL; + socket_family = AF_UNSPEC; + } +#endif + OPENSSL_free(host); host = BUF_strdup(opt_arg()); break; case OPT_PORT: - port = atoi(opt_arg()); +#ifdef AF_UNIX + if (socket_family == AF_UNIX) { + OPENSSL_free(host); host = NULL; + OPENSSL_free(port); port = NULL; + socket_family = AF_UNSPEC; + } +#endif + OPENSSL_free(port); port = BUF_strdup(opt_arg()); break; case OPT_CONNECT: +#ifdef AF_UNIX + if (socket_family == AF_UNIX) { + socket_family = AF_UNSPEC; + } +#endif + OPENSSL_free(host); host = NULL; + OPENSSL_free(port); port = NULL; connectstr = opt_arg(); break; case OPT_PROXY: proxystr = opt_arg(); starttls_proto = PROTO_CONNECT; break; +#ifdef AF_UNIX case OPT_UNIX: - unix_path = opt_arg(); + socket_family = AF_UNIX; + OPENSSL_free(host); host = BUF_strdup(opt_arg()); + OPENSSL_free(port); port = NULL; break; +#endif case OPT_XMPPHOST: xmpphost = opt_arg(); break; @@ -1286,18 +1339,41 @@ int s_client_main(int argc, char **argv) argv = opt_rest(); if (proxystr) { + int res; + char *tmp_host = host, *tmp_port = port; if (connectstr == NULL) { BIO_printf(bio_err, "%s: -proxy requires use of -connect\n", prog); goto opthelp; } - if (!extract_host_port(proxystr, &host, NULL, &port)) + res = BIO_parse_hostserv(proxystr, &host, &port, BIO_PARSE_PRIO_HOST); + if (tmp_host != host) + OPENSSL_free(tmp_host); + if (tmp_port != port) + OPENSSL_free(tmp_port); + if (!res) { + BIO_printf(bio_err, "%s: -proxy argument malformed or ambiguous\n", + prog); + goto end; + } + } else { + int res = 1; + char *tmp_host = host, *tmp_port = port; + if (connectstr != NULL) + res = BIO_parse_hostserv(connectstr, &host, &port, + BIO_PARSE_PRIO_HOST); + if (tmp_host != host) + OPENSSL_free(tmp_host); + if (tmp_port != port) + OPENSSL_free(tmp_port); + if (!res) { + BIO_printf(bio_err, + "%s: -connect argument malformed or ambiguous\n", + prog); goto end; + } } - else if (connectstr != NULL - && !extract_host_port(connectstr, &host, NULL, &port)) - goto end; - if (unix_path && (socket_type != SOCK_STREAM)) { + if (socket_family == AF_UNIX && socket_type != SOCK_STREAM) { BIO_printf(bio_err, "Can't use unix sockets and datagrams together\n"); goto end; @@ -1610,12 +1686,7 @@ int s_client_main(int argc, char **argv) } re_start: -#ifdef NO_SYS_UN_H - if (init_client(&s, host, port, socket_type) == 0) -#else - if ((!unix_path && (init_client(&s, host, port, socket_type) == 0)) || - (unix_path && (init_client_unix(&s, unix_path) == 0))) -#endif + if (init_client(&s, host, port, socket_family, socket_type) == 0) { BIO_printf(bio_err, "connect:errno=%d\n", get_last_socket_error()); SHUTDOWN(s); @@ -2364,6 +2435,8 @@ int s_client_main(int argc, char **argv) #ifndef OPENSSL_NO_SRP OPENSSL_free(srp_arg.srppassin); #endif + OPENSSL_free(host); + OPENSSL_free(port); X509_VERIFY_PARAM_free(vpm); ssl_excert_free(exc); sk_OPENSSL_STRING_free(ssl_args); diff --git a/apps/s_server.c b/apps/s_server.c index 848ba1f0a2..1a54f082f4 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -191,9 +191,12 @@ typedef unsigned int u_int; #endif static int not_resumable_sess_cb(SSL *s, int is_forward_secure); -static int sv_body(char *hostname, int s, int stype, unsigned char *context); -static int www_body(char *hostname, int s, int stype, unsigned char *context); -static int rev_body(char *hostname, int s, int stype, unsigned char *context); +static int sv_body(const char *hostname, int s, int stype, + unsigned char *context); +static int www_body(const char *hostname, int s, int stype, + unsigned char *context); +static int rev_body(const char *hostname, int s, int stype, + unsigned char *context); static void close_accept_socket(void); static int init_ssl_connection(SSL *s); static void print_stats(BIO *bp, SSL_CTX *ctx); @@ -791,8 +794,8 @@ static char *srtp_profiles = NULL; #endif typedef enum OPTION_choice { - OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, - OPT_ENGINE, OPT_PORT, OPT_UNIX, OPT_UNLINK, OPT_NACCEPT, + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, OPT_ENGINE, + OPT_4, OPT_6, OPT_ACCEPT, OPT_PORT, OPT_UNIX, OPT_UNLINK, OPT_NACCEPT, OPT_VERIFY, OPT_UPPER_V_VERIFY, OPT_CONTEXT, OPT_CERT, OPT_CRL, OPT_CRL_DOWNLOAD, OPT_SERVERINFO, OPT_CERTFORM, OPT_KEY, OPT_KEYFORM, OPT_PASS, OPT_CERT_CHAIN, OPT_DHPARAM, OPT_DCERTFORM, OPT_DCERT, @@ -821,9 +824,13 @@ typedef enum OPTION_choice { OPTIONS s_server_options[] = { {"help", OPT_HELP, '-', "Display this summary"}, {"port", OPT_PORT, 'p'}, - {"accept", OPT_PORT, 'p', - "TCP/IP port to accept on (default is " PORT_STR ")"}, + {"accept", OPT_ACCEPT, 's', + "TCP/IP port or service to accept on (default is " PORT ")"}, +#ifdef AF_UNIX {"unix", OPT_UNIX, 's', "Unix domain socket to accept on"}, +#endif + {"4", OPT_4, '-', "Use IPv4 only"}, + {"6", OPT_6, '-', "Use IPv6 only"}, {"unlink", OPT_UNLINK, '-', "For -unix, unlink existing socket first"}, {"context", OPT_CONTEXT, 's', "Set session ID context"}, {"verify", OPT_VERIFY, 'n', "Turn on peer certificate verification"}, @@ -998,11 +1005,10 @@ int s_server_main(int argc, char *argv[]) #ifndef OPENSSL_NO_PSK char *p; #endif - const char *unix_path = NULL; -#ifndef NO_SYS_UN_H +#ifdef AF_UNIX int unlink_unix_path = 0; #endif - int (*server_cb) (char *hostname, int s, int stype, + int (*server_cb) (const char *hostname, int s, int stype, unsigned char *context); int vpmtouched = 0, build_chain = 0, no_cache = 0, ext_cache = 0; #ifndef OPENSSL_NO_DH @@ -1012,9 +1018,11 @@ int s_server_main(int argc, char *argv[]) int noCApath = 0, noCAfile = 0; int s_cert_format = FORMAT_PEM, s_key_format = FORMAT_PEM; int s_dcert_format = FORMAT_PEM, s_dkey_format = FORMAT_PEM; - int rev = 0, naccept = -1, sdebug = 0, socket_type = SOCK_STREAM; + int rev = 0, naccept = -1, sdebug = 0; + int socket_family = AF_UNSPEC, socket_type = SOCK_STREAM; int state = 0, crl_format = FORMAT_PEM, crl_download = 0; - unsigned short port = PORT; + char *host = NULL; + char *port = BUF_strdup(PORT); unsigned char *context = NULL; OPTION_CHOICE o; EVP_PKEY *s_key2 = NULL; @@ -1059,26 +1067,71 @@ int s_server_main(int argc, char *argv[]) ret = 0; goto end; + case OPT_4: +#ifdef AF_UNIX + if (socket_family == AF_UNIX) { + OPENSSL_free(host); host = NULL; + OPENSSL_free(port); port = NULL; + } +#endif + socket_family = AF_INET; + break; + case OPT_6: + if (1) { +#ifdef AF_INET6 +#ifdef AF_UNIX + if (socket_family == AF_UNIX) { + OPENSSL_free(host); host = NULL; + OPENSSL_free(port); port = NULL; + } +#endif + socket_family = AF_INET6; + } else { +#endif + BIO_printf(bio_err, "%s: IPv6 domain sockets unsupported\n", prog); + goto end; + } + break; case OPT_PORT: - if (!extract_port(opt_arg(), &port)) +#ifdef AF_UNIX + if (socket_family == AF_UNIX) { + socket_family = AF_UNSPEC; + } +#endif + OPENSSL_free(port); port = NULL; + OPENSSL_free(host); host = NULL; + if (BIO_parse_hostserv(opt_arg(), NULL, &port, BIO_PARSE_PRIO_SERV) < 1) { + BIO_printf(bio_err, + "%s: -port argument malformed or ambiguous\n", + port); + goto end; + } + break; + case OPT_ACCEPT: +#ifdef AF_UNIX + if (socket_family == AF_UNIX) { + socket_family = AF_UNSPEC; + } +#endif + OPENSSL_free(port); port = NULL; + OPENSSL_free(host); host = NULL; + if (BIO_parse_hostserv(opt_arg(), &host, &port, BIO_PARSE_PRIO_SERV) < 1) { + BIO_printf(bio_err, + "%s: -accept argument malformed or ambiguous\n", + port); goto end; + } break; +#ifdef AF_UNIX case OPT_UNIX: -#ifdef NO_SYS_UN_H - BIO_printf(bio_err, "unix domain sockets unsupported\n"); - goto end; -#else - unix_path = opt_arg(); -#endif + socket_family = AF_UNIX; + OPENSSL_free(host); host = BUF_strdup(opt_arg()); + OPENSSL_free(port); port = NULL; break; case OPT_UNLINK: -#ifdef NO_SYS_UN_H - BIO_printf(bio_err, "unix domain sockets unsupported\n"); - goto end; -#else unlink_unix_path = 1; -#endif break; +#endif case OPT_NACCEPT: naccept = atol(opt_arg()); break; @@ -1462,11 +1515,13 @@ int s_server_main(int argc, char *argv[]) } #endif - if (unix_path && (socket_type != SOCK_STREAM)) { +#ifdef AF_UNIX + if (socket_family == AF_UNIX && socket_type != SOCK_STREAM) { BIO_printf(bio_err, "Can't use unix sockets and datagrams together\n"); goto end; } +#endif #if !defined(OPENSSL_NO_JPAKE) && !defined(OPENSSL_NO_PSK) if (jpake_secret) { if (psk_key) { @@ -1929,16 +1984,13 @@ int s_server_main(int argc, char *argv[]) server_cb = www_body; else server_cb = sv_body; -#ifndef NO_SYS_UN_H - if (unix_path) { - if (unlink_unix_path) - unlink(unix_path); - do_server_unix(unix_path, &accept_socket, server_cb, context, - naccept); - } else +#ifdef AF_UNIX + if (socket_family == AF_UNIX + && unlink_unix_path) + unlink(host); #endif - do_server(port, socket_type, &accept_socket, server_cb, context, - naccept); + do_server(&accept_socket, host, port, socket_family, socket_type, + server_cb, context, naccept); print_stats(bio_s_out, ctx); ret = 0; end: @@ -1952,6 +2004,8 @@ int s_server_main(int argc, char *argv[]) sk_X509_pop_free(s_dchain, X509_free); OPENSSL_free(pass); OPENSSL_free(dpass); + OPENSSL_free(host); + OPENSSL_free(port); X509_VERIFY_PARAM_free(vpm); free_sessions(); OPENSSL_free(tlscstatp.host); @@ -2006,7 +2060,8 @@ static void print_stats(BIO *bio, SSL_CTX *ssl_ctx) SSL_CTX_sess_get_cache_size(ssl_ctx)); } -static int sv_body(char *hostname, int s, int stype, unsigned char *context) +static int sv_body(const char *hostname, int s, int stype, + unsigned char *context) { char *buf = NULL; fd_set readfds; @@ -2599,7 +2654,8 @@ static DH *load_dh_param(const char *dhfile) } #endif -static int www_body(char *hostname, int s, int stype, unsigned char *context) +static int www_body(const char *hostname, int s, int stype, + unsigned char *context) { char *buf = NULL; int ret = 1; @@ -2986,7 +3042,8 @@ static int www_body(char *hostname, int s, int stype, unsigned char *context) return (ret); } -static int rev_body(char *hostname, int s, int stype, unsigned char *context) +static int rev_body(const char *hostname, int s, int stype, + unsigned char *context) { char *buf = NULL; int i; 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 - -#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 +# include -# 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 */ -- 2.25.1