Add a -sctp option to s_client
authorMatt Caswell <matt@openssl.org>
Thu, 20 Apr 2017 08:57:12 +0000 (09:57 +0100)
committerMatt Caswell <matt@openssl.org>
Tue, 25 Apr 2017 10:13:39 +0000 (11:13 +0100)
Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/3286)

apps/s_apps.h
apps/s_client.c
apps/s_socket.c

index 1f76009b8d1396816b117513e0445bfe8976fd13..38c6b67d9dba8fc2eecffbfa5edcdb0c60a25f59 100644 (file)
@@ -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,
index 9267393582ebe3be00b9a52e6f0bfbbf8194692f..52b99ce7a70f50bd22a58fa8d0b4ffbec1e3d06b 100644 (file)
@@ -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);
index 97dc9afffb76cc6996c963debaa376f45c3f6dc7..04f3e6741c1970c342e285e4b4579841305ccec5 100644 (file)
@@ -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;