[Pkg-gnupg-commit] [libassuan] 03/11: Support hostname based SOCKS5 connection.

Eric Dorland eric at moszumanska.debian.org
Thu Nov 12 16:14:47 UTC 2015


This is an automated email from the git hooks/post-receive script.

eric pushed a commit to branch master
in repository libassuan.

commit 4061ac57ca84a1e0ed779096897a160d49b50c03
Author: Werner Koch <wk at gnupg.org>
Date:   Mon Oct 26 14:35:41 2015 +0100

    Support hostname based SOCKS5 connection.
    
    * src/assuan.h.in (ASSUAN_SOCK_SOCKS): New.
    (ASSUAN_SOCK_TOR): New.
    (assuan_sock_connect_byname): New.
    * src/libassuan.def, src/libassuan.vers: Add that function.
    
    * src/assuan-socket.c (socks5_connect): Add args socksport,
    credentials, hostname, and hostport.  Implement user/password
    authentication and domainname address type.  Change callers
    accordingly.
    (_assuan_sock_connect_byname): New.
    (assuan_sock_connect_byname): New.
    
    * tests/socks5.c (main): Add options --byname, --user, and --pass.
    --
    
    The assuan_sock_connect_byname may eventually be extended to work
    without Tor or SOCKS by using getaddrinfo.  Or we move that all to
    libgpgrt (aka libgpg-error).
    
    Signed-off-by: Werner Koch <wk at gnupg.org>
---
 NEWS                |  14 +++-
 doc/assuan.texi     |  16 +++++
 src/assuan-socket.c | 177 +++++++++++++++++++++++++++++++++++++++++++++-----
 src/assuan.h.in     |  12 ++++
 src/libassuan.def   |   1 +
 src/libassuan.vers  |   1 +
 tests/socks5.c      | 183 ++++++++++++++++++++++++++++++++++------------------
 7 files changed, 324 insertions(+), 80 deletions(-)

diff --git a/NEWS b/NEWS
index 934feda..01fa3b6 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,18 @@
-Noteworthy changes in version 2.3.1 (unreleased) [C6/A6/R_]
+Noteworthy changes in version 2.4.0 (unreleased) [C6/A6/R_]
 ------------------------------------------------
 
+ * New flags "socks" and "tor-mode" for assuan_sock_{set,get}_flag.
+
+ * New function assuan_sock_connect_byname.
+
+ * Interface changes relative to the 2.3.0 release:
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ assuan_sock_connect_byname    NEW.
+ ASSUAN_SOCK_TOR               NEW.
+ ASSUAN_SOCK_SOCKS             NEW.
+ assuan_sock_set_flag          EXTENDED.
+ assuan_sock_get_flag          EXTENDED.
+
 
 Noteworthy changes in version 2.3.0 (2015-08-28) [C6/A6/R0]
 ------------------------------------------------
diff --git a/doc/assuan.texi b/doc/assuan.texi
index 9161f0b..8445eb4 100644
--- a/doc/assuan.texi
+++ b/doc/assuan.texi
@@ -1979,6 +1979,22 @@ details on the redirection file format.
 @end deftypefun
 
 
