Obsolete option --no-sig-create-check.
[gnupg.git] / util / http.c
index 81bf91d..2f630ae 100644 (file)
@@ -1,11 +1,12 @@
 /* http.c  -  HTTP protocol handler
- *     Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+ * Copyright (C) 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+ *               2009, 2012 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -14,8 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
@@ -26,8 +26,9 @@
 #include <ctype.h>
 #include <errno.h>
 
-#ifndef HAVE_DOSISH_SYSTEM  /* fixme: add support W32 sockets */
-
+#ifdef _WIN32
+#include <windows.h>
+#else
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
-
-#include <gcrypt.h>
+#endif
 
 #include "util.h"
 #include "iobuf.h"
 #include "i18n.h"
-
 #include "http.h"
+#include "srv.h"
+
+#ifdef _WIN32
+#define sock_close(a)  closesocket(a)
+#else
+#define sock_close(a)  close(a)
+#endif
 
 #define MAX_LINELEN 20000  /* max. length of a HTTP line */
 #define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz"   \
@@ -52,7 +58,7 @@
                        "!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
 
 #ifndef EAGAIN
-  #define EAGAIN  EWOULDBLOCK
+#define EAGAIN  EWOULDBLOCK
 #endif
 
 static int parse_uri( PARSED_URI *ret_uri, const char *uri );
@@ -62,22 +68,94 @@ static int remove_escapes( byte *string );
 static int insert_escapes( byte *buffer, const byte *string,
                                         const byte *special );
 static URI_TUPLE parse_tuple( byte *string );
-static int send_request( HTTP_HD hd );
+static int send_request( HTTP_HD hd, const char *auth, const char *proxy,
+                        struct http_srv *srv, STRLIST headers);
 static byte *build_rel_path( PARSED_URI uri );
 static int parse_response( HTTP_HD hd );
 
-static int connect_server( const char *server, ushort port );
+static int connect_server( const char *server, ushort port, unsigned int flags,
+                          struct http_srv *srv );
 static int write_server( int sock, const char *data, size_t length );
 
+#ifdef _WIN32
+static void
+deinit_sockets (void)
+{
+    WSACleanup();
+}
+
+static void
+init_sockets (void)
+{
+    static int initialized;
+    static WSADATA wsdata;
+
+    if (initialized)
+        return;
+
+    if( WSAStartup( 0x0101, &wsdata ) ) {
+        log_error ("error initializing socket library: ec=%d\n",
+                    (int)WSAGetLastError () );
+        return;
+    }
+    if( wsdata.wVersion < 0x0001 ) {
+        log_error ("socket library version is %x.%x - but 1.1 needed\n",
+                   LOBYTE(wsdata.wVersion), HIBYTE(wsdata.wVersion));
+        WSACleanup();
+        return;
+    }
+    atexit ( deinit_sockets );
+    initialized = 1;
+}
+#endif /*_WIN32*/
+
+static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                        "abcdefghijklmnopqrstuvwxyz"
+                        "0123456789+/";
+
+/****************
+ * create a radix64 encoded string.
+ */
+
+/* TODO: This is a duplicate of code in g10/armor.c modified to do the
+   "=" padding.  Better to use a single copy in strgutil.c ? */
+static char *
+make_radix64_string( const byte *data, size_t len )
+{
+    char *buffer, *p;
+
+    buffer = p = xmalloc( (len+2)/3*4 + 1 );
+    for( ; len >= 3 ; len -= 3, data += 3 ) {
+       *p++ = bintoasc[(data[0] >> 2) & 077];
+       *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
+       *p++ = bintoasc[(((data[1]<<2)&074)|((data[2]>>6)&03))&077];
+       *p++ = bintoasc[data[2]&077];
+    }
+    if( len == 2 ) {
+       *p++ = bintoasc[(data[0] >> 2) & 077];
+       *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
+       *p++ = bintoasc[((data[1]<<2)&074)];
+       *p++ = '=';
+    }
+    else if( len == 1 ) {
+       *p++ = bintoasc[(data[0] >> 2) & 077];
+       *p++ = bintoasc[(data[0] <<4)&060];
+       *p++ = '=';
+       *p++ = '=';
+    }
+    *p = 0;
+    return buffer;
+}
 
 int
 http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url,
