Require autoconf 2.59.
[elisp/starttls.git] / starttls.c
index 7343396..4159c5f 100644 (file)
@@ -29,6 +29,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdbool.h>
 
 #include <unistd.h>
 
 #include <signal.h>
 #include <fcntl.h>
 #include <netinet/in.h>
-#ifdef HAVE_POLL_H
-#include <sys/poll.h>
-#endif
-#define _GNU_SOURCE
-#include "getopt.h"
+#include <poll.h>
+#include <getopt.h>
+#include "getaddrinfo.h"
+#include "gettext.h"
+#include "inet_ntop.h"
+#include "strdup.h"
 
 extern void tls_negotiate (int, const char *, const char *);
 extern int tls_write(int, const char *, int);
@@ -55,7 +57,8 @@ extern int tls_read(int, char *, int);
 extern int tls_pending();
 
 static char *opt_cert_file = NULL, *opt_key_file = NULL;
-static int tls_fd;
+static bool opt_assuan = false;
+static int tls_fd = -1;
 
 static void
 usage (progname)
@@ -69,31 +72,25 @@ usage (progname)
          "Usage: %s [options] host port\n\n"
          "Options:\n\n"
          " --cert-file [file]      specify certificate file\n"
-         " --key-file [file]       specify private key file\n",
+         " --key-file [file]       specify private key file\n"
+         " --help                  show this help\n",
          progname, PACKAGE, VERSION, progname);
 }
 
 static void
 do_tls_negotiate(sig)
-  int sig;
+     int sig;
 {
-  tls_negotiate(tls_fd, opt_cert_file, opt_key_file);
+  tls_negotiate (tls_fd, opt_cert_file, opt_key_file);
 }
 
 int
 tcp_connect (hostname, service)
      const char *hostname, *service;
 {
-  int server, false = 0;
-#ifdef HAVE_ADDRINFO
+  int server, _false = 0;
   struct addrinfo *in, *in0, hints;
-#else
-  struct hostent *host;
-  struct servent *serv;
-  struct sockaddr_in sin;
-#endif
 
-#ifdef HAVE_ADDRINFO
   memset (&hints, 0, sizeof (hints));
   hints.ai_family = AF_UNSPEC;
   hints.ai_socktype = SOCK_STREAM;
@@ -115,49 +112,65 @@ tcp_connect (hostname, service)
 
   if (server < 0)
     return -1;
-#else
-  memset (&sin, 0, sizeof (sin));
-  host = gethostbyname (hostname);
-  if (!host)
-    return -1;
-  memcpy (&sin.sin_addr, host->h_addr, host->h_length);
-  serv = getservbyname (service, "tcp");
-  if (serv)
-    sin.sin_port = serv->s_port;
-  else if (isdigit (service[0]))
-    sin.sin_port = htons (atoi (service));
-  sin.sin_family = AF_INET;
-  server = socket (sin.sin_family, SOCK_STREAM, 0);
-  if (server == -1)
-    return -1;
-
-  if (connect (server, (struct sockaddr *)&sin, sizeof (sin)) < 0)
-    {
-      close (server);
-      return -1;
-    }
-#endif
 
-  setsockopt (server, SOL_SOCKET, SO_KEEPALIVE, (const char *) &false,
-             sizeof (false));
+  setsockopt (server, SOL_SOCKET, SO_KEEPALIVE, (const char *) &_false,
+             sizeof (_false));
 
   return server;
 }
 
 int
+redirect (fd, buffer, nbuffer, write_function)
+     int fd;
+     char *buffer;
+     int nbuffer;
+     int (*write_function) (int, const char *, int);
+{
+  sigset_t mask, orig_mask;
+  struct pollfd writefds[1];
+  int wrote;
+  char *retry;
+
+  sigemptyset (&mask);
+  sigaddset (&mask, SIGALRM);
+
+  writefds[0].fd = fd;
+  writefds[0].events = POLLOUT;
+
+  for (retry = buffer; nbuffer > 0; nbuffer -= wrote, retry += wrote)
+    {
+      int ready;
+
+      sigprocmask (SIG_SETMASK, &mask, &orig_mask);
+      ready = poll (writefds, 1, -1);
+      sigprocmask (SIG_SETMASK, &orig_mask, NULL);
+      if (ready == -1 && errno != EINTR)
+       {
+         if (opt_assuan)
+           printf ("ERR 1 poll: %s\r\n", strerror (errno));
+         return 1;
+       }
+      wrote = (*write_function)(fd, retry, nbuffer);
+      if (wrote == -1)
+       {
+         if (opt_assuan)
+           printf ("ERR 1 write: %s\r\n", strerror (errno));
+         return 1;
+       }
+    }
+}
+
+int
 main (argc, argv) 
