diff --git a/apps/s_apps.h b/apps/s_apps.h index 1f76009b8d1396816b117513e0445bfe8976fd13..38c6b67d9dba8fc2eecffbfa5edcdb0c60a25f59 100644 --- a/apps/s_apps.h +++ b/apps/s_apps.h @@ -38,7 +38,7 @@ int ssl_print_groups(BIO *out, SSL *s, int noshared); #endif int ssl_print_tmp_key(BIO *out, SSL *s); int init_client(int *sock, const char *host, const char *port, - int family, int type); + int family, int type, int protocol); int should_retry(int i); long bio_dump_callback(BIO *bio, int cmd, const char *argp, diff --git a/apps/s_client.c b/apps/s_client.c index 9267393582ebe3be00b9a52e6f0bfbbf8194692f..52b99ce7a70f50bd22a58fa8d0b4ffbec1e3d06b 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -539,7 +539,7 @@ typedef enum OPTION_choice { #endif OPT_SSL3, OPT_SSL_CONFIG, OPT_TLS1_3, OPT_TLS1_2, OPT_TLS1_1, OPT_TLS1, OPT_DTLS, OPT_DTLS1, - OPT_DTLS1_2, OPT_TIMEOUT, OPT_MTU, OPT_KEYFORM, OPT_PASS, + OPT_DTLS1_2, OPT_SCTP, OPT_TIMEOUT, OPT_MTU, OPT_KEYFORM, OPT_PASS, OPT_CERT_CHAIN, OPT_CAPATH, OPT_NOCAPATH, OPT_CHAINCAPATH, OPT_VERIFYCAPATH, OPT_KEY, OPT_RECONNECT, OPT_BUILD_CHAIN, OPT_CAFILE, OPT_NOCAFILE, @@ -699,6 +699,9 @@ const OPTIONS s_client_options[] = { #ifndef OPENSSL_NO_DTLS1_2 {"dtls1_2", OPT_DTLS1_2, '-', "Just use DTLSv1.2"}, #endif +#ifndef OPENSSL_NO_SCTP + {"sctp", OPT_SCTP, '-', "Use SCTP"}, +#endif #ifndef OPENSSL_NO_SSL_TRACE {"trace", OPT_TRACE, '-', "Show trace output of protocol messages"}, #endif @@ -847,7 +850,7 @@ int s_client_main(int argc, char **argv) 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, cmdletters = 1; - int socket_family = AF_UNSPEC, socket_type = SOCK_STREAM; + int socket_family = AF_UNSPEC, socket_type = SOCK_STREAM, protocol = 0; 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; #if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS) @@ -900,6 +903,7 @@ int s_client_main(int argc, char **argv) #endif BIO *bio_c_msg = NULL; const char *keylog_file = NULL, *early_data_file = NULL; + int isdtls = 0; FD_ZERO(&readfds); FD_ZERO(&writefds); @@ -1217,6 +1221,7 @@ int s_client_main(int argc, char **argv) #ifndef OPENSSL_NO_DTLS meth = DTLS_client_method(); socket_type = SOCK_DGRAM; + isdtls = 1; #endif break; case OPT_DTLS1: @@ -1225,6 +1230,7 @@ int s_client_main(int argc, char **argv) min_version = DTLS1_VERSION; max_version = DTLS1_VERSION; socket_type = SOCK_DGRAM; + isdtls = 1; #endif break; case OPT_DTLS1_2: @@ -1233,6 +1239,12 @@ int s_client_main(int argc, char **argv) min_version = DTLS1_2_VERSION; max_version = DTLS1_2_VERSION; socket_type = SOCK_DGRAM; + isdtls = 1; +#endif + break; + case OPT_SCTP: +#ifndef OPENSSL_NO_SCTP + protocol = IPPROTO_SCTP; #endif break; case OPT_TIMEOUT: @@ -1432,6 +1444,17 @@ int s_client_main(int argc, char **argv) goto end; } +#ifndef OPENSSL_NO_SCTP + if (protocol == IPPROTO_SCTP) { + if (socket_type != SOCK_DGRAM) { + BIO_printf(bio_err, "Can't use -sctp without DTLS\n"); + goto end; + } + /* SCTP is unusual. It uses DTLS over a SOCK_STREAM protocol */ + socket_type = SOCK_STREAM; + } +#endif + if (split_send_fragment > SSL3_RT_MAX_PLAIN_LENGTH) { BIO_printf(bio_err, "Bad split send fragment size\n"); goto end; @@ -1804,7 +1827,8 @@ int s_client_main(int argc, char **argv) } re_start: - if (init_client(&s, host, port, socket_family, socket_type) == 0) { + if (init_client(&s, host, port, socket_family, socket_type, protocol) + == 0) { BIO_printf(bio_err, "connect:errno=%d\n", get_last_socket_error()); BIO_closesocket(s); goto end; @@ -1819,10 +1843,16 @@ int s_client_main(int argc, char **argv) BIO_printf(bio_c_out, "Turned on non blocking io\n"); } #ifndef OPENSSL_NO_DTLS - if (socket_type == SOCK_DGRAM) { + if (isdtls) { union BIO_sock_info_u peer_info; - sbio = BIO_new_dgram(s, BIO_NOCLOSE); +#ifndef OPENSSL_NO_SCTP + if (protocol == IPPROTO_SCTP) + sbio = BIO_new_dgram_sctp(s, BIO_NOCLOSE); + else +#endif + sbio = BIO_new_dgram(s, BIO_NOCLOSE); + if ((peer_info.addr = BIO_ADDR_new()) == NULL) { BIO_printf(bio_err, "memory allocation failure\n"); BIO_closesocket(s); diff --git a/apps/s_socket.c b/apps/s_socket.c index 97dc9afffb76cc6996c963debaa376f45c3f6dc7..04f3e6741c1970c342e285e4b4579841305ccec5 100644 --- a/apps/s_socket.c +++ b/apps/s_socket.c @@ -44,6 +44,7 @@ typedef unsigned int u_int; * @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 + * @protocol: socket protocol, e.g. IPPROTO_TCP or IPPROTO_UDP (or 0 for any) * * 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. @@ -55,7 +56,7 @@ typedef unsigned int u_int; * Returns 1 on success, 0 on failure. */ int init_client(int *sock, const char *host, const char *port, - int family, int type) + int family, int type, int protocol) { BIO_ADDRINFO *res = NULL; const BIO_ADDRINFO *ai = NULL; @@ -64,7 +65,8 @@ int init_client(int *sock, const char *host, const char *port, if (!BIO_sock_init()) return 0; - ret = BIO_lookup(host, port, BIO_LOOKUP_CLIENT, family, type, &res); + ret = BIO_lookup_ex(host, port, BIO_LOOKUP_CLIENT, family, type, protocol, + &res); if (ret == 0) { ERR_print_errors(bio_err); return 0; @@ -76,7 +78,9 @@ int init_client(int *sock, const char *host, const char *port, * 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))); + && (type == 0 || type == BIO_ADDRINFO_socktype(res)) + && (protocol == 0 + || protocol == BIO_ADDRINFO_protocol(res))); *sock = BIO_socket(BIO_ADDRINFO_family(ai), BIO_ADDRINFO_socktype(ai), BIO_ADDRINFO_protocol(res), 0); @@ -86,6 +90,25 @@ int init_client(int *sock, const char *host, const char *port, */ continue; } + +#ifndef OPENSSL_NO_SCTP + if (protocol == IPPROTO_SCTP) { + /* + * For SCTP we have to set various options on the socket prior to + * connecting. This is done automatically by BIO_new_dgram_sctp(). + * We don't actually need the created BIO though so we free it again + * immediately. + */ + BIO *tmpbio = BIO_new_dgram_sctp(*sock, BIO_NOCLOSE); + + if (tmpbio == NULL) { + ERR_print_errors(bio_err); + return 0; + } + BIO_free(tmpbio); + } +#endif + if (!BIO_connect(*sock, BIO_ADDRINFO_address(ai), 0)) { BIO_closesocket(*sock); *sock = INVALID_SOCKET;