-                                             unsigned int flags )
+          char *auth, unsigned int flags, const char *proxy,
+          struct http_srv *srv, STRLIST headers )
 {
     int rc;
 
     if( !(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST) )
-       return GPGERR_INV_ARG;
+       return G10ERR_INV_ARG;
 
     /* initialize the handle */
     memset( hd, 0, sizeof *hd );
@@ -88,17 +166,17 @@ http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url,
 
     rc = parse_uri( &hd->uri, url );
     if( !rc ) {
-       rc = send_request( hd );
+        rc = send_request( hd, auth, proxy, srv, headers );
        if( !rc ) {
-           hd->fp_write = iobuf_fdopen( hd->sock , "w" );
+           hd->fp_write = iobuf_sockopen( hd->sock , "w" );
            if( hd->fp_write )
                return 0;
-           rc = GPGERR_GENERAL;
+           rc = G10ERR_GENERAL;
        }
     }
 
     if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
-       close( hd->sock );
+       sock_close( hd->sock );
     iobuf_close( hd->fp_read );
     iobuf_close( hd->fp_write);
     release_parsed_uri( hd->uri );
@@ -111,8 +189,9 @@ http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url,
 void
 http_start_data( HTTP_HD hd )
 {
+    iobuf_flush ( hd->fp_write );
     if( !hd->in_data ) {
-       iobuf_put( hd->fp_write, '\n' );
+        write_server (hd->sock, "\r\n", 2);
        hd->in_data = 1;
     }
 }
@@ -124,19 +203,26 @@ http_wait_response( HTTP_HD hd, unsigned int *ret_status )
     int rc;
 
     http_start_data( hd ); /* make sure that we are in the data */
-    iobuf_flush( hd->fp_write );
 
+#if 0
     hd->sock = dup( hd->sock );
     if( hd->sock == -1 )
-       return GPGERR_GENERAL;
-    iobuf_close( hd->fp_write );
+       return G10ERR_GENERAL;
+#endif
+    iobuf_ioctl (hd->fp_write, 1, 1, NULL); /* keep the socket open */
+    iobuf_close (hd->fp_write);
     hd->fp_write = NULL;
-    shutdown( hd->sock, 1 );
+    /* We do not want the shutdown code anymore.  It used to be there
+       to support old versions of pksd.  These versions are anyway
+       unusable and the latest releases haven been fixed to properly
+       handle HTTP 1.0. */
+    /* if ( !(hd->flags & HTTP_FLAG_NO_SHUTDOWN) ) */
+    /*     shutdown( hd->sock, 1 ); */
     hd->in_data = 0;
 
-    hd->fp_read = iobuf_fdopen( hd->sock , "r" );
+    hd->fp_read = iobuf_sockopen( hd->sock , "r" );
     if( !hd->fp_read )
-       return GPGERR_GENERAL;
+       return G10ERR_GENERAL;
 
     rc = parse_response( hd );
     if( !rc && ret_status )
@@ -147,11 +233,14 @@ http_wait_response( HTTP_HD hd, unsigned int *ret_status )
 
 
 int
-http_open_document( HTTP_HD hd, const char *document, unsigned int flags )
+http_open_document( HTTP_HD hd, const char *document, char *auth,
+                   unsigned int flags, const char *proxy, struct http_srv *srv,
+                   STRLIST headers )
 {
     int rc;
 
-    rc = http_open( hd, HTTP_REQ_GET, document, flags );
+    rc = http_open(hd, HTTP_REQ_GET, document, auth, flags, proxy, srv,
+                  headers );
     if( rc )
        return rc;
 
@@ -163,19 +252,17 @@ http_open_document( HTTP_HD hd, const char *document, unsigned int flags )
 }
 
 
-
-
 void
 http_close( HTTP_HD hd )
 {
     if( !hd || !hd->initialized )
        return;
     if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
-       close( hd->sock );
+       sock_close( hd->sock );
     iobuf_close( hd->fp_read );
     iobuf_close( hd->fp_write );
     release_parsed_uri( hd->uri );
-    gcry_free( hd->buffer );
+    xfree( hd->buffer );
     hd->initialized = 0;
 }
 
@@ -189,7 +276,7 @@ http_close( HTTP_HD hd )
 static int
 parse_uri( PARSED_URI *ret_uri, const char *uri )
 {
-   *ret_uri = gcry_xcalloc( 1, sizeof(**ret_uri) + strlen(uri) );
+   *ret_uri = xmalloc_clear( sizeof(**ret_uri) + strlen(uri) );
    strcpy( (*ret_uri)->buffer, uri );
    return do_parse_uri( *ret_uri, 0 );
 }
@@ -203,9 +290,9 @@ release_parsed_uri( PARSED_URI uri )
 
        for( r = uri->query; r; r = r2 ) {
            r2 = r->next;
-           gcry_free( r );
+           xfree( r );
        }
-       gcry_free( uri );
+       xfree( uri );
     }
 }
 
