diff --git a/CHANGELOG b/CHANGELOG
index fefa210b0..5519fc2de 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,9 @@
# Nmap Changelog ($Id$); -*-text-*-
+o [Ncat][GH#446] Added Datagram TLS (DTLS) support to Ncat in connect (client)
+ mode with --udp --ssl. Also added Application Layer Protocol Negotiation
+ (ALPN) support with the --ssl-alpn option. [denandz, Daniel Miller]
+
o Updated the default ciphers list for Ncat and the secure ciphers list for
Nsock to use "!aNULL:!eNULL" instead of "!ADH". With the addition of ECDH
ciphersuites, anonymous ECDH suites were being allowed. [Daniel Miller]
diff --git a/ncat/docs/ncat.xml b/ncat/docs/ncat.xml
index 1dd88ee00..cc87dc332 100644
--- a/ncat/docs/ncat.xml
+++ b/ncat/docs/ncat.xml
@@ -314,6 +314,8 @@
particularly handy for talking to SSL enabled HTTP servers, etc.
In server mode, this option listens for incoming SSL connections,
rather than plain untunneled traffic.
+ In UDP connect mode, this option enables Datagram TLS (DTLS).
+ This is not supported in server mode.
@@ -395,6 +397,20 @@
ALL:!aNULL:!eNULL:!LOW:!EXP:!MD5:@STRENGTH
+
+
+
+ (Specify ALPN protocol list)
+ (Ncat option)
+
+
+ This option allows you to specify a comma-separated list of
+ protocols to send via the Application-Layer Protocol Negotiation
+ (ALPN) TLS extension. Not supported by all versions of OpenSSL.
+
+
+
+
diff --git a/ncat/docs/ncatguide.xml b/ncat/docs/ncatguide.xml
index ec401a5fa..fa8ec2cd3 100644
--- a/ncat/docs/ncatguide.xml
+++ b/ncat/docs/ncatguide.xml
@@ -308,8 +308,8 @@ Content-Type: text/html; charset=UTF-8
with only one client, and the
(Ncat option)not supported with UDP
option doesn't work, the reason for this being that UDP has no notion
- of a connection. UDP may not be combined with
- SSL.SSLnot supported with UDP
+ of a connection. UDP may be secured by a form of SSL called Datagram TLS (DTLS)DTLSDatagram TLS.
+ This is currently only supported in connect (client) mode.SSLnot supported with UDP in server mode
diff --git a/ncat/ncat_connect.c b/ncat/ncat_connect.c
index 51f061ee3..d8c73ab1b 100644
--- a/ncat/ncat_connect.c
+++ b/ncat/ncat_connect.c
@@ -256,6 +256,30 @@ static void set_ssl_ctx_options(SSL_CTX *ctx)
if (!SSL_CTX_set_cipher_list(ctx, o.sslciphers))
bye("Unable to set OpenSSL cipher list: %s", ERR_error_string(ERR_get_error(), NULL));
}
+
+#ifdef HAVE_ALPN_SUPPORT
+
+ if (o.sslalpn) {
+ size_t alpn_len;
+ unsigned char *alpn = next_protos_parse(&alpn_len, o.sslalpn);
+
+ if (alpn == NULL)
+ bye("Could not parse ALPN string");
+
+ if (o.debug)
+ logdebug("Using ALPN String %s\n", o.sslalpn);
+
+ /* SSL_CTX_set_alpn_protos returns 0 on success */
+ if (SSL_CTX_set_alpn_protos(ctx, alpn, alpn_len) != 0){
+ free(alpn);
+ bye("SSL_CTX_set_alpn_protos: %s.", ERR_error_string(ERR_get_error(), NULL));
+ }
+
+ free(alpn);
+ }
+
+#endif
+
}
#endif
@@ -878,6 +902,51 @@ static int do_proxy_socks5(void)
return(sd);
}
+static nsock_iod new_iod(nsock_pool mypool) {
+ nsock_iod nsi = nsock_iod_new(mypool, NULL);
+ if (nsi == NULL)
+ bye("Failed to create nsock_iod.");
+ if (nsock_iod_set_hostname(nsi, o.target) == -1)
+ bye("Failed to set hostname on iod.");
+
+ switch (srcaddr.storage.ss_family) {
+ case AF_UNSPEC:
+ break;
+ case AF_INET:
+ nsock_iod_set_localaddr(nsi, &srcaddr.storage,
+ sizeof(srcaddr.in));
+ break;
+#ifdef AF_INET6
+ case AF_INET6:
+ nsock_iod_set_localaddr(nsi, &srcaddr.storage,
+ sizeof(srcaddr.in6));
+ break;
+#endif
+#if HAVE_SYS_UN_H
+ case AF_UNIX:
+ nsock_iod_set_localaddr(nsi, &srcaddr.storage,
+ SUN_LEN((struct sockaddr_un *)&srcaddr.storage));
+ break;
+#endif
+ default:
+ nsock_iod_set_localaddr(nsi, &srcaddr.storage,
+ sizeof(srcaddr.storage));
+ break;
+ }
+
+ if (o.numsrcrtes) {
+ unsigned char *ipopts = NULL;
+ size_t ipoptslen = 0;
+
+ if (o.af != AF_INET)
+ bye("Sorry, -g can only currently be used with IPv4.");
+ ipopts = buildsrcrte(targetaddrs->addr.in.sin_addr, o.srcrtes, o.numsrcrtes, o.srcrteptr, &ipoptslen);
+
+ nsock_iod_set_ipoptions(nsi, ipopts, ipoptslen);
+ free(ipopts); /* Nsock has its own copy */
+ }
+ return nsi;
+}
int ncat_connect(void)
{
@@ -908,18 +977,15 @@ int ncat_connect(void)
nsock_pool_set_broadcast(mypool, 1);
#ifdef HAVE_OPENSSL
- set_ssl_ctx_options((SSL_CTX *) nsock_pool_ssl_init(mypool, 0));
+#ifdef HAVE_DTLS_CLIENT_METHOD
+ if(o.proto == IPPROTO_UDP)
+ set_ssl_ctx_options((SSL_CTX *) nsock_pool_dtls_init(mypool, 0));
+ else
+#endif
+ set_ssl_ctx_options((SSL_CTX *) nsock_pool_ssl_init(mypool, 0));
#endif
if (!o.proxytype) {
- /* A non-proxy connection. Create an iod for a new socket. */
- cs.sock_nsi = nsock_iod_new(mypool, NULL);
- if (cs.sock_nsi == NULL)
- bye("Failed to create nsock_iod.");
-
- if (nsock_iod_set_hostname(cs.sock_nsi, o.target) == -1)
- bye("Failed to set hostname on iod.");
-
#if HAVE_SYS_UN_H
/* For DGRAM UNIX socket we have to use source socket */
if (o.af == AF_UNIX && o.proto == IPPROTO_UDP)
@@ -945,50 +1011,13 @@ int ncat_connect(void)
strncpy(srcaddr.un.sun_path, tmp_name, sizeof(srcaddr.un.sun_path));
free (tmp_name);
}
- nsock_iod_set_localaddr(cs.sock_nsi, &srcaddr.storage,
- SUN_LEN((struct sockaddr_un *)&srcaddr.storage));
if (o.verbose)
loguser("[%s] used as source DGRAM Unix domain socket.\n", srcaddr.un.sun_path);
}
- else
#endif
- switch (srcaddr.storage.ss_family) {
- case AF_UNSPEC:
- break;
- case AF_INET:
- nsock_iod_set_localaddr(cs.sock_nsi, &srcaddr.storage,
- sizeof(srcaddr.in));
- break;
-#ifdef AF_INET6
- case AF_INET6:
- nsock_iod_set_localaddr(cs.sock_nsi, &srcaddr.storage,
- sizeof(srcaddr.in6));
- break;
-#endif
-#if HAVE_SYS_UN_H
- case AF_UNIX:
- nsock_iod_set_localaddr(cs.sock_nsi, &srcaddr.storage,
- SUN_LEN((struct sockaddr_un *)&srcaddr.storage));
- break;
-#endif
- default:
- nsock_iod_set_localaddr(cs.sock_nsi, &srcaddr.storage,
- sizeof(srcaddr.storage));
- break;
- }
-
- if (o.numsrcrtes) {
- unsigned char *ipopts = NULL;
- size_t ipoptslen = 0;
-
- if (o.af != AF_INET)
- bye("Sorry, -g can only currently be used with IPv4.");
- ipopts = buildsrcrte(targetaddrs->addr.in.sin_addr, o.srcrtes, o.numsrcrtes, o.srcrteptr, &ipoptslen);
-
- nsock_iod_set_ipoptions(cs.sock_nsi, ipopts, ipoptslen);
- free(ipopts); /* Nsock has its own copy */
- }
+ /* A non-proxy connection. Create an iod for a new socket. */
+ cs.sock_nsi = new_iod(mypool);
#if HAVE_SYS_UN_H
if (o.af == AF_UNIX) {
@@ -1068,35 +1097,27 @@ int ncat_connect(void)
static void try_nsock_connect(nsock_pool nsp, struct sockaddr_list *conn_addr)
{
+#ifdef HAVE_OPENSSL
+ if (o.ssl) {
+ nsock_connect_ssl(nsp, cs.sock_nsi, connect_handler,
+ o.conntimeout, (void *)conn_addr->next,
+ &conn_addr->addr.sockaddr, conn_addr->addrlen,
+ o.proto, inet_port(&conn_addr->addr),
+ NULL);
+ }
+ else
+#endif
if (o.proto == IPPROTO_UDP) {
nsock_connect_udp(nsp, cs.sock_nsi, connect_handler, (void *)conn_addr->next,
&conn_addr->addr.sockaddr, conn_addr->addrlen,
inet_port(&conn_addr->addr));
}
-#ifdef HAVE_OPENSSL
- else if (o.proto == IPPROTO_SCTP && o.ssl) {
- nsock_connect_ssl(nsp, cs.sock_nsi, connect_handler,
- o.conntimeout, (void *)conn_addr->next,
- &conn_addr->addr.sockaddr, conn_addr->addrlen,
- IPPROTO_SCTP, inet_port(&conn_addr->addr),
- NULL);
- }
-#endif
else if (o.proto == IPPROTO_SCTP) {
nsock_connect_sctp(nsp, cs.sock_nsi, connect_handler,
o.conntimeout, (void *)conn_addr->next,
&conn_addr->addr.sockaddr, conn_addr->addrlen,
inet_port(&conn_addr->addr));
}
-#ifdef HAVE_OPENSSL
- else if (o.ssl) {
- nsock_connect_ssl(nsp, cs.sock_nsi, connect_handler,
- o.conntimeout, (void *)conn_addr->next,
- &conn_addr->addr.sockaddr, conn_addr->addrlen,
- IPPROTO_TCP, inet_port(&conn_addr->addr),
- NULL);
- }
-#endif
else {
nsock_connect_tcp(nsp, cs.sock_nsi, connect_handler,
o.conntimeout, (void *)conn_addr->next,
@@ -1132,6 +1153,13 @@ static void connect_handler(nsock_pool nsp, nsock_event evt, void *data)
loguser("Connection to %s failed: %s.\n", inet_socktop(&peer), socket_strerror(errcode));
loguser("Trying next address...\n");
}
+#ifdef HAVE_OPENSSL
+ /* If it's an SSL reconnect, clear out any old session info */
+ if (nsock_iod_check_ssl(cs.sock_nsi)) {
+ nsock_iod_delete(cs.sock_nsi, NSOCK_PENDING_NOTIFY);
+ cs.sock_nsi = new_iod(nsp);
+ }
+#endif
try_nsock_connect(nsp, next_addr);
return;
}
diff --git a/ncat/ncat_core.c b/ncat/ncat_core.c
index e5c983bfc..ef6ffdb80 100644
--- a/ncat/ncat_core.c
+++ b/ncat/ncat_core.c
@@ -215,6 +215,7 @@ void options_init(void)
o.sslverify = 0;
o.ssltrustfile = NULL;
o.sslciphers = NULL;
+ o.sslalpn = NULL;
#endif
}
diff --git a/ncat/ncat_core.h b/ncat/ncat_core.h
index a9a774517..3075a2155 100644
--- a/ncat/ncat_core.h
+++ b/ncat/ncat_core.h
@@ -218,6 +218,7 @@ struct options {
int sslverify;
char *ssltrustfile;
char *sslciphers;
+ char *sslalpn;
int zerobyte;
};
diff --git a/ncat/ncat_listen.c b/ncat/ncat_listen.c
index 6dc9c73ff..6d39ce4c4 100644
--- a/ncat/ncat_listen.c
+++ b/ncat/ncat_listen.c
@@ -264,6 +264,8 @@ static int ncat_listen_stream(int proto)
#ifdef HAVE_OPENSSL
if (o.ssl)
+ if (o.sslalpn)
+ bye("ALPN is not supported in listen mode\n");
setup_ssl_listen();
#endif
@@ -712,6 +714,11 @@ static int ncat_listen_dgram(int proto)
struct timeval *tvp = NULL;
unsigned int num_sockets;
+#ifdef HAVE_OPENSSL
+ if(o.ssl)
+ bye("DTLS is not supported in listen mode\n");
+#endif
+
for (i = 0; i < NUM_LISTEN_ADDRS; i++) {
sockfd[i].fd = -1;
sockfd[i].addr.storage.ss_family = AF_UNSPEC;
diff --git a/ncat/ncat_main.c b/ncat/ncat_main.c
index e045fc0a6..b4aa40532 100644
--- a/ncat/ncat_main.c
+++ b/ncat/ncat_main.c
@@ -320,12 +320,14 @@ int main(int argc, char *argv[])
{"ssl-verify", no_argument, NULL, 0},
{"ssl-trustfile", required_argument, NULL, 0},
{"ssl-ciphers", required_argument, NULL, 0},
+ {"ssl-alpn", required_argument, NULL, 0},
#else
{"ssl-cert", optional_argument, NULL, 0},
{"ssl-key", optional_argument, NULL, 0},
{"ssl-verify", no_argument, NULL, 0},
{"ssl-trustfile", optional_argument, NULL, 0},
{"ssl-ciphers", optional_argument, NULL, 0},
+ {"ssl-alpn", optional_argument, NULL, 0},
#endif
{0, 0, 0, 0}
};
@@ -536,7 +538,16 @@ int main(int argc, char *argv[])
} else if (strcmp(long_options[option_index].name, "ssl-ciphers") == 0) {
o.ssl = 1;
o.sslciphers = Strdup(optarg);
+#ifdef HAVE_ALPN_SUPPORT
+ } else if (strcmp(long_options[option_index].name, "ssl-alpn") == 0) {
+ o.ssl = 1;
+ o.sslalpn = Strdup(optarg);
}
+#else
+ } else if (strcmp(long_options[option_index].name, "ssl-alpn") == 0) {
+ bye("OpenSSL does not have ALPN support compiled in. The --ssl-alpn option cannot be chosen.");
+ }
+#endif
#else
else if (strcmp(long_options[option_index].name, "ssl-cert") == 0) {
bye("OpenSSL isn't compiled in. The --ssl-cert option cannot be chosen.");
@@ -548,6 +559,8 @@ int main(int argc, char *argv[])
bye("OpenSSL isn't compiled in. The --ssl-trustfile option cannot be chosen.");
} else if (strcmp(long_options[option_index].name, "ssl-ciphers") == 0) {
bye("OpenSSL isn't compiled in. The --ssl-ciphers option cannot be chosen.");
+ } else if (strcmp(long_options[option_index].name, "ssl-alpn") == 0) {
+ bye("OpenSSL isn't compiled in. The --ssl-alpn option cannot be chosen.");
}
#endif
#ifdef HAVE_LUA
@@ -637,6 +650,7 @@ int main(int argc, char *argv[])
" --ssl-verify Verify trust and domain name of certificates\n"
" --ssl-trustfile PEM file containing trusted SSL certificates\n"
" --ssl-ciphers Cipherlist containing SSL ciphers to use\n"
+" --ssl-alpn ALPN protocol list to use.\n"
#endif
" --version Display Ncat's version information and exit\n"
"\n"
@@ -903,9 +917,11 @@ int main(int argc, char *argv[])
}
if (o.proto == IPPROTO_UDP) {
- /* Don't allow a false sense of security if someone tries SSL over UDP. */
+
+#ifndef HAVE_DTLS_CLIENT_METHOD
if (o.ssl)
- bye("UDP mode does not support SSL.");
+ bye("OpenSSL does not have DTLS support compiled in.");
+#endif
if (o.keepopen && o.cmdexec == NULL)
bye("UDP mode does not support the -k or --keep-open options, except with --exec or --sh-exec.");
if (o.broker)
diff --git a/ncat/util.c b/ncat/util.c
index 795b57caa..fa1398534 100644
--- a/ncat/util.c
+++ b/ncat/util.c
@@ -750,3 +750,39 @@ int fix_line_endings(char *src, int *len, char **dst, int *state)
return 1;
}
+
+/*-
+ * next_protos_parse parses a comma separated list of strings into a string
+ * in a format suitable for passing to SSL_CTX_set_next_protos_advertised.
+ * outlen: (output) set to the length of the resulting buffer on success.
+ * err: NULL on failure
+ * in: a NULL terminated string like "abc,def,ghi"
+ *
+ * returns: a malloc'd buffer or NULL on failure.
+ */
+unsigned char *next_protos_parse(size_t *outlen, const char *in)
+{
+ size_t len;
+ unsigned char *out;
+ size_t i, start = 0;
+
+ len = strlen(in);
+ if (len >= 65535)
+ return NULL;
+
+ out = safe_malloc(strlen(in) + 1);
+ for (i = 0; i <= len; ++i) {
+ if (i == len || in[i] == ',') {
+ if (i - start > 255) {
+ free(out);
+ return NULL;
+ }
+ out[start] = i - start;
+ start = i + 1;
+ } else
+ out[i + 1] = in[i];
+ }
+
+ *outlen = len + 1;
+ return out;
+}
diff --git a/ncat/util.h b/ncat/util.h
index fbbd40a34..e6bc2dc08 100644
--- a/ncat/util.h
+++ b/ncat/util.h
@@ -230,4 +230,6 @@ struct fdinfo *get_fdinfo(const fd_list_t *, int);
int fix_line_endings(char *src, int *len, char **dst, int *state);
+unsigned char *next_protos_parse(size_t *outlen, const char *in);
+
#endif
diff --git a/nsock/include/nsock.h b/nsock/include/nsock.h
index 396424aad..7ad4775c2 100644
--- a/nsock/include/nsock.h
+++ b/nsock/include/nsock.h
@@ -248,6 +248,13 @@ void nsock_pool_set_device(nsock_pool nsp, const char *device);
#define NSOCK_SSL_MAX_SPEED (1 << 0)
nsock_ssl_ctx nsock_pool_ssl_init(nsock_pool ms_pool, int flags);
+/* Initializes an Nsock pool to create a DTLS connect. This sets and internal
+ * SSL_CTX, which is like a template that sets options for all connections that
+ * are made from it. Returns the SSL_CTX so tyou can set your own options.
+ *
+ * Functionally similar to nsock_pool_ssl_init, just for the DTLS */
+nsock_ssl_ctx nsock_pool_dtls_init(nsock_pool ms_pool, int flags);
+
/* Enforce use of a given IO engine.
* The engine parameter is a zero-terminated string that will be
* strup()'ed by the library. No validity check is performed by this function,
diff --git a/nsock/include/nsock_config.h.in b/nsock/include/nsock_config.h.in
index 76615849f..8d21eba4a 100644
--- a/nsock/include/nsock_config.h.in
+++ b/nsock/include/nsock_config.h.in
@@ -82,6 +82,8 @@
#undef HAVE_OPENSSL
#undef HAVE_SSL_SET_TLSEXT_HOST_NAME
+#undef HAVE_DTLS_CLIENT_METHOD
+#undef HAVE_ALPN_SUPPORT
#undef HAVE_EPOLL
#undef HAVE_POLL
diff --git a/nsock/src/configure b/nsock/src/configure
index c2ccc3dc7..4763c4b3d 100755
--- a/nsock/src/configure
+++ b/nsock/src/configure
@@ -664,6 +664,7 @@ infodir
docdir
oldincludedir
includedir
+runstatedir
localstatedir
sharedstatedir
sysconfdir
@@ -738,6 +739,7 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE}'
@@ -990,6 +992,15 @@ do
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
+ -runstatedir | --runstatedir | --runstatedi | --runstated \
+ | --runstate | --runstat | --runsta | --runst | --runs \
+ | --run | --ru | --r)
+ ac_prev=runstatedir ;;
+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+ | --run=* | --ru=* | --r=*)
+ runstatedir=$ac_optarg ;;
+
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1127,7 +1138,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
- libdir localedir mandir
+ libdir localedir mandir runstatedir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
@@ -1280,6 +1291,7 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
@@ -4908,6 +4920,52 @@ if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }; $as_echo "#define HAVE_SSL_SET_TLSEXT_HOST_NAME 1" >>confdefs.h
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for DTLS_client_method" >&5
+$as_echo_n "checking for DTLS_client_method... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include
+int
+main ()
+{
+DTLS_client_method()
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }; $as_echo "#define HAVE_DTLS_CLIENT_METHOD 1" >>confdefs.h
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_set_alpn_protos" >&5
+$as_echo_n "checking for SSL_set_alpn_protos... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include
+int
+main ()
+{
+SSL_set_alpn_protos(NULL, NULL, 0)
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }; $as_echo "#define HAVE_ALPN_SUPPORT 1" >>confdefs.h
+
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
diff --git a/nsock/src/configure.ac b/nsock/src/configure.ac
index c446f79b3..a170d00d7 100644
--- a/nsock/src/configure.ac
+++ b/nsock/src/configure.ac
@@ -263,6 +263,14 @@ if test "$use_openssl" = "yes"; then
AC_TRY_LINK([#include ], [SSL_set_tlsext_host_name(NULL, NULL)],
[AC_MSG_RESULT([yes]); AC_DEFINE(HAVE_SSL_SET_TLSEXT_HOST_NAME)],
[AC_MSG_RESULT([no])])
+ AC_MSG_CHECKING([for DTLS_client_method])
+ AC_TRY_LINK([#include ], [DTLS_client_method()],
+ [AC_MSG_RESULT([yes]); AC_DEFINE(HAVE_DTLS_CLIENT_METHOD)],
+ [AC_MSG_RESULT([no])])
+ AC_MSG_CHECKING([for SSL_set_alpn_protos])
+ AC_TRY_LINK([#include ], [SSL_set_alpn_protos(NULL, NULL, 0)],
+ [AC_MSG_RESULT([yes]); AC_DEFINE(HAVE_ALPN_SUPPORT)],
+ [AC_MSG_RESULT([no])])
LIBS="$LIBS_TMP"
fi
diff --git a/nsock/src/nsock_connect.c b/nsock/src/nsock_connect.c
index f5a1a59af..b6a9c2089 100644
--- a/nsock/src/nsock_connect.c
+++ b/nsock/src/nsock_connect.c
@@ -184,8 +184,8 @@ int nsock_setup_udp(nsock_pool nsp, nsock_iod ms_iod, int af) {
return nsi->sd;
}
-/* This does the actual logistics of requesting a TCP connection. It is shared
- * by nsock_connect_tcp and nsock_connect_ssl */
+/* This does the actual logistics of requesting a connection. It is shared
+ * by nsock_connect_tcp and nsock_connect_ssl, among others */
void nsock_connect_internal(struct npool *ms, struct nevent *nse, int type, int proto, struct sockaddr_storage *ss, size_t sslen,
unsigned short port) {
@@ -256,7 +256,7 @@ void nsock_connect_internal(struct npool *ms, struct nevent *nse, int type, int
if (ms->engine->io_operations->iod_connect(ms, iod->sd, (struct sockaddr *)ss, sslen) == -1) {
int err = socket_errno();
- if (proto == IPPROTO_UDP || (err != EINPROGRESS && err != EAGAIN)) {
+ if ((proto == IPPROTO_UDP) || (err != EINPROGRESS && err != EAGAIN)) {
nse->event_done = 1;
nse->status = NSE_STATUS_ERROR;
nse->errnum = err;
@@ -376,7 +376,7 @@ nsock_event_id nsock_connect_sctp(nsock_pool nsp, nsock_iod ms_iod, nsock_ev_han
return nse->id;
}
-/* Request an SSL over TCP/SCTP connection to another system (by IP address).
+/* Request an SSL over TCP/SCTP/UDP connection to another system (by IP address).
* The in_addr is normal network byte order, but the port number should be given
* in HOST BYTE ORDER. This function will call back only after it has made the
* connection AND done the initial SSL negotiation. From that point on, you use
@@ -397,7 +397,12 @@ nsock_event_id nsock_connect_ssl(nsock_pool nsp, nsock_iod nsiod, nsock_ev_handl
struct nevent *nse;
if (!ms->sslctx)
- nsock_pool_ssl_init(ms, 0);
+ {
+ if (proto == IPPROTO_UDP)
+ nsock_pool_dtls_init(ms, 0);
+ else
+ nsock_pool_ssl_init(ms, 0);
+ }
assert(nsi->state == NSIOD_STATE_INITIAL || nsi->state == NSIOD_STATE_UNKNOWN);
@@ -405,14 +410,22 @@ nsock_event_id nsock_connect_ssl(nsock_pool nsp, nsock_iod nsiod, nsock_ev_handl
assert(nse);
/* Set our SSL_SESSION so we can benefit from session-id reuse. */
- nsi_set_ssl_session(nsi, (SSL_SESSION *)ssl_session);
+ /* but not with DTLS; save space in ClientHello message */
+ if (proto != IPPROTO_UDP)
+ nsi_set_ssl_session(nsi, (SSL_SESSION *)ssl_session);
- nsock_log_info("SSL connection requested to %s:%hu/%s (IOD #%li) EID %li",
+ if (proto == IPPROTO_UDP)
+ nsock_log_info("DTLS connection requested to %s:%hu/udp (IOD #%li) EID %li",
+
+ inet_ntop_ez(ss, sslen), port, nsi->id, nse->id);
+ else
+ nsock_log_info("SSL connection requested to %s:%hu/%s (IOD #%li) EID %li",
inet_ntop_ez(ss, sslen), port, (proto == IPPROTO_TCP ? "tcp" : "sctp"),
nsi->id, nse->id);
/* Do the actual connect() */
- nsock_connect_internal(ms, nse, SOCK_STREAM, proto, ss, sslen, port);
+ nsock_connect_internal(ms, nse, (proto == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM), proto, ss, sslen, port);
+
nsock_pool_add_event(ms, nse);
return nse->id;
diff --git a/nsock/src/nsock_core.c b/nsock/src/nsock_core.c
index e4faf8a80..23b9c9f94 100644
--- a/nsock/src/nsock_core.c
+++ b/nsock/src/nsock_core.c
@@ -377,7 +377,9 @@ void handle_connect_result(struct npool *ms, struct nevent *nse, enum nse_status
}
#if HAVE_SSL_SET_TLSEXT_HOST_NAME
- if (iod->hostname != NULL) {
+ /* Avoid sending SNI extension with DTLS because many servers don't allow
+ * fragmented ClientHello messages. */
+ if (iod->hostname != NULL && iod->lastproto != IPPROTO_UDP) {
if (SSL_set_tlsext_host_name(iod->ssl, iod->hostname) != 1)
fatal("SSL_set_tlsext_host_name failed: %s", ERR_error_string(ERR_get_error(), NULL));
}
diff --git a/nsock/src/nsock_ssl.c b/nsock/src/nsock_ssl.c
index ba7b2dd77..7683afff4 100644
--- a/nsock/src/nsock_ssl.c
+++ b/nsock/src/nsock_ssl.c
@@ -80,8 +80,7 @@
extern struct timeval nsock_tod;
-/* Create an SSL_CTX and do initialization that is common to all init modes. */
-static SSL_CTX *ssl_init_common() {
+static SSL_CTX *ssl_init_helper(const SSL_METHOD *method) {
SSL_CTX *ctx;
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined LIBRESSL_VERSION_NUMBER
@@ -89,7 +88,7 @@ static SSL_CTX *ssl_init_common() {
SSL_library_init();
#endif
- ctx = SSL_CTX_new(SSLv23_client_method());
+ ctx = SSL_CTX_new(method);
if (!ctx) {
fatal("OpenSSL failed to create a new SSL_CTX: %s",
ERR_error_string(ERR_get_error(), NULL));
@@ -105,18 +104,19 @@ static SSL_CTX *ssl_init_common() {
return ctx;
}
+/* Create an SSL_CTX and do initialization that is common to all init modes. */
+static SSL_CTX *ssl_init_common() {
+ return ssl_init_helper(SSLv23_client_method());
+}
+
/* Initializes an Nsock pool to create SSL connections. This sets an internal
* SSL_CTX, which is like a template that sets options for all connections that
* are made from it. The connections made from this context will use only secure
* ciphers but no server certificate verification is done. Returns the SSL_CTX
* so you can set your own options. */
-nsock_ssl_ctx nsock_pool_ssl_init(nsock_pool ms_pool, int flags) {
- struct npool *ms = (struct npool *)ms_pool;
+static nsock_ssl_ctx nsock_pool_ssl_init_helper(struct npool *ms, int flags) {
char rndbuf[128];
- if (ms->sslctx == NULL)
- ms->sslctx = ssl_init_common();
-
/* Get_random_bytes may or may not provide high-quality randomness. Add it to
* the entropy pool without increasing the entropy estimate (third argument of
* RAND_add is 0). We rely on OpenSSL's entropy gathering, called implicitly
@@ -146,6 +146,49 @@ nsock_ssl_ctx nsock_pool_ssl_init(nsock_pool ms_pool, int flags) {
return ms->sslctx;
}
+nsock_ssl_ctx nsock_pool_ssl_init(nsock_pool ms_pool, int flags) {
+ struct npool *ms = (struct npool *)ms_pool;
+
+ if (ms->sslctx == NULL)
+ ms->sslctx = ssl_init_common();
+ return nsock_pool_ssl_init_helper(ms, flags);
+}
+
+#ifdef HAVE_DTLS_CLIENT_METHOD
+
+/* Create an SSL_CTX and do initialisation, creating a DTLS client */
+static SSL_CTX *dtls_init_common() {
+ return ssl_init_helper(DTLS_client_method());
+}
+
+/* Initializes an Nsock pool to create DTLS connections. Very much similar to
+ * nsock_pool_ssl_init, just with DTLS. */
+nsock_ssl_ctx nsock_pool_dtls_init(nsock_pool ms_pool, int flags) {
+ SSL_CTX *dtls_ctx = NULL;
+ struct npool *ms = (struct npool *)ms_pool;
+
+ if (ms->sslctx == NULL)
+ ms->sslctx = dtls_init_common();
+ dtls_ctx = (SSL_CTX *) nsock_pool_ssl_init_helper(ms, flags);
+
+ /* Don't add padding or the ClientHello will fragment and not connect properly. */
+ SSL_CTX_clear_options(dtls_ctx, SSL_OP_TLSEXT_PADDING);
+
+ if (!SSL_CTX_set_cipher_list(dtls_ctx, "DEFAULT"))
+ fatal("Unable to set OpenSSL cipher list: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+
+ return dtls_ctx;
+}
+
+#else /* OpenSSL Version does not support DTLS */
+
+nsock_ssl_ctx nsock_pool_dtls_init(nsock_pool ms_pool, int flags) {
+ fatal("%s called with no OpenSSL DTLS support", __func__);
+}
+
+#endif
+
/* Check server certificate verification, after a connection is established. We
* check first that a certificate was even offered, then call
* SSL_get_verify_result to get the overall status of verification. (Just
@@ -180,6 +223,10 @@ nsock_ssl_ctx nsock_pool_ssl_init(nsock_pool ms_pool, int flags) {
fatal("%s called with no OpenSSL support", __func__);
}
+nsock_ssl_ctx nsock_pool_dtls_init(nsock_pool ms_pool, int flags) {
+ fatal("%s called with no OpenSSL support", __func__);
+}
+
int nsi_ssl_post_connect_verify(const nsock_iod nsockiod) {
return 1;
}