/* http.c - HTTP protocol handler
- * Copyright (C) 1999 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,
* 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>
#include <ctype.h>
#include <errno.h>
-#ifndef HAVE_DOSISH_SYSTEM
-
+#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>
+#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" \
"!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
#ifndef EAGAIN
- #define EAGAIN EWOULDBLOCK
+#define EAGAIN EWOULDBLOCK
#endif
static int parse_uri( PARSED_URI *ret_uri, const char *uri );
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( flags || !(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST) )
+ if( !(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST) )
return G10ERR_INV_ARG;
/* initialize the handle */
hd->sock = -1;
hd->initialized = 1;
hd->req_type = reqtype;
+ hd->flags = flags;
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 = 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 );
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;
}
}
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 G10ERR_GENERAL;
- iobuf_close( hd->fp_write );
+#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 G10ERR_GENERAL;
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;
- if( flags )
- return G10ERR_INV_ARG;
-
- rc = http_open( hd, HTTP_REQ_GET, document, 0 );
+ rc = http_open(hd, HTTP_REQ_GET, document, auth, flags, proxy, srv,
+ headers );
if( rc )
return rc;
}
-
-
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;
}
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 );
}
for( r = uri->query; r; r = r2 ) {
r2 = r->next;
- gcry_free( r );
+ xfree( r );
}
- gcry_free( uri );
+ xfree( uri );
}
}
*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 G10ERR_INVALID_URI; /* Unsupported scheme */
+ return G10ERR_INVALID_URI; /* Unsupported scheme */
p = p2;
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 G10ERR_BAD_URI;
- if( n != strlen( p ) )
+ if( n != strlen( uri->host ) )
return G10ERR_BAD_URI; /* hostname with a Nul in it */
p = p2 ? p2 : NULL;
}
}
else {
if( buffer ) {
- sprintf( buffer, "%02X", *string );
+ sprintf( buffer, "%%%02X", *string );
buffer += 3;
}
n += 3;
}
-
-
-
static URI_TUPLE
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 */
}
else { /* name and value */
if( (n = remove_escapes( p2 )) < 0 ) {
- gcry_free( tuple );
+ xfree( tuple );
return NULL; /* bad URI */
}
tuple->value = p2;
* 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;
+ char *proxy_authstr=NULL,*authstr=NULL;
server = *hd->uri->host? hd->uri->host : "localhost";
port = hd->uri->port? hd->uri->port : 80;
- hd->sock = connect_server( server, port );
+ if(proxy && *proxy)
+ {
+ PARSED_URI uri;
+
+ rc = parse_uri( &uri, proxy );
+ if (rc)
+ {
+ log_error("invalid HTTP proxy (%s): %s\n",proxy,g10_errstr(rc));
+ release_parsed_uri( uri );
+ return G10ERR_NETWORK;
+ }
+ hd->sock = connect_server( *uri->host? uri->host : "localhost",
+ 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->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 G10ERR_NETWORK;
p = build_rel_path( hd->uri );
- request = gcry_xmalloc( strlen(p) + 20 );
- 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;
+}
/****************
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 */
return 0;
}
-#if 0
+#ifdef TEST
static int
-start_server()
+start_server(void)
{
struct sockaddr_in mya;
struct sockaddr_in peer;
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;
}
fclose(fp);
exit(0);
}
- close( client );
+ sock_close( client );
}
#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));
- addr.sin_addr = *(struct in_addr*)host->h_addr;
+ if((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET)
+ {
+ log_error("error creating socket: ec=%d\n",(int)WSAGetLastError());
+ return -1;
+ }
- sd = socket(AF_INET, SOCK_STREAM, 0);
- if( sd == -1 )
- return -1;
+ addr.sin_family=AF_INET;
+ addr.sin_port=htons(port);
+ memcpy(&addr.sin_addr,&inaddr,sizeof(inaddr));
+
+ 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;
log_info("write failed: %s\n", strerror(errno));
return G10ERR_NETWORK;
}
+#endif
nleft -=nwritten;
data += nwritten;
}
return 0;
}
-#endif /* HAVE_DOSISH_SYSTEM */
-
/**** Test code ****/
#ifdef TEST
}
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, g10_errstr(rc));
return 1;