@@ -225,46 +312,63 @@ do_parse_uri( PARSED_URI uri, int only_local_part )
 
     /* a quick validity check */
     if( strspn( p, VALID_URI_CHARS) != n )
-       return GPGERR_BAD_URI; /* invalid characters found */
+       return G10ERR_BAD_URI; /* invalid characters found */
 
     if( !only_local_part ) {
        /* find the scheme */
        if( !(p2 = strchr( p, ':' ) ) || p2 == p )
-          return GPGERR_BAD_URI; /* No scheme */
+          return G10ERR_BAD_URI; /* No scheme */
        *p2++ = 0;
        strlwr( p );
        uri->scheme = p;
-       if( !strcmp( uri->scheme, "http" ) )
-           ;
-       else if( !strcmp( uri->scheme, "x-hkp" ) ) /* same as HTTP */
-           ;
+       if(strcmp(uri->scheme,"http")==0)
+         uri->port = 80;
        else
-           return GPGERR_INVALID_URI; /* Unsupported scheme */
+         return G10ERR_INVALID_URI; /* Unsupported scheme */
 
        p = p2;
 
        /* find the hostname */
        if( *p != '/' )
-           return GPGERR_INVALID_URI; /* does not start with a slash */
+           return G10ERR_INVALID_URI; /* does not start with a slash */
 
        p++;
        if( *p == '/' ) {  /* there seems to be a hostname */
            p++;
            if( (p2 = strchr(p, '/')) )
                *p2++ = 0;
+
+           /* Check for username/password encoding */
+           if((p3=strchr(p,'@')))
+             {
+               uri->auth=p;
+               *p3++='\0';
+               p=p3;
+             }
+
            strlwr( p );
-           uri->host = p;
-           if( (p3=strchr( p, ':' )) ) {
-               *p3++ = 0;
-               uri->port = atoi( p3 );
-           }
+
+           /* Handle a host of [IP] so that [IP:V6]:port works */
+           if( *p == '[' && (p3=strchr( p, ']' )) )
+             {
+               *p3++ = '\0';
+               /* worst case, uri->host should have length 0, points to \0 */
+               uri->host = p + 1;
+               p = p3;
+             }
            else
-              uri->port = 80;
-           uri->host = p;
+             uri->host = p;
+
+           if( (p3=strchr( p, ':' )) )
+             {
+               *p3++ = '\0';
+               uri->port = atoi( p3 );
+             }
+
            if( (n = remove_escapes( uri->host )) < 0 )
-               return GPGERR_BAD_URI;
-           if( n != strlen( p ) )
-               return GPGERR_BAD_URI; /* hostname with a Nul in it */
+               return G10ERR_BAD_URI;
+           if( n != strlen( uri->host ) )
+               return G10ERR_BAD_URI; /* hostname with a Nul in it */
            p = p2 ? p2 : NULL;
        }
     } /* end global URI part */
@@ -281,9 +385,9 @@ do_parse_uri( PARSED_URI uri, int only_local_part )
 
     uri->path = p;
     if( (n = remove_escapes( p )) < 0 )
-       return GPGERR_BAD_URI;
+       return G10ERR_BAD_URI;
     if( n != strlen( p ) )
-       return GPGERR_BAD_URI; /* path with a Nul in it */
+       return G10ERR_BAD_URI; /* path with a Nul in it */
     p = p2 ? p2 : NULL;
 
     if( !p || !*p ) /* we don't have a query string */
