s_client/s_server: support unix domain sockets

The "-unix <path>" argument allows s_server and s_client to use a unix
domain socket in the filesystem instead of IPv4 ("-connect", "-port",
"-accept", etc). If s_server exits gracefully, such as when "-naccept"
is used and the requested number of SSL/TLS connections have occurred,
then the domain socket file is removed. On ctrl-C, it is likely that
the stale socket file will be left over, such that s_server would
normally fail to restart with the same arguments. For this reason,
s_server also supports an "-unlink" option, which will clean up any
stale socket file before starting.

If you have any reason to want encrypted IPC within an O/S instance,
this concept might come in handy. Otherwise it just demonstrates that
there is nothing about SSL/TLS that limits it to TCP/IP in any way.

(There might also be benchmarking and profiling use in this path, as
unix domain sockets are much lower overhead than connecting over local
IP addresses).

Signed-off-by: Geoff Thorpe <geoff@openssl.org>
diff --git a/apps/s_server.c b/apps/s_server.c
index 05ffc35..7c4f7bc 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -479,7 +479,9 @@
 	{
 	BIO_printf(bio_err,"usage: s_server [args ...]\n");
 	BIO_printf(bio_err,"\n");
-	BIO_printf(bio_err," -accept arg   - port to accept on (default is %d)\n",PORT);
+	BIO_printf(bio_err," -accept port  - TCP/IP port to accept on (default is %d)\n",PORT);
+	BIO_printf(bio_err," -unix path    - unix domain socket to accept on\n");
+	BIO_printf(bio_err," -unlink       - for -unix, unlink existing socket first\n");
 	BIO_printf(bio_err," -context arg  - set session ID context\n");
 	BIO_printf(bio_err," -verify arg   - turn on peer certificate verification\n");
 	BIO_printf(bio_err," -Verify arg   - turn on peer certificate verification, must have a cert.\n");
@@ -1008,6 +1010,9 @@
 	X509_VERIFY_PARAM *vpm = NULL;
 	int badarg = 0;
 	short port=PORT;
+	const char *unix_path=NULL;
+	int unlink_unix_path=0;
+	int (*server_cb)(char *hostname, int s, int stype, unsigned char *context);
 	char *CApath=NULL,*CAfile=NULL;
 	char *chCApath=NULL,*chCAfile=NULL;
 	char *vfyCApath=NULL,*vfyCAfile=NULL;
@@ -1100,6 +1105,25 @@
 			if (!extract_port(*(++argv),&port))
 				goto bad;
 			}
+		else if (strcmp(*argv,"-unix") == 0)
+			{
+#ifdef NO_SYS_UN_H
+			BIO_printf(bio_err, "unix domain sockets unsupported\n");
+			goto bad;
+#else
+			if (--argc < 1) goto bad;
+			unix_path = *(++argv);
+#endif
+			}
+		else if (strcmp(*argv,"-unlink") == 0)
+			{
+#ifdef NO_SYS_UN_H
+			BIO_printf(bio_err, "unix domain sockets unsupported\n");
+			goto bad;
+#else
+			unlink_unix_path = 1;
+#endif
+			}
 		else if	(strcmp(*argv,"-naccept") == 0)
 			{
 			if (--argc < 1) goto bad;
@@ -1544,6 +1568,11 @@
 		goto end;
 		}
 
+	if (unix_path && (socket_type != SOCK_STREAM))
+		{
+		BIO_printf(bio_err, "Can't use unix sockets and datagrams together\n");
+			goto end;
+		}
 #if !defined(OPENSSL_NO_JPAKE) && !defined(OPENSSL_NO_PSK)
 	if (jpake_secret)
 		{
@@ -2106,11 +2135,19 @@
 	BIO_printf(bio_s_out,"ACCEPT\n");
 	(void)BIO_flush(bio_s_out);
 	if (rev)
-		do_server(port,socket_type,&accept_socket,rev_body, context, naccept);
+		server_cb = rev_body;
 	else if (www)
-		do_server(port,socket_type,&accept_socket,www_body, context, naccept);
+		server_cb = www_body;
 	else
-		do_server(port,socket_type,&accept_socket,sv_body, context, naccept);
+		server_cb = sv_body;
+	if (unix_path)
+		{
+		if (unlink_unix_path)
+			unlink(unix_path);
+		do_server_unix(unix_path,&accept_socket,server_cb, context, naccept);
+		}
+	else
+		do_server(port,socket_type,&accept_socket,server_cb, context, naccept);
 	print_stats(bio_s_out,ctx);
 	ret=0;
 end: