1
/* http.c - HTTP protocol handler
2
* Copyright (C) 1999, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
4
* This file is part of GnuPG.
6
* GnuPG is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* GnuPG is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
33
#include <sys/types.h>
34
#include <sys/socket.h>
37
#include <netinet/in.h>
38
#include <arpa/inet.h>
49
#define sock_close(a) closesocket(a)
51
#define sock_close(a) close(a)
54
#define MAX_LINELEN 20000 /* max. length of a HTTP line */
55
#define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
56
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
58
"!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
61
#define EAGAIN EWOULDBLOCK
64
static int parse_uri( PARSED_URI *ret_uri, const char *uri );
65
static void release_parsed_uri( PARSED_URI uri );
66
static int do_parse_uri( PARSED_URI uri, int only_local_part );
67
static int remove_escapes( byte *string );
68
static int insert_escapes( byte *buffer, const byte *string,
69
const byte *special );
70
static URI_TUPLE parse_tuple( byte *string );
71
static int send_request( HTTP_HD hd, const char *proxy );
72
static byte *build_rel_path( PARSED_URI uri );
73
static int parse_response( HTTP_HD hd );
75
static int connect_server( const char *server, ushort port, unsigned int flags,
77
static int write_server( int sock, const char *data, size_t length );
89
static int initialized;
90
static WSADATA wsdata;
95
if( WSAStartup( 0x0101, &wsdata ) ) {
96
log_error ("error initializing socket library: ec=%d\n",
97
(int)WSAGetLastError () );
100
if( wsdata.wVersion < 0x0001 ) {
101
log_error ("socket library version is %x.%x - but 1.1 needed\n",
102
LOBYTE(wsdata.wVersion), HIBYTE(wsdata.wVersion));
106
atexit ( deinit_sockets );
111
static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
112
"abcdefghijklmnopqrstuvwxyz"
116
* create a radix64 encoded string.
119
/* TODO: This is a duplicate of code in g10/armor.c. Better to use a
120
single copy in strgutil.c */
122
make_radix64_string( const byte *data, size_t len )
126
buffer = p = m_alloc( (len+2)/3*4 + 1 );
127
for( ; len >= 3 ; len -= 3, data += 3 ) {
128
*p++ = bintoasc[(data[0] >> 2) & 077];
129
*p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
130
*p++ = bintoasc[(((data[1]<<2)&074)|((data[2]>>6)&03))&077];
131
*p++ = bintoasc[data[2]&077];
134
*p++ = bintoasc[(data[0] >> 2) & 077];
135
*p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
136
*p++ = bintoasc[((data[1]<<2)&074)];
138
else if( len == 1 ) {
139
*p++ = bintoasc[(data[0] >> 2) & 077];
140
*p++ = bintoasc[(data[0] <<4)&060];
147
http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url,
148
unsigned int flags, const char *proxy )
152
if( !(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST) )
153
return G10ERR_INV_ARG;
155
/* initialize the handle */
156
memset( hd, 0, sizeof *hd );
159
hd->req_type = reqtype;
162
rc = parse_uri( &hd->uri, url );
164
rc = send_request( hd, proxy );
166
hd->fp_write = iobuf_sockopen( hd->sock , "w" );
173
if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
174
sock_close( hd->sock );
175
iobuf_close( hd->fp_read );
176
iobuf_close( hd->fp_write);
177
release_parsed_uri( hd->uri );
185
http_start_data( HTTP_HD hd )
187
iobuf_flush ( hd->fp_write );
189
write_server (hd->sock, "\r\n", 2);
196
http_wait_response( HTTP_HD hd, unsigned int *ret_status )
200
http_start_data( hd ); /* make sure that we are in the data */
203
hd->sock = dup( hd->sock );
205
return G10ERR_GENERAL;
207
iobuf_ioctl (hd->fp_write, 1, 1, NULL); /* keep the socket open */
208
iobuf_close (hd->fp_write);
210
if ( !(hd->flags & HTTP_FLAG_NO_SHUTDOWN) )
211
shutdown( hd->sock, 1 );
214
hd->fp_read = iobuf_sockopen( hd->sock , "r" );
216
return G10ERR_GENERAL;
218
rc = parse_response( hd );
219
if( !rc && ret_status )
220
*ret_status = hd->status_code;
227
http_open_document( HTTP_HD hd, const char *document,
228
unsigned int flags, const char *proxy )
232
rc = http_open( hd, HTTP_REQ_GET, document, flags, proxy );
236
rc = http_wait_response( hd, NULL );
245
http_close( HTTP_HD hd )
247
if( !hd || !hd->initialized )
249
if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
250
sock_close( hd->sock );
251
iobuf_close( hd->fp_read );
252
iobuf_close( hd->fp_write );
253
release_parsed_uri( hd->uri );
254
m_free( hd->buffer );
261
* Parse an URI and put the result into the newly allocated ret_uri.
262
* The caller must always use release_parsed_uri to releases the
263
* resources (even on an error).
266
parse_uri( PARSED_URI *ret_uri, const char *uri )
268
*ret_uri = m_alloc_clear( sizeof(**ret_uri) + strlen(uri) );
269
strcpy( (*ret_uri)->buffer, uri );
270
return do_parse_uri( *ret_uri, 0 );
274
release_parsed_uri( PARSED_URI uri )
280
for( r = uri->query; r; r = r2 ) {
289
do_parse_uri( PARSED_URI uri, int only_local_part )
296
n = strlen( uri->buffer );
297
/* initialize all fields to an empty string or an empty list */
298
uri->scheme = uri->host = uri->path = p + n;
300
uri->params = uri->query = NULL;
302
/* a quick validity check */
303
if( strspn( p, VALID_URI_CHARS) != n )
304
return G10ERR_BAD_URI; /* invalid characters found */
306
if( !only_local_part ) {
307
/* find the scheme */
308
if( !(p2 = strchr( p, ':' ) ) || p2 == p )
309
return G10ERR_BAD_URI; /* No scheme */
313
if(strcmp(uri->scheme,"http")==0)
315
else if(strcmp(uri->scheme,"hkp")==0)
318
return G10ERR_INVALID_URI; /* Unsupported scheme */
322
/* find the hostname */
324
return G10ERR_INVALID_URI; /* does not start with a slash */
327
if( *p == '/' ) { /* there seems to be a hostname */
329
if( (p2 = strchr(p, '/')) )
332
/* Check for username/password encoding */
333
if((p3=strchr(p,'@')))
342
if( (p3=strchr( p, ':' )) ) {
344
uri->port = atoi( p3 );
348
if( (n = remove_escapes( uri->host )) < 0 )
349
return G10ERR_BAD_URI;
350
if( n != strlen( p ) )
351
return G10ERR_BAD_URI; /* hostname with a Nul in it */
354
} /* end global URI part */
356
/* parse the pathname part */
357
if( !p || !*p ) /* we don't have a path */
358
return 0; /* and this is okay */
360
/* todo: here we have to check params */
362
/* do we have a query part */
363
if( (p2 = strchr( p, '?' )) )
367
if( (n = remove_escapes( p )) < 0 )
368
return G10ERR_BAD_URI;
369
if( n != strlen( p ) )
370
return G10ERR_BAD_URI; /* path with a Nul in it */
373
if( !p || !*p ) /* we don't have a query string */
376
/* now parse the query string */
381
if( (p2 = strchr( p, '&' )) )
383
if( !(elem = parse_tuple( p )) )
384
return G10ERR_BAD_URI;
399
* Remove all %xx escapes; this is done inplace.
400
* Returns: new length of the string.
403
remove_escapes( byte *string )
408
for(p=s=string; *s ; s++ ) {
410
if( s[1] && s[2] && isxdigit(s[1]) && isxdigit(s[2]) ) {
412
*p = *s >= '0' && *s <= '9' ? *s - '0' :
413
*s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
416
*p |= *s >= '0' && *s <= '9' ? *s - '0' :
417
*s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
429
return -1; /* bad URI */
438
*p = 0; /* always keep a string terminator */
444
insert_escapes( byte *buffer, const byte *string, const byte *special )
448
for( ; *string; string++ ) {
449
if( strchr( VALID_URI_CHARS, *string )
450
&& !strchr( special, *string ) ) {
457
sprintf( buffer, "%%%02X", *string );
468
parse_tuple( byte *string )
475
if( (p2 = strchr( p, '=' )) )
477
if( (n = remove_escapes( p )) < 0 )
478
return NULL; /* bad URI */
479
if( n != strlen( p ) )
480
return NULL; /* name with a Nul in it */
481
tuple = m_alloc_clear( sizeof *tuple );
484
/* we have only the name, so we assume an empty value string */
485
tuple->value = p + strlen(p);
488
else { /* name and value */
489
if( (n = remove_escapes( p2 )) < 0 ) {
491
return NULL; /* bad URI */
501
* Send a HTTP request to the server
502
* Returns 0 if the request was successful
505
send_request( HTTP_HD hd, const char *proxy )
513
server = *hd->uri->host? hd->uri->host : "localhost";
514
port = hd->uri->port? hd->uri->port : 80;
520
rc = parse_uri( &uri, proxy );
523
log_error("invalid HTTP proxy (%s): %s\n",proxy,g10_errstr(rc));
524
release_parsed_uri( uri );
525
return G10ERR_NETWORK;
527
hd->sock = connect_server( *uri->host? uri->host : "localhost",
528
uri->port? uri->port : 80, 0, NULL );
531
char *x=make_radix64_string(uri->auth,strlen(uri->auth));
532
auth=m_alloc(50+strlen(x));
533
sprintf(auth,"Proxy-Authorization: Basic %s\r\n",x);
537
release_parsed_uri( uri );
541
hd->sock = connect_server( server, port, hd->flags, hd->uri->scheme );
544
char *x=make_radix64_string(hd->uri->auth,strlen(hd->uri->auth));
545
auth=m_alloc(50+strlen(x));
546
sprintf(auth,"Authorization: Basic %s\r\n",x);
552
return G10ERR_NETWORK;
554
p = build_rel_path( hd->uri );
556
request=m_alloc(strlen(server)*2 + strlen(p) + (auth?strlen(auth):0) + 65);
558
sprintf( request, "%s http://%s:%hu%s%s HTTP/1.0\r\n%s",
559
hd->req_type == HTTP_REQ_GET ? "GET" :
560
hd->req_type == HTTP_REQ_HEAD? "HEAD":
561
hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
562
server, port, *p == '/'? "":"/", p, auth?auth:"" );
568
sprintf(portstr,":%u",port);
570
sprintf( request, "%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s",
571
hd->req_type == HTTP_REQ_GET ? "GET" :
572
hd->req_type == HTTP_REQ_HEAD? "HEAD":
573
hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
574
*p == '/'? "":"/", p, server, (port!=80)?portstr:"",
580
rc = write_server( hd->sock, request, strlen(request) );
589
* Build the relative path from the parsed URI.
590
* Minimal implementation.
593
build_rel_path( PARSED_URI uri )
599
/* count the needed space */
600
n = insert_escapes( NULL, uri->path, "%;?&" );
601
/* todo: build params */
602
for( r=uri->query; r; r = r->next ) {
604
n += insert_escapes( NULL, r->name, "%;?&=" );
606
n += insert_escapes( NULL, r->value, "%;?&=" );
610
/* now allocate and copy */
611
p = rel_path = m_alloc( n );
612
n = insert_escapes( p, uri->path, "%;?&" );
614
/* todo: add params */
615
for( r=uri->query; r; r = r->next ) {
616
*p++ = r == uri->query? '?':'&';
617
n = insert_escapes( p, r->name, "%;?&=" );
620
/* todo: use valuelen */
621
n = insert_escapes( p, r->value, "%;?&=" );
630
/***********************
631
* Parse the response from a server.
632
* Returns: errorcode and sets some fileds in the handle
635
parse_response( HTTP_HD hd )
638
unsigned maxlen, len;
640
/* Wait for the status line */
642
maxlen = MAX_LINELEN;
643
len = iobuf_read_line( hd->fp_read, &hd->buffer,
644
&hd->buffer_size, &maxlen );
647
return -1; /* line has been truncated */
652
if( (p = strchr( line, '/')) )
654
if( !p || strcmp( line, "HTTP" ) )
655
return 0; /* assume http 0.9 */
657
if( (p2 = strpbrk( p, " \t" ) ) ) {
659
p2 += strspn( p2, " \t" );
662
return 0; /* assume http 0.9 */
664
/* todo: add HTTP version number check here */
665
if( (p2 = strpbrk( p, " \t" ) ) )
667
if( !isdigit(p[0]) || !isdigit(p[1]) || !isdigit(p[2]) || p[3] ) {
668
/* malformed HTTP statuscode - assume HTTP 0.9 */
670
hd->status_code = 200;
673
hd->status_code = atoi( p );
675
/* skip all the header lines and wait for the empty line */
677
maxlen = MAX_LINELEN;
678
len = iobuf_read_line( hd->fp_read, &hd->buffer,
679
&hd->buffer_size, &maxlen );
681
/* we ignore truncated lines */
684
/* time lineendings */
685
if( (*line == '\r' && line[1] == '\n') || *line == '\n' )
687
} while( len && *line );
696
struct sockaddr_in mya;
697
struct sockaddr_in peer;
703
if( (fd=socket(AF_INET,SOCK_STREAM, 0)) == -1 ) {
704
log_error("socket() failed: %s\n", strerror(errno));
708
if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (byte*)&i, sizeof(i) ) )
709
log_info("setsockopt(SO_REUSEADDR) failed: %s\n", strerror(errno) );
711
mya.sin_family=AF_INET;
712
memset(&mya.sin_addr, 0, sizeof(mya.sin_addr));
713
mya.sin_port=htons(11371);
715
if( bind( fd, (struct sockaddr *)&mya, sizeof(mya)) ) {
716
log_error("bind to port 11371 failed: %s\n", strerror(errno) );
721
if( listen( fd, 5 ) ) {
722
log_error("listen failed: %s\n", strerror(errno) );
731
if( select( fd+1, &rfds, NULL, NULL, NULL) <= 0 )
732
continue; /* ignore any errors */
734
if( !FD_ISSET( fd, &rfds ) )
737
addrlen = sizeof peer;
738
client = accept( fd, (struct sockaddr *)&peer, &addrlen);
742
log_info("connect from %s\n", inet_ntoa( peer.sin_addr ) );
750
fp = fdopen( client , "r" );
751
while( (c=getc(fp)) != EOF )
756
sock_close( client );
766
connect_server( const char *server, ushort port, unsigned int flags,
769
int sock=-1,srv,srvcount=0,connected=0,hostfound=0;
770
struct srventry *srvlist=NULL;
773
unsigned long inaddr;
776
/* Win32 gethostbyname doesn't handle IP addresses internally, so we
777
try inet_addr first on that platform only. */
778
if((inaddr=inet_addr(server))!=SOCKET_ERROR)
780
struct sockaddr_in addr;
782
memset(&addr,0,sizeof(addr));
784
if((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET)
786
log_error("error creating socket: ec=%d\n",(int)WSAGetLastError());
790
addr.sin_family=AF_INET;
791
addr.sin_port=htons(port);
792
memcpy(&addr.sin_addr,&inaddr,sizeof(inaddr));
794
if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
805
/* Do the SRV thing */
806
if(flags&HTTP_FLAG_TRY_SRV && srvtag)
808
/* We're using SRV, so append the tags */
809
if(1+strlen(srvtag)+6+strlen(server)+1<=MAXDNAME)
811
char srvname[MAXDNAME];
814
strcat(srvname,srvtag);
815
strcat(srvname,"._tcp.");
816
strcat(srvname,server);
817
srvcount=getsrv(srvname,&srvlist);
824
/* Either we're not using SRV, or the SRV lookup failed. Make
825
up a fake SRV record. */
826
srvlist=m_alloc_clear(sizeof(struct srventry));
828
strncpy(srvlist->target,server,MAXDNAME);
829
srvlist->target[MAXDNAME-1]='\0';
833
#ifdef HAVE_GETADDRINFO
835
for(srv=0;srv<srvcount;srv++)
837
struct addrinfo hints,*res,*ai;
840
sprintf(portstr,"%u",srvlist[srv].port);
841
memset(&hints,0,sizeof(hints));
842
hints.ai_socktype=SOCK_STREAM;
843
if(getaddrinfo(srvlist[srv].target,portstr,&hints,&res)==0)
848
for(ai=res;ai;ai=ai->ai_next)
850
if((sock=socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol))==-1)
852
log_error("error creating socket: %s\n",strerror(errno));
857
if(connect(sock,ai->ai_addr,ai->ai_addrlen)==0)
870
#else /* !HAVE_GETADDRINFO */
872
for(srv=0;srv<srvcount;srv++)
875
struct hostent *host=NULL;
876
struct sockaddr_in addr;
878
memset(&addr,0,sizeof(addr));
880
if((host=gethostbyname(srvlist[srv].target))==NULL)
883
if((sock=socket(host->h_addrtype,SOCK_STREAM,0))==-1)
885
log_error("error creating socket: %s\n",strerror(errno));
889
addr.sin_family=host->h_addrtype;
890
if(addr.sin_family!=AF_INET)
892
log_error("%s: unknown address family\n",srvlist[srv].target);
896
addr.sin_port=htons(srvlist[srv].port);
898
/* Try all A records until one responds. */
899
while(host->h_addr_list[i])
901
if(host->h_length!=4)
903
log_error("%s: illegal address length\n",srvlist[srv].target);
907
memcpy(&addr.sin_addr,host->h_addr_list[i],host->h_length);
909
if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
918
if(host->h_addr_list[i])
921
#endif /* !HAVE_GETADDRINFO */
929
log_error("%s: Unable to connect: ec=%d\n",server,(int)WSAGetLastError());
931
log_error("%s: Host not found: ec=%d\n",server,(int)WSAGetLastError());
934
log_error("%s: %s\n",server,strerror(errno));
936
log_error("%s: Host not found\n",server);
948
write_server( int sock, const char *data, size_t length )
957
nwritten = send (sock, data, nleft, 0);
958
if ( nwritten == SOCKET_ERROR ) {
959
log_info ("write failed: ec=%d\n", (int)WSAGetLastError ());
960
return G10ERR_NETWORK;
963
int nwritten = write( sock, data, nleft );
964
if( nwritten == -1 ) {
967
if( errno == EAGAIN ) {
972
select(0, NULL, NULL, NULL, &tv);
975
log_info("write failed: %s\n", strerror(errno));
976
return G10ERR_NETWORK;
986
/**** Test code ****/
990
main(int argc, char **argv)
995
struct http_context hd;
998
log_set_name("http-test");
1005
fprintf(stderr,"usage: http-test uri\n");
1010
rc = parse_uri( &uri, *argv );
1012
log_error("`%s': %s\n", *argv, g10_errstr(rc));
1013
release_parsed_uri( uri );
1017
printf("Scheme: %s\n", uri->scheme );
1018
printf("Host : %s\n", uri->host );
1019
printf("Port : %u\n", uri->port );
1020
printf("Path : %s\n", uri->path );
1021
for( r=uri->params; r; r = r->next ) {
1022
printf("Params: %s=%s", r->name, r->value );
1023
if( strlen( r->value ) != r->valuelen )
1024
printf(" [real length=%d]", (int)r->valuelen );
1027
for( r=uri->query; r; r = r->next ) {
1028
printf("Query : %s=%s", r->name, r->value );
1029
if( strlen( r->value ) != r->valuelen )
1030
printf(" [real length=%d]", (int)r->valuelen );
1033
release_parsed_uri( uri ); uri = NULL;
1035
rc = http_open_document( &hd, *argv, 0, NULL );
1037
log_error("can't get `%s': %s\n", *argv, g10_errstr(rc));
1040
log_info("open_http_document succeeded; status=%u\n", hd.status_code );
1041
while( (c=iobuf_get( hd.fp_read)) != -1 )