-  int argc;
-  char **argv;
+     int argc;
+     char **argv;
 {
-  int in = fileno (stdin), out = fileno (stdout), 
+  int in = fileno (stdin), out = fileno (stdout),
     nbuffer, wrote;
-#ifdef HAVE_POLL
-  struct pollfd readfds[2], writefds[1];
-#else
-  fd_set readfds, writefds;
-#endif
-  char buffer[BUFSIZ], *retry;
+  struct pollfd readfds[2];
+  char buffer[BUFSIZ];
   struct sigaction act;
+  sigset_t mask, orig_mask;
 
   int this_option_optind = optind ? optind : 1;
   int option_index = 0, c;
@@ -165,12 +178,14 @@ main (argc, argv)
     {
       {"cert-file", 1, 0, 'c'},
       {"key-file", 1, 0, 'k'},
+      {"help", 0, 0, 'h'},
+      {"assuan", 0, 0, 'A'},
       {0, 0, 0, 0}
     };
 
   while (1)
     {
-      c = getopt_long (argc, argv, "c:k:", long_options, &option_index);
+      c = getopt_long (argc, argv, "c:k:hA", long_options, &option_index);
       if (c == -1)
        break;
     
@@ -182,15 +197,21 @@ main (argc, argv)
        case 'k':
          opt_key_file = optarg;
          break;
+       case 'h':
+         usage (argv[0]);
+         return 0;
+       case 'A':
+         opt_assuan = true;
+         break;
        default:
-         usage (basename (argv[0]));
+         usage (argv[0]);
          return 1;
        }
     }
 
   if (optind+2 != argc)
     {
-      usage (basename (argv[0]));
+      usage (argv[0]);
       return 1;
     }
 
@@ -207,87 +228,41 @@ main (argc, argv)
   act.sa_flags = SA_RESTART|SA_RESETHAND;
   sigaction (SIGALRM, &act, NULL);
 
-#ifdef HAVE_POLL
+  sigemptyset (&mask);
+  sigaddset (&mask, SIGALRM);
+
   readfds[0].fd = in;
   readfds[1].fd = tls_fd;
   readfds[0].events = POLLIN;
   readfds[1].events = POLLIN;
-  writefds[0].events = POLLOUT;
-#endif
 
   while (1)
     {
-#ifdef HAVE_POLL
-      if (poll (readfds, 2, -1) == -1 && errno != EINTR)
-#else
-      FD_ZERO (&readfds);
-      FD_SET (tls_fd, &readfds);
-      FD_SET (in, &readfds);
-      if (select (tls_fd+1, &readfds, NULL, NULL, NULL) == -1
-         && errno != EINTR )
-#endif
+      int ready;
+
+      sigprocmask (SIG_SETMASK, &mask, &orig_mask);
+      ready = poll (readfds, 2, -1);
+      sigprocmask (SIG_SETMASK, &orig_mask, NULL);
+      if (ready == -1 && errno != EINTR)
        {
-         perror ("poll");
+         if (opt_assuan)
+           printf ("ERR 1 poll: %s\r\n", strerror (errno));
          return 1;
        }
-#ifdef HAVE_POLL
       if (readfds[0].revents & POLLIN)
-#else
-      if (FD_ISSET (in, &readfds))
-#endif
        {
          nbuffer = read (in, buffer, sizeof buffer -1);
-
          if (nbuffer == 0)
            goto finish;
-         for (retry = buffer; nbuffer > 0; nbuffer -= wrote, retry += wrote)
-           {
-#ifdef HAVE_POLL
-             writefds[0].fd = tls_fd;
-             if (poll (writefds, 1, -1) == -1)
-#else
-             FD_ZERO (&writefds);
-             FD_SET (tls_fd, &writefds);
-             if (select (tls_fd+1, NULL, &writefds, NULL, NULL) == -1)
-#endif
-               {
-                 perror ("poll");
-                 return 1;
-               }
-             wrote = tls_write(tls_fd, retry, nbuffer);
-             if (wrote < 0) goto finish;
-           }
+         redirect (tls_fd, buffer, nbuffer, tls_write);
        }
-#ifdef HAVE_POLL
       if (readfds[1].revents & POLLIN)
-#else
-      if (FD_ISSET (tls_fd, &readfds))
-#endif
-       {
-readtop:
-         nbuffer = tls_read(tls_fd, buffer, sizeof buffer -1);
+       do {
+         nbuffer = tls_read (tls_fd, buffer, sizeof buffer -1);
          if (nbuffer == 0)
            goto finish;
-         for (retry = buffer; nbuffer > 0; nbuffer -= wrote, retry += wrote)
-           {
-#ifdef HAVE_POLL
-             writefds[0].fd = out;
-             if (poll (writefds, 1, -1) == -1)
-#else
-             FD_ZERO (&writefds);
-             FD_SET (out, &writefds);
-             if (select (out+1, NULL, &writefds, NULL, NULL) == -1)
-#endif
-               {
-                 perror ("poll");
-                 return 1;
-               }
-             wrote = write (out, retry, nbuffer);
-             if (wrote < 0) goto finish;
-           }
-         if (tls_pending())
-           goto readtop;
-       }
+         redirect (out, buffer, nbuffer, write);
+       } while (tls_pending ());
     }
 
  finish: