summaryrefslogtreecommitdiffstats
path: root/doc/examples/tlsproxy/tlsproxy.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--doc/examples/tlsproxy/tlsproxy.c464
1 files changed, 464 insertions, 0 deletions
diff --git a/doc/examples/tlsproxy/tlsproxy.c b/doc/examples/tlsproxy/tlsproxy.c
new file mode 100644
index 0000000..8e781f7
--- /dev/null
+++ b/doc/examples/tlsproxy/tlsproxy.c
@@ -0,0 +1,464 @@
+/*
+
+The MIT License (MIT)
+
+Copyright (c) 2016 Wrymouth Innovation Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+#include "config.h"
+
+#include <errno.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "crypto-gnutls.h"
+
+static char *connectaddr = NULL;
+static char *listenaddr = NULL;
+static char *keyfile = NULL;
+static char *certfile = NULL;
+static char *cacertfile = NULL;
+static char *hostname = NULL;
+static int debug = 0;
+static int insecure = 0;
+static int nofork = 0;
+static int server = 0;
+
+static const char *defaultport = "12345";
+
+static volatile sig_atomic_t rxsigquit = 0;
+
+static int
+bindtoaddress (char *addrport)
+{
+ struct addrinfo hints;
+ struct addrinfo *result, *rp;
+ int fd, s;
+ char addr[128];
+
+ snprintf(addr, sizeof(addr), "%s", addrport);
+
+ memset (&hints, 0, sizeof (struct addrinfo));
+ hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
+ hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
+ hints.ai_socktype = SOCK_STREAM; /* Stream socket */
+ hints.ai_protocol = 0; /* any protocol */
+
+ char *colon = strrchr (addr, ':');
+ const char *port = defaultport;
+ if (colon)
+ {
+ *colon = 0;
+ port = colon + 1;
+ }
+
+ s = getaddrinfo (addr, port, &hints, &result);
+ if (s != 0)
+ {
+ fprintf (stderr, "Error in address %s: %s\n", addr, gai_strerror (s));
+ return -1;
+ }
+
+ /* attempt to bind to each address */
+
+ for (rp = result; rp != NULL; rp = rp->ai_next)
+ {
+ fd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+
+ if (fd >= 0)
+ {
+ int one = 1;
+ if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof (one)) <
+ 0)
+ {
+ close (fd);
+ continue;
+ }
+ if (bind (fd, rp->ai_addr, rp->ai_addrlen) == 0)
+ break;
+ close (fd);
+ }
+ }
+
+ if (!rp)
+ {
+ fprintf (stderr, "Error binding to %s:%s: %m\n", addr, port);
+ freeaddrinfo (result);
+ return -1;
+ }
+
+ freeaddrinfo (result); /* No longer needed */
+
+ if (listen (fd, 5) < 0)
+ {
+ close (fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int
+connecttoaddress (char *addrport)
+{
+ struct addrinfo hints;
+ struct addrinfo *result, *rp;
+ int fd, s;
+ char addr[128];
+
+ snprintf(addr, sizeof(addr), "%s", addrport);
+
+ memset (&hints, 0, sizeof (struct addrinfo));
+ hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
+ hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
+ hints.ai_socktype = SOCK_STREAM; /* Stream socket */
+ hints.ai_protocol = 0; /* any protocol */
+
+ char *colon = strrchr (addr, ':');
+ const char *port = defaultport;
+ if (colon)
+ {
+ *colon = 0;
+ port = colon + 1;
+ }
+
+ if (!hostname && !server)
+ hostname = strdup (addr);
+
+ s = getaddrinfo (addr, port, &hints, &result);
+ if (s != 0)
+ {
+ fprintf (stderr, "Error in address %s: %s\n", addr, gai_strerror (s));
+ return -1;
+ }
+
+ /* attempt to connect to each address */
+ for (rp = result; rp != NULL; rp = rp->ai_next)
+ {
+ fd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if (fd >= 0)
+ {
+ if (connect (fd, rp->ai_addr, rp->ai_addrlen) == 0)
+ break;
+ close (fd);
+ }
+ }
+
+ if (!rp)
+ {
+ fprintf (stderr, "Error connecting to %s:%s: %m\n", addr, port);
+ freeaddrinfo (result);
+ return -1;
+ }
+
+ freeaddrinfo (result); /* No longer needed */
+
+ return fd;
+}
+
+static int
+quitfn (void *opaque)
+{
+ return rxsigquit;
+}
+
+static int
+runproxy (int acceptfd)
+{
+ int connectfd;
+ if ((connectfd = connecttoaddress (connectaddr)) < 0)
+ {
+ fprintf (stderr, "Could not connect\n");
+ close (acceptfd);
+ return -1;
+ }
+
+ tlssession_t *session =
+ tlssession_new (server, keyfile, certfile, cacertfile, hostname, insecure,
+ debug, quitfn, NULL, NULL);
+ if (!session)
+ {
+ fprintf (stderr, "Could create TLS session\n");
+ close (connectfd);
+ close (acceptfd);
+ return -1;
+ }
+
+ int ret;
+ if (server)
+ ret = tlssession_mainloop (acceptfd, connectfd, session);
+ else
+ ret = tlssession_mainloop (connectfd, acceptfd, session);
+
+ tlssession_close (session);
+ close (connectfd);
+ close (acceptfd);
+
+ if (ret < 0)
+ {
+ fprintf (stderr, "TLS proxy exited with an error\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+runlistener (void)
+{
+ int listenfd;
+ if ((listenfd = bindtoaddress (listenaddr)) < 0)
+ {
+ fprintf (stderr, "Could not bind listener\n");
+ return -1;
+ }
+
+ /*
+ if (!nofork)
+ daemon (FALSE, FALSE);
+ */
+
+ int fd;
+ while (!rxsigquit)
+ {
+ do
+ {
+ if ((fd = accept (listenfd, NULL, NULL)) < 0)
+ {
+ if (errno != EINTR)
+ {
+ fprintf (stderr, "Accept failed\n");
+ return -1;
+ }
+ }
+ }
+ while (fd < 0 && !rxsigquit);
+ if (rxsigquit)
+ break;
+ if (nofork < 2)
+ {
+ int ret = runproxy (fd);
+ if (ret < 0)
+ return -1;
+ }
+ else
+ {
+ int cpid = fork ();
+ if (cpid == 0)
+ {
+ /* we're the child */
+ runproxy (fd);
+ exit (0);
+ }
+ else
+ close (fd);
+ }
+ }
+ return 0;
+}
+
+static void
+usage (void)
+{
+ fprintf (stderr, "tlsproxy\n\n\
+Usage:\n\
+ tlsproxy [OPTIONS]\n\
+\n\
+A TLS client or server proxy\n\
+\n\
+Options:\n\
+ -c, --connect ADDRESS Connect to ADDRESS\n\
+ -l, --listen ADDRESS Listen on ADDRESS\n\
+ -K, --key FILE Use FILE as private key\n\
+ -C, --cert FILE Use FILE as public key\n\
+ -A, --cacert FILE Use FILE as public CA cert file\n\
+ -H, --hostname HOSTNAME Use HOSTNAME to validate the CN of the peer\n\
+ rather than hostname extracted from -C option\n\
+ -s, --server Run the listen port encrypted rather than the\n\
+ connect port\n\
+ -i, --insecure Do not validate certificates\n\
+ -n, --nofork Do not fork off (aids debugging); specify twice\n\
+ to stop forking on accept as well\n\
+ -d, --debug Turn on debugging\n\
+ -h, --help Show this usage message\n\
+\n\
+\n");
+}
+
+static void
+processoptions (int argc, char **argv)
+{
+ while (1)
+ {
+ static const struct option longopts[] = {
+ {"connect", required_argument, 0, 'c'},
+ {"listen", required_argument, 0, 'l'},
+ {"key", required_argument, 0, 'K'},
+ {"cert", required_argument, 0, 'C'},
+ {"cacert", required_argument, 0, 'A'},
+ {"hostname", required_argument, 0, 'H'},
+ {"server", no_argument, 0, 's'},
+ {"insecure", no_argument, 0, 'i'},
+ {"nofork", no_argument, 0, 'n'},
+ {"debug", no_argument, 0, 'd'},
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0}
+ };
+
+ int optidx = 0;
+
+ int c =
+ getopt_long (argc, argv, "c:l:K:C:A:H:sindh", longopts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0: /* set a flag, nothing else to do */
+ break;
+
+ case 'c':
+ free (connectaddr);
+ connectaddr = strdup (optarg);
+ break;
+
+ case 'l':
+ free (listenaddr);
+ listenaddr = strdup (optarg);
+ break;
+
+ case 'K':
+ free (keyfile);
+ keyfile = strdup (optarg);
+ break;
+
+ case 'C':
+ free (certfile);
+ certfile = strdup (optarg);
+ break;
+
+ case 'A':
+ free (cacertfile);
+ cacertfile = strdup (optarg);
+ break;
+
+ case 'H':
+ free (hostname);
+ hostname = strdup (optarg);
+ break;
+
+ case 's':
+ server = 1;
+ break;
+
+ case 'i':
+ insecure = 1;
+ break;
+
+ case 'n':
+ nofork++;
+ break;
+
+ case 'd':
+ debug++;
+ break;
+
+ case 'h':
+ usage ();
+ exit (0);
+ break;
+
+ default:
+ usage ();
+ exit (1);
+ }
+ }
+
+ if (optind != argc || !connectaddr || !listenaddr)
+ {
+ usage ();
+ exit (1);
+ }
+
+ if (!certfile && keyfile)
+ certfile = strdup (keyfile);
+}
+
+static void
+handlesignal (int sig)
+{
+ switch (sig)
+ {
+ case SIGINT:
+ case SIGTERM:
+ rxsigquit++;
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+setsignalmasks (void)
+{
+ struct sigaction sa;
+ /* Set up the structure to specify the new action. */
+ memset (&sa, 0, sizeof (struct sigaction));
+ sa.sa_handler = handlesignal;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction (SIGINT, &sa, NULL);
+ sigaction (SIGTERM, &sa, NULL);
+
+ memset (&sa, 0, sizeof (struct sigaction));
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = SA_RESTART;
+ sigaction (SIGPIPE, &sa, NULL);
+}
+
+int
+main (int argc, char **argv)
+{
+ processoptions (argc, argv);
+
+ setsignalmasks ();
+
+ if (tlssession_init ())
+ exit (1);
+
+ runlistener ();
+
+ free (connectaddr);
+ free (listenaddr);
+ free (keyfile);
+ free (certfile);
+ free (cacertfile);
+ free (hostname);
+
+ exit (0);
+}