@@ -297,7 +401,7 @@ do_parse_uri( PARSED_URI uri, int only_local_part )
        if( (p2 = strchr( p, '&' )) )
            *p2++ = 0;
        if( !(elem = parse_tuple( p )) )
-           return GPGERR_BAD_URI;
+           return G10ERR_BAD_URI;
        *tail = elem;
        tail = &elem->next;
 
@@ -370,7 +474,7 @@ insert_escapes( byte *buffer, const byte *string, const byte *special )
        }
        else {
            if( buffer ) {
-               sprintf( buffer, "%02X", *string );
+               sprintf( buffer, "%%%02X", *string );
                buffer += 3;
            }
            n += 3;
@@ -380,9 +484,6 @@ insert_escapes( byte *buffer, const byte *string, const byte *special )
 }
 
 
-
-
-
 static URI_TUPLE
 parse_tuple( byte *string )
 {
@@ -397,7 +498,7 @@ parse_tuple( byte *string )
        return NULL; /* bad URI */
     if( n != strlen( p ) )
        return NULL; /* name with a Nul in it */
-    tuple = gcry_xcalloc( 1, sizeof *tuple );
+    tuple = xmalloc_clear( sizeof *tuple );
     tuple->name = p;
     if( !p2 )  {
        /* we have only the name, so we assume an empty value string */
@@ -406,7 +507,7 @@ parse_tuple( byte *string )
     }
     else { /* name and value */
        if( (n = remove_escapes( p2 )) < 0 ) {
-           gcry_free( tuple );
+           xfree( tuple );
            return NULL; /* bad URI */
        }
        tuple->value = p2;
@@ -421,62 +522,120 @@ parse_tuple( byte *string )
  * Returns 0 if the request was successful
  */
 static int
-send_request( HTTP_HD hd )
+send_request( HTTP_HD hd, const char *auth, const char *proxy,
+             struct http_srv *srv, STRLIST headers )
 {
     const byte *server;
     byte *request, *p;
     ushort port;
     int rc;
-    const char *http_proxy = NULL;
+    char *proxy_authstr=NULL,*authstr=NULL;
 
     server = *hd->uri->host? hd->uri->host : "localhost";
     port   = hd->uri->port?  hd->uri->port : 80;
 
-    if( (hd->flags & HTTP_FLAG_TRY_PROXY)
-       && (http_proxy = getenv( "http_proxy" )) ) {
+    if(proxy && *proxy)
+      {
        PARSED_URI uri;
 
-       rc = parse_uri( &uri, http_proxy );
-       if (rc) {
-           log_error("invalid $http_proxy: %s\n", gpg_errstr(rc));
+       rc = parse_uri( &uri, proxy );
+       if (rc)
+         {
+           log_error("invalid HTTP proxy (%s): %s\n",proxy,g10_errstr(rc));
            release_parsed_uri( uri );
-           return GPGERR_NETWORK;
-       }
+           return G10ERR_NETWORK;
+         }
        hd->sock = connect_server( *uri->host? uri->host : "localhost",
-                                   uri->port? uri->port : 80 );
+                                  uri->port? uri->port : 80, 0, srv );
+       if(uri->auth)
+         {
+           char *x;
+           remove_escapes(uri->auth);
+           x=make_radix64_string(uri->auth,strlen(uri->auth));
+           proxy_authstr=xmalloc(52+strlen(x));
+           sprintf(proxy_authstr,"Proxy-Authorization: Basic %s\r\n",x);
+           xfree(x);
+         }
+
        release_parsed_uri( uri );
-    }
+      }
     else
-       hd->sock = connect_server( server, port );
+      hd->sock = connect_server( server, port, hd->flags, srv );
+
+    if(auth || hd->uri->auth)
+      {
+       char *x,*tempauth=NULL;
+
+       if(auth)
+         {
+           tempauth=xstrdup(auth);
+           remove_escapes(tempauth);
+         }
+       else if(hd->uri->auth)
+         remove_escapes(hd->uri->auth);
+
+       x=make_radix64_string(tempauth?tempauth:hd->uri->auth,
+                             strlen(tempauth?tempauth:hd->uri->auth));
+       authstr=xmalloc(52+strlen(x));
+       sprintf(authstr,"Authorization: Basic %s\r\n",x);
+       xfree(x);
+       xfree(tempauth);
+      }
 
     if( hd->sock == -1 )
-       return GPGERR_NETWORK;
+       return G10ERR_NETWORK;
 
     p = build_rel_path( hd->uri );
-    request = gcry_xmalloc( strlen(p) + 20 );
-    if( http_proxy ) {
-       sprintf( request, "%s http://%s:%hu%s%s HTTP/1.0\r\n",
-                         hd->req_type == HTTP_REQ_GET ? "GET" :
-                         hd->req_type == HTTP_REQ_HEAD? "HEAD":
-                         hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
-                         server, port,  *p == '/'? "":"/", p );
-    }
-    else {
-       sprintf( request, "%s %s%s HTTP/1.0\r\n",
-                         hd->req_type == HTTP_REQ_GET ? "GET" :
-                         hd->req_type == HTTP_REQ_HEAD? "HEAD":
-                         hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
-                                                 *p == '/'? "":"/", p );
-    }
-    gcry_free(p);
+
+    request=xmalloc(strlen(server)*2 + strlen(p)
+                   + (authstr?strlen(authstr):0)
+                   + (proxy_authstr?strlen(proxy_authstr):0) + 65);
+    if( proxy && *proxy )
+      sprintf( request, "%s http://%s:%hu%s%s HTTP/1.0\r\n%s%s",
+              hd->req_type == HTTP_REQ_GET ? "GET" :
+              hd->req_type == HTTP_REQ_HEAD? "HEAD":
+              hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
+              server, port,  *p == '/'? "":"/", p,
+              authstr?authstr:"",proxy_authstr?proxy_authstr:"" );
+    else
+      {
+       char portstr[35];
+
+       if(port == 80 || (srv && srv->used_server))
+         *portstr = 0;
+       else
+         sprintf(portstr,":%u",port);
+
+       sprintf( request, "%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s",
+                hd->req_type == HTTP_REQ_GET ? "GET" :
+                hd->req_type == HTTP_REQ_HEAD? "HEAD":
+                hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
+                *p == '/'? "":"/", p, server, portstr,
+                authstr?authstr:"");
+      }
+
+    xfree(p);
 
     rc = write_server( hd->sock, request, strlen(request) );
-    gcry_free( request );
 
-    return rc;
-}
+    if(rc==0)
+      for(;headers;headers=headers->next)
+       {
+         rc = write_server( hd->sock, headers->d, strlen(headers->d) );
+         if(rc)
+           break;
+
+         rc = write_server( hd->sock, "\r\n", 2 );
+         if(rc)
+           break;
+       }
 
+    xfree( request );
+    xfree(proxy_authstr);
+    xfree(authstr);
 
+    return rc;
+}
 
 
 /****************
@@ -502,7 +661,7 @@ build_rel_path( PARSED_URI uri )
     n++;
 
     /* now  allocate and copy */