+ at deftypefun int assuan_sock_connect_byname (@w{const char * @var{host}}, @
+         @w{unsigned short @var{port}}, @
+         @w{int @var{reserved}}, @
+         @w{const char *@var{credentials}}, @
+         @w{unsigned int @var{flags}})
+
+Directly connect to @var{port} on @var{host} given as a name.  The
+current implementation requires that @var{flags} has either
+ at code{ASSUAN_SOCK_SOCKS} or @code{ASSUAN_SOCK_TOR} set.  On success a
+new TCP STREAM socket is returned; on error @code{ASSUAN_INVALID_FD}
+and ERRNO set.  If @var{credentials} is not @code{NULL}, it is a
+string used for password based SOCKS authentication.  Username and
+password are separated by a colon. @var{reserved} should be 0.
+ at end deftypefun
+
+
 @deftypefun int assuan_sock_bind ( @
         @w{assuan_fd_t @var{sockfd}}, @
         @w{struct sockaddr *@var{addr}}, @
diff --git a/src/assuan-socket.c b/src/assuan-socket.c
index 9a6ee66..12e9e38 100644
--- a/src/assuan-socket.c
+++ b/src/assuan-socket.c
@@ -657,9 +657,11 @@ do_writen (assuan_context_t ctx, assuan_fd_t sockfd,
 }
 
 
-/* Connect using the SOCKS5 protocol.  */
+/* Connect using the SOCKS5 protocol. */
 static int
-socks5_connect (assuan_context_t ctx, int sock,
+socks5_connect (assuan_context_t ctx, int sock, unsigned short socksport,
+                const char *credentials,
+                const char *hostname, unsigned short hostport,
                 struct sockaddr *addr, socklen_t length)
 {
   int ret;
@@ -669,16 +671,36 @@ socks5_connect (assuan_context_t ctx, int sock,
   size_t proxyaddrlen;
   struct sockaddr_in6 *addr_in6;
   struct sockaddr_in  *addr_in;
-  unsigned char buffer[22];
-  size_t buflen;
+  unsigned char buffer[22+512]; /* The extra 512 gives enough space
+                                   for username/password or the
+                                   hostname. */
+  size_t buflen, hostnamelen;
+  int method;
 
   /* memset (&proxyaddr_in6, 0, sizeof proxyaddr_in6); */
   memset (&proxyaddr_in, 0, sizeof proxyaddr_in);
 
+  /* Either HOSTNAME or ADDR may be given.  */
+  if (hostname && addr)
+    {
+      gpg_err_set_errno (EINVAL);
+      return -1;
+    }
+
+  /* If a hostname is given it must fit into our buffer and it must be
+     less than 256 so that its length can be encoded in one byte.  */
+  hostnamelen = hostname? strlen (hostname) : 0;
+  if (hostnamelen > 255)
+    {
+      gpg_err_set_errno (ENAMETOOLONG);
+      return -1;
+    }
+
   /* Connect to local host.  */
-  /* Fixme: First try to use IPv6.  */
+  /* Fixme: First try to use IPv6 but note that
+     _assuan_sock_connect_byname created the socket with AF_INET.  */
   proxyaddr_in.sin_family = AF_INET;
-  proxyaddr_in.sin_port = htons (tor_mode);
+  proxyaddr_in.sin_port = htons (socksport);
   proxyaddr_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
   proxyaddr = (struct sockaddr *)&proxyaddr_in;
   proxyaddrlen = sizeof proxyaddr_in;
@@ -687,7 +709,11 @@ socks5_connect (assuan_context_t ctx, int sock,
     return ret;
   buffer[0] = 5; /* RFC-1928 VER field.  */
   buffer[1] = 1; /* NMETHODS */
-  buffer[2] = 0; /* Method: No authentication required. */
+  if (credentials)
+    method = 2; /* Method: username/password authentication. */
+  else
+    method = 0; /* Method: No authentication required. */
+  buffer[2] = method;
 
   /* Negotiate method.  */
   ret = do_writen (ctx, sock, buffer, 3);
@@ -696,7 +722,7 @@ socks5_connect (assuan_context_t ctx, int sock,
   ret = do_readn (ctx, sock, buffer, 2);
   if (ret)
     return ret;
-  if (buffer[0] != 5 || buffer[1] != 0 )
+  if (buffer[0] != 5 || buffer[1] != method )
     {
       /* Socks server returned wrong version or does not support our
          requested method.  */
@@ -704,11 +730,70 @@ socks5_connect (assuan_context_t ctx, int sock,
       return -1;
     }
 
+  if (credentials)
+    {
+      const char *password;
+      int ulen, plen;
+
+      password = strchr (credentials, ':');
+      if (!password)
+        {
+          gpg_err_set_errno (EINVAL); /* No password given.  */
+          return -1;
+        }
+      ulen = password - credentials;
+      password++;
+      plen = strlen (password);
+      if (!ulen || ulen > 255 || !plen || plen > 255)
+        {
+          gpg_err_set_errno (EINVAL);
+          return -1;
+        }
+
+      buffer[0] = 1; /* VER of the sub-negotiation. */
+      buffer[1] = ulen;
+      buflen = 2;
+      memcpy (buffer+buflen, credentials, ulen);
+      buflen += ulen;
+      buffer[buflen++] = plen;
+      memcpy (buffer+buflen, password, plen);
+      buflen += plen;
+      ret = do_writen (ctx, sock, buffer, buflen);
+      wipememory (buffer, buflen);
+      if (ret)
+        return ret;
+      ret = do_readn (ctx, sock, buffer, 2);
+      if (ret)
+        return ret;
+      if (buffer[0] != 1)
+        {
+          /* SOCKS server returned wrong version.  */
+          gpg_err_set_errno (EPROTO);
+          return -1;
+        }
+      if (buffer[1])
+        {
+          /* SOCKS server denied access.  */
+          gpg_err_set_errno (EACCES);
+          return -1;
+        }
+    }
+
   /* Send request details (rfc-1928, 4).  */
   buffer[0] = 5; /* VER  */
   buffer[1] = 1; /* CMD = CONNECT  */
   buffer[2] = 0; /* RSV  */
-  if (addr->sa_family == AF_INET6)
+  if (hostname)
+    {
+      buffer[3] = 3; /* ATYP = DOMAINNAME */
+      buflen = 4;
+      buffer[buflen++] = hostnamelen;
+      memcpy (buffer+buflen, hostname, hostnamelen);
+      buflen += hostnamelen;
+      buffer[buflen++] = (hostport >> 8); /* DST.PORT */
+      buffer[buflen++] = hostport;
+    }
+  else if (addr->sa_family == AF_INET6)
     {
       addr_in6 = (struct sockaddr_in6 *)addr;
 
@@ -729,7 +814,7 @@ socks5_connect (assuan_context_t ctx, int sock,
   ret = do_writen (ctx, sock, buffer, buflen);
   if (ret)
     return ret;
-  ret = do_readn (ctx, sock, buffer, buflen);
+  ret = do_readn (ctx, sock, buffer, 10 /* Length for IPv4 */);
   if (ret)
     return ret;
   if (buffer[0] != 5 || buffer[2] != 0 )
@@ -743,10 +828,10 @@ socks5_connect (assuan_context_t ctx, int sock,
     {
       switch (buffer[1])
         {
-        case 0x01: /* general SOCKS server failure.  */
+        case 0x01: /* General SOCKS server failure.  */
           gpg_err_set_errno (ENETDOWN);
           break;
-        case 0x02: /* connection not allowed by ruleset.  */
+        case 0x02: /* Connection not allowed by ruleset.  */
           gpg_err_set_errno (EACCES);
           break;
         case 0x03: /* Network unreachable */
@@ -770,6 +855,15 @@ socks5_connect (assuan_context_t ctx, int sock,
         }
       return -1;
     }
+  if (buffer[3] == 4)
+    {
+      /* ATYP indicates a v6 address.  We need to read the remaining
+         12 bytes.  */
+      ret = do_readn (ctx, sock, buffer+10, 12);
+      if (ret)
+        return ret;
+    }
+
   /* FIXME: We have not way to store the actual address used by the
      server.  */
 
@@ -779,7 +873,7 @@ socks5_connect (assuan_context_t ctx, int sock,
 
 
 /* Return true if SOCKS shall be used.  This is the case if tor_mode
-   is enabled and and the desired address is not the loopback
+   is enabled and the desired address is not the loopback
    address.  */
 static int
 use_socks (struct sockaddr *addr)
@@ -874,7 +968,8 @@ _assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd,
     }
   else if (use_socks (addr))
     {
-      return socks5_connect (ctx, HANDLE2SOCKET (sockfd), addr, addrlen);
+      return socks5_connect (ctx, HANDLE2SOCKET (sockfd), tor_mode,
+                             NULL, NULL, 0, addr, addrlen);
     }
   else
     {
@@ -916,7 +1011,8 @@ _assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd,
 
   if (use_socks (addr))
     {
-      return socks5_connect (ctx, sockfd, addr, addrlen);
+      return socks5_connect (ctx, sockfd, tor_mode,
+                             NULL, NULL, 0, addr, addrlen);
     }
   else
     {
@@ -926,6 +1022,48 @@ _assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd,
 }
 
 
+/* Connect to HOST specified as host name on PORT.  The current
+   implementation requires that either the flags ASSUAN_SOCK_SOCKS or
+   ASSUAN_SOCK_TOR are give in FLAGS.  On success a new socket is
+   returned; on error ASSUAN_INVALID_FD is returned and ERRNO set.  If
+   CREDENTIALS is not NULL, it is a string used for password based
+   authentication.  Username and password are separated by a
+   colon.  RESERVED must be 0. */
+assuan_fd_t
+_assuan_sock_connect_byname (assuan_context_t ctx, const char *host,
+                             unsigned short port, int reserved,
+                             const char *credentials, unsigned int flags)
+{
+  int fd;
+  unsigned short socksport;
+
+  if ((flags & ASSUAN_SOCK_TOR))
+    socksport = TOR_PORT;
+  else if ((flags & ASSUAN_SOCK_SOCKS))
+    socksport = SOCKS_PORT;
+  else
+    {
+      gpg_err_set_errno (ENOTSUP);
+      return ASSUAN_INVALID_FD;
+    }
+
+  fd = _assuan_sock_new (ctx, AF_INET, SOCK_STREAM, 0);
+  if (fd == ASSUAN_INVALID_FD)
+    return fd;
+
+  if (socks5_connect (ctx, fd, socksport,
+                      credentials, host, port, NULL, 0))
+    {
+      int save_errno = errno;
+      assuan_sock_close (fd);
+      gpg_err_set_errno (save_errno);
+      return -1;
+    }
+
+  return fd;
+}
+
+
 int
 _assuan_sock_bind (assuan_context_t ctx, assuan_fd_t sockfd,
 		   struct sockaddr *addr, int addrlen)
@@ -1257,6 +1395,15 @@ assuan_sock_connect (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen)
   return _assuan_sock_connect (sock_ctx, sockfd, addr, addrlen);
 }
 
+assuan_fd_t
+assuan_sock_connect_byname (const char *host, unsigned short port,
+                            int reserved, const char *credentials,
+                            unsigned int flags)
+{
+  return _assuan_sock_connect_byname (sock_ctx,
+                                      host, port, reserved, credentials, flags);
+}
+
 int
 assuan_sock_bind (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen)
 {
diff --git a/src/assuan.h.in b/src/assuan.h.in
index b26fa3b..67a1c20 100644
--- a/src/assuan.h.in
+++ b/src/assuan.h.in
@@ -461,6 +461,14 @@ gpg_error_t assuan_set_error (assuan_context_t ctx, gpg_error_t err,
 
 /*-- assuan-socket.c --*/
 
+/* This flag is used with assuan_sock_connect_byname to
+   connect via SOCKS.  */
+#define ASSUAN_SOCK_SOCKS   1
+/* This flag is used with assuan_sock_connect_byname to force a
+   connection via Tor even if the socket subsystem has not been
+   swicthed into Tor mode.  This flags overrides ASSUAN_SOCK_SOCKS. */
+#define ASSUAN_SOCK_TOR     2
+
 /* These are socket wrapper functions to support an emulation of Unix
    domain sockets on Windows W32.  */
 gpg_error_t assuan_sock_init (void);
@@ -471,6 +479,10 @@ int assuan_sock_set_flag (assuan_fd_t sockfd, const char *name, int value);
 int assuan_sock_get_flag (assuan_fd_t sockfd, const char *name, int *r_value);
 int assuan_sock_connect (assuan_fd_t sockfd,
                          struct sockaddr *addr, int addrlen);
+assuan_fd_t assuan_sock_connect_byname (const char *host, unsigned short port,
+                                        int reserved,
+                                        const char *credentials,
+                                        unsigned int flags);
 int assuan_sock_bind (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen);
 int assuan_sock_set_sockaddr_un (const char *fname, struct sockaddr *addr,
                                  int *r_redirected);
diff --git a/src/libassuan.def b/src/libassuan.def
index 9f31c31..c320151 100644
--- a/src/libassuan.def
+++ b/src/libassuan.def
@@ -114,6 +114,7 @@ EXPORTS
     assuan_sock_set_sockaddr_un         @93
     assuan_sock_set_flag                @94
     assuan_sock_get_flag                @95
+    assuan_sock_connect_byname          @96
 
 ; END
 
diff --git a/src/libassuan.vers b/src/libassuan.vers
index 2b2389d..37c0131 100644
--- a/src/libassuan.vers
+++ b/src/libassuan.vers
@@ -104,6 +104,7 @@ LIBASSUAN_1.0 {
     assuan_sock_set_sockaddr_un;
     assuan_sock_set_flag;
     assuan_sock_get_flag;
+    assuan_sock_connect_byname;
 
     __assuan_close;
     __assuan_pipe;
diff --git a/tests/socks5.c b/tests/socks5.c
index c179108..7aa2b71 100644
--- a/tests/socks5.c
+++ b/tests/socks5.c
@@ -63,6 +63,9 @@ main (int argc, char **argv)
   int only_v4 = 0;
   int use_tor = 0;
   int disable_socks = 0;
+  int opt_byname = 0;
+  const char *user = NULL;
+  const char *pass = NULL;
   assuan_fd_t sock = ASSUAN_INVALID_FD;
   estream_t infp, outfp;
   int c;
@@ -83,15 +86,18 @@ main (int argc, char **argv)
       else if (!strcmp (*argv, "--help"))
         {
           puts (
-"usage: ./socks5 [options] HOST PORT\n"
-"\n"
-"Options:\n"
-"  --verbose        Show what is going on\n"
-"  --use-tor        Use port 9050 instead of 1080\n"
-"  --inet6-only     Use only IPv6\n"
-"  --inet4-only     Use only IPv4\n"
-"  --disable-socks  Connect w/o SOCKS\n"
-);
+                "usage: ./socks5 [options] HOST PORT\n"
+                "\n"
+                "Options:\n"
+                "  --verbose        Show what is going on\n"
+                "  --use-tor        Use port 9050 instead of 1080\n"
+                "  --inet6-only     Use only IPv6\n"
+                "  --inet4-only     Use only IPv4\n"
+                "  --disable-socks  Connect w/o SOCKS\n"
+                "  --byname         Use assuan_sock_connect_byname\n"
+                "  --user STRING    Use STRING as user for authentication\n"
+                "  --pass STRING    Use STRING as password for authentication\n"
+                );
           exit (0);
         }
       if (!strcmp (*argv, "--verbose"))
@@ -124,6 +130,29 @@ main (int argc, char **argv)
           disable_socks = 1;
           argc--; argv++;
         }
+      else if (!strcmp (*argv, "--byname"))
+        {
+          opt_byname = 1;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--user"))
+        {
+          argc--; argv++;
+          if (argc)
+            {
+              user = *argv;
+              argc--; argv++;
+            }
+        }
+      else if (!strcmp (*argv, "--pass"))
+        {
+          argc--; argv++;
+          if (argc)
+            {
+              pass = *argv;
+              argc--; argv++;
+            }
+        }
       else if (!strncmp (*argv, "--", 2))
         {
           log_error ("unknown option '%s'\n", *argv);
@@ -153,60 +182,86 @@ main (int argc, char **argv)
                  use_tor? "TOR": "SOCKS", gpg_strerror (err));
     }
 
-  {
-    struct addrinfo hints, *res, *ai;
-    int ret;
-    int anyok = 0;
-
-    memset (&hints, 0, sizeof (hints));
-    hints.ai_socktype = SOCK_STREAM;
-    ret = getaddrinfo (argv[0], argv[1], &hints, &res);
-    if (ret)
-      {
-        log_error ("error resolving '%s': %s\n", argv[0], gai_strerror (ret));
+  if (opt_byname)
+    {
+      unsigned short port;
+      char *cred;
+
+      if (user || pass)
+        cred = xstrconcat (user?user:"", ":", pass, NULL);
+      else
+        cred = NULL;
+
+      port = strtoul (argv[1], NULL, 10);
+      if (port < 0 || port > 65535)
+        log_fatal ("port number out of range\n");
+
+      sock = assuan_sock_connect_byname (argv[0], port, 0, cred,
+                                         ASSUAN_SOCK_TOR);
+      if (sock == ASSUAN_INVALID_FD)
+        {
+          err = gpg_error_from_syserror ();
+          log_error ("assuan_sock_connect_byname (%s) failed: %s\n",
+                     argv[0], gpg_strerror (err));
+          exit (1);
+        }
+      xfree (cred);
+    }
+  else
+    {
+      struct addrinfo hints, *res, *ai;
+      int ret;
+      int anyok = 0;
+
+      memset (&hints, 0, sizeof (hints));
+      hints.ai_socktype = SOCK_STREAM;
+      ret = getaddrinfo (argv[0], argv[1], &hints, &res);
+      if (ret)
+        {
+          log_error ("error resolving '%s': %s\n", argv[0], gai_strerror (ret));
+          exit (1);
+        }
+
+      for (ai = res; ai; ai = ai->ai_next)
+        {
+          if (ai->ai_family == AF_INET && only_v6)
+            continue;
+          if (ai->ai_family == AF_INET6 && only_v4)
+            continue;
+
+          if (sock != ASSUAN_INVALID_FD)
+            assuan_sock_close (sock);
+          sock = assuan_sock_new (ai->ai_family, ai->ai_socktype,
+                                  ai->ai_protocol);
+          if (sock == ASSUAN_INVALID_FD)
+            {
+              err = gpg_error_from_syserror ();
+              log_error ("error creating socket: %s\n", gpg_strerror (err));
+              freeaddrinfo (res);
+              exit (1);
+            }
+
+          if (assuan_sock_connect (sock,  ai->ai_addr, ai->ai_addrlen))
+            {
+              err = gpg_error_from_syserror ();
+              log_error ("assuan_sock_connect (%s) failed: %s\n",
+                         ai->ai_family == AF_INET6? "v6" :
+                         ai->ai_family == AF_INET ? "v4" : "?",
+                         gpg_strerror (err));
+            }
+          else
+            {
+              log_info ("assuan_sock_connect succeeded (%s)\n",
+                        ai->ai_family == AF_INET6? "v6" :
+                        ai->ai_family == AF_INET ? "v4" : "?");
+              anyok = 1;
+              break;
+            }
+        }
+      freeaddrinfo (res);
+      if (!anyok)
         exit (1);
-      }
-
-    for (ai = res; ai; ai = ai->ai_next)
-      {
-        if (ai->ai_family == AF_INET && only_v6)
-          continue;
-        if (ai->ai_family == AF_INET6 && only_v4)
-          continue;
-
-        if (sock != ASSUAN_INVALID_FD)
-          assuan_sock_close (sock);
-        sock = assuan_sock_new (ai->ai_family, ai->ai_socktype,
-                                ai->ai_protocol);
-        if (sock == ASSUAN_INVALID_FD)
-          {
-            err = gpg_error_from_syserror ();
-            log_error ("error creating socket: %s\n", gpg_strerror (err));
-            freeaddrinfo (res);
-            exit (1);
-          }
-
-        if (assuan_sock_connect (sock,  ai->ai_addr, ai->ai_addrlen))
-          {
-            err = gpg_error_from_syserror ();
-            log_error ("assuan_sock_connect (%s) failed: %s\n",
-                       ai->ai_family == AF_INET6? "v6" :
-                       ai->ai_family == AF_INET ? "v4" : "?",
-                       gpg_strerror (err));
-          }
-        else
-          {
-            log_info ("assuan_sock_connect succeeded (%d)\n",
-                      ai->ai_family == AF_INET6? "v6" :
-                      ai->ai_family == AF_INET ? "v4" : "?");
-            anyok = 1;
-            break;
-          }
-      }
-    freeaddrinfo (res);
-    if (!anyok)
-      exit (1);
-  }
+    }
 
   infp = es_fdopen_nc (sock, "rb");
   if (!infp)
@@ -220,8 +275,8 @@ main (int argc, char **argv)
     {
       err = gpg_error_from_syserror ();
       es_fclose (infp);
-        assuan_sock_close (sock);
-        log_fatal ("opening outbound stream failed: %s\n", gpg_strerror (err));
+      assuan_sock_close (sock);
+      log_fatal ("opening outbound stream failed: %s\n", gpg_strerror (err));
     }
 
   es_fputs ("HEAD / HTTP/1.0\r\n\r\n", outfp);

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-gnupg/libassuan.git



More information about the Pkg-gnupg-commit mailing list