-    p = rel_path = gcry_xmalloc( n );
+    p = rel_path = xmalloc( n );
     n = insert_escapes( p, uri->path, "%;?&" );
     p += n;
     /* todo: add params */
@@ -583,9 +742,9 @@ parse_response( HTTP_HD hd )
     return 0;
 }
 
-#if 0
+#ifdef TEST
 static int
-start_server()
+start_server(void)
 {
     struct sockaddr_in mya;
     struct sockaddr_in peer;
@@ -608,13 +767,13 @@ start_server()
 
     if( bind( fd, (struct sockaddr *)&mya, sizeof(mya)) ) {
        log_error("bind to port 11371 failed: %s\n", strerror(errno) );
-       close( fd );
+       sock_close( fd );
        return -1;
     }
 
     if( listen( fd, 5 ) ) {
        log_error("listen failed: %s\n", strerror(errno) );
-       close( fd );
+       sock_close( fd );
        return -1;
     }
 
@@ -647,7 +806,7 @@ start_server()
            fclose(fp);
            exit(0);
        }
-       close( client );
+       sock_close( client );
     }
 
 
@@ -656,43 +815,230 @@ start_server()
 #endif
 
 
-
 static int
-connect_server( const char *server, ushort port )
+connect_server( const char *server, ushort port, unsigned int flags,
+               struct http_srv *srv )
 {
-    struct sockaddr_in addr;
-    struct hostent *host;
-    int sd;
+  int sock = -1;
+  int srvcount = 0;
+  int connected = 0;
+  int hostfound = 0;
+  int chosen = -1;
+  int fakesrv = 0;
+  struct srventry *srvlist = NULL;
+  int srvindex;
+
+#ifdef _WIN32
+  unsigned long inaddr;
+
+  init_sockets();
+  /* Win32 gethostbyname doesn't handle IP addresses internally, so we
+     try inet_addr first on that platform only. */
+  if((inaddr=inet_addr(server))!=INADDR_NONE)
+    {
+      struct sockaddr_in addr;
 
-    addr.sin_family = AF_INET;
-    addr.sin_port = htons(port);
-    host = gethostbyname((char*)server);
-    if( !host )
-        return -1;
+      memset(&addr,0,sizeof(addr));
+
+      if((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET)
+       {
+         log_error("error creating socket: ec=%d\n",(int)WSAGetLastError());
+         return -1;
+       }
 
-    addr.sin_addr = *(struct in_addr*)host->h_addr;
+      addr.sin_family=AF_INET;
+      addr.sin_port=htons(port);
+      memcpy(&addr.sin_addr,&inaddr,sizeof(inaddr));
 
-    sd = socket(AF_INET, SOCK_STREAM, 0);
-    if( sd == -1 )
-       return -1;
+      if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
+       return sock;
+      else
+       {
+         sock_close(sock);
+         return -1;
+       }
+    }
+#endif
+
+#ifdef USE_DNS_SRV
+  /* Do the SRV thing */
+  if(srv && srv->srvtag)
+    {
+      /* We're using SRV, so append the tags */
+      if(1+strlen(srv->srvtag)+6+strlen(server)+1<=MAXDNAME)
+       {
+         char srvname[MAXDNAME];
+
+         strcpy(srvname,"_");
+         strcat(srvname,srv->srvtag);
+         strcat(srvname,"._tcp.");
+         strcat(srvname,server);
+         srvcount=getsrv(srvname,&srvlist);
+       }
+    }
+#endif
 
-    if( connect( sd, (struct sockaddr *)&addr, sizeof addr) == -1 ) {
-       close(sd);
+  if(srvlist==NULL)
+    {
+      /* Either we're not using SRV, or the SRV lookup failed.  Make
+        up a fake SRV record. */
+      srvlist=calloc(1,sizeof(struct srventry));
+      if(!srvlist)
        return -1;
+      srvlist->port=port;
+      strncpy(srvlist->target,server,MAXDNAME);
+      srvlist->target[MAXDNAME-1]='\0';
+      srvcount = 1;
+      fakesrv = 1;
+    }
+
+#ifdef HAVE_GETADDRINFO
+
+  for(srvindex=0;srvindex<srvcount;srvindex++)
+    {
+      struct addrinfo hints,*res,*ai;
+      char portstr[6];
+
+      sprintf(portstr,"%u",srvlist[srvindex].port);
+      memset(&hints,0,sizeof(hints));
+      hints.ai_socktype=SOCK_STREAM;
+      if(getaddrinfo(srvlist[srvindex].target,portstr,&hints,&res)==0)
+       hostfound=1;
+      else
+       continue;
+
+      for(ai=res;ai;ai=ai->ai_next)
+       {
+         if((sock=socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol))==-1)
+           {
+             log_error("error creating socket: %s\n",strerror(errno));
+             freeaddrinfo(res);
+             return -1;
+           }
+
+         if(connect(sock,ai->ai_addr,ai->ai_addrlen)==0)
+           {
+             connected=1;
+             chosen = srvindex;
+             break;
+           }
+
+         sock_close(sock);
+       }
+
+      freeaddrinfo(res);
+
+      if(ai)
+       break;
+    }
+
+#else /* !HAVE_GETADDRINFO */
+
+  for(srvindex=0; srvindex < srvcount; srvindex++)
+    {
+      int i=0;
+      struct hostent *host=NULL;
+      struct sockaddr_in addr;
+
+      memset(&addr,0,sizeof(addr));
+
+      if((host=gethostbyname(srvlist[srvindex].target))==NULL)
+       continue;
+
+      hostfound=1;
+
+      if((sock=socket(host->h_addrtype,SOCK_STREAM,0))==-1)
+       {
+         log_error("error creating socket: %s\n",strerror(errno));
+         return -1;
+       }
+
+      addr.sin_family=host->h_addrtype;
+      if(addr.sin_family!=AF_INET)
+       {
+         log_error("%s: unknown address family\n",srvlist[srvindex].target);
+         return -1;
+       }
+
+      addr.sin_port=htons(srvlist[srvindex].port);
+
+      /* Try all A records until one responds. */
+      while(host->h_addr_list[i])
+       {
+         if(host->h_length!=4)
+           {
+             log_error("%s: illegal address length\n",srvlist[srvindex].target);
+             return -1;
+           }
+
+         memcpy(&addr.sin_addr,host->h_addr_list[i],host->h_length);
+
+         if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
+           {
+             connected=1;
+             chosen = srvindex;
+             break;
+           }
+
+         i++;
+       }
+
+      if(host->h_addr_list[i])
+       break;
+
+      sock_close(sock);
+    }
+#endif /* !HAVE_GETADDRINFO */
+
+  if(!fakesrv && chosen > -1 && srv)
+    {
+      srv->used_server = strdup (srvlist[chosen].target);
+      srv->used_port = srvlist[chosen].port;
+    }
+
+  free(srvlist);
+
+  if(!connected)
+    {
+      int err=errno;
+#ifdef _WIN32
+      if(hostfound)
+       log_error("%s: Unable to connect: ec=%d\n",server,(int)WSAGetLastError());
+      else
+       log_error("%s: Host not found: ec=%d\n",server,(int)WSAGetLastError());
+#else
+      if(hostfound)
+       log_error("%s: %s\n",server,strerror(err));
+      else
+       log_error("%s: Host not found\n",server);
+#endif
+      if(sock!=-1)
+       sock_close(sock);
+      errno=err;
+      return -1;
     }
 
-    return sd;
+  return sock;
 }
 
 
 static int
 write_server( int sock, const char *data, size_t length )
 {
-    int nleft, nwritten;
+    int nleft;
 
     nleft = length;
     while( nleft > 0 ) {
-       nwritten = write( sock, data, nleft );
+#ifdef _WIN32
+        int nwritten;
+
+        nwritten = send (sock, data, nleft, 0);
+        if ( nwritten == SOCKET_ERROR ) {
+           log_info ("write failed: ec=%d\n", (int)WSAGetLastError ());
+           return G10ERR_NETWORK;
+        }
+#else
+       int nwritten = write( sock, data, nleft );
        if( nwritten == -1 ) {
            if( errno == EINTR )
                continue;
@@ -705,8 +1051,9 @@ write_server( int sock, const char *data, size_t length )
                continue;
            }
            log_info("write failed: %s\n", strerror(errno));
-           return GPGERR_NETWORK;
+           return G10ERR_NETWORK;
        }
+#endif
        nleft -=nwritten;
        data += nwritten;
     }
@@ -714,8 +1061,6 @@ write_server( int sock, const char *data, size_t length )
     return 0;
 }
 
-#endif /* HAVE_DOSISH_SYSTEM */
-
 /**** Test code ****/
 #ifdef TEST
 
@@ -742,7 +1087,7 @@ main(int argc, char **argv)
 
     rc = parse_uri( &uri, *argv );
     if( rc ) {
-       log_error("`%s': %s\n", *argv, gpg_errstr(rc));
+       log_error("`%s': %s\n", *argv, g10_errstr(rc));
        release_parsed_uri( uri );
        return 1;
     }
@@ -765,9 +1110,9 @@ main(int argc, char **argv)
     }
     release_parsed_uri( uri ); uri = NULL;
 
-    rc = http_open_document( &hd, *argv, 0 );
+    rc = http_open_document( &hd, *argv, NULL, 0, NULL, NULL, NULL );
     if( rc ) {
-       log_error("can't get `%s': %s\n", *argv, gpg_errstr(rc));
+       log_error("can't get `%s': %s\n", *argv, g10_errstr(rc));
        return 1;
     }
     log_info("open_http_document succeeded; status=%u\n", hd.status_code );