1 /* http.c - HTTP protocol handler
2 * Copyright (C) 1999, 2001, 2002, 2003, 2004,
3 * 2005 Free Software Foundation, Inc.
5 * This file is part of GnuPG.
7 * GnuPG is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * GnuPG is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
35 #include <sys/types.h>
36 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
51 #define sock_close(a) closesocket(a)
53 #define sock_close(a) close(a)
56 #define MAX_LINELEN 20000 /* max. length of a HTTP line */
57 #define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
58 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
60 "!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
63 #define EAGAIN EWOULDBLOCK
66 static int parse_uri( PARSED_URI *ret_uri, const char *uri );
67 static void release_parsed_uri( PARSED_URI uri );
68 static int do_parse_uri( PARSED_URI uri, int only_local_part );
69 static int remove_escapes( byte *string );
70 static int insert_escapes( byte *buffer, const byte *string,
71 const byte *special );
72 static URI_TUPLE parse_tuple( byte *string );
73 static int send_request( HTTP_HD hd, const char *auth, const char *proxy );
74 static byte *build_rel_path( PARSED_URI uri );
75 static int parse_response( HTTP_HD hd );
77 static int connect_server( const char *server, ushort port, unsigned int flags,
79 static int write_server( int sock, const char *data, size_t length );
91 static int initialized;
92 static WSADATA wsdata;
97 if( WSAStartup( 0x0101, &wsdata ) ) {
98 log_error ("error initializing socket library: ec=%d\n",
99 (int)WSAGetLastError () );
102 if( wsdata.wVersion < 0x0001 ) {
103 log_error ("socket library version is %x.%x - but 1.1 needed\n",
104 LOBYTE(wsdata.wVersion), HIBYTE(wsdata.wVersion));
108 atexit ( deinit_sockets );
113 static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
114 "abcdefghijklmnopqrstuvwxyz"
118 * create a radix64 encoded string.
121 /* TODO: This is a duplicate of code in g10/armor.c modified to do the
122 "=" padding. Better to use a single copy in strgutil.c ? */
124 make_radix64_string( const byte *data, size_t len )
128 buffer = p = xmalloc( (len+2)/3*4 + 1 );
129 for( ; len >= 3 ; len -= 3, data += 3 ) {
130 *p++ = bintoasc[(data[0] >> 2) & 077];
131 *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
132 *p++ = bintoasc[(((data[1]<<2)&074)|((data[2]>>6)&03))&077];
133 *p++ = bintoasc[data[2]&077];
136 *p++ = bintoasc[(data[0] >> 2) & 077];
137 *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
138 *p++ = bintoasc[((data[1]<<2)&074)];
141 else if( len == 1 ) {
142 *p++ = bintoasc[(data[0] >> 2) & 077];
143 *p++ = bintoasc[(data[0] <<4)&060];
152 http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url,
153 char *auth, unsigned int flags, const char *proxy )
157 if( !(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST) )
158 return G10ERR_INV_ARG;
160 /* initialize the handle */
161 memset( hd, 0, sizeof *hd );
164 hd->req_type = reqtype;
167 rc = parse_uri( &hd->uri, url );
169 rc = send_request( hd, auth, proxy );
171 hd->fp_write = iobuf_sockopen( hd->sock , "w" );
178 if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
179 sock_close( hd->sock );
180 iobuf_close( hd->fp_read );
181 iobuf_close( hd->fp_write);
182 release_parsed_uri( hd->uri );
190 http_start_data( HTTP_HD hd )
192 iobuf_flush ( hd->fp_write );
194 write_server (hd->sock, "\r\n", 2);
201 http_wait_response( HTTP_HD hd, unsigned int *ret_status )
205 http_start_data( hd ); /* make sure that we are in the data */
208 hd->sock = dup( hd->sock );
210 return G10ERR_GENERAL;
212 iobuf_ioctl (hd->fp_write, 1, 1, NULL); /* keep the socket open */
213 iobuf_close (hd->fp_write);
215 /* We do not want the shutdown code anymore. It used to be there
216 to support old versions of pksd. These versions are anyway
217 unusable and the latest releases haven been fixed to properly
219 /* if ( !(hd->flags & HTTP_FLAG_NO_SHUTDOWN) ) */
220 /* shutdown( hd->sock, 1 ); */
223 hd->fp_read = iobuf_sockopen( hd->sock , "r" );
225 return G10ERR_GENERAL;
227 rc = parse_response( hd );
228 if( !rc && ret_status )
229 *ret_status = hd->status_code;
236 http_open_document( HTTP_HD hd, const char *document, char *auth,
237 unsigned int flags, const char *proxy )
241 rc = http_open(hd, HTTP_REQ_GET, document, auth, flags, proxy );
245 rc = http_wait_response( hd, NULL );
254 http_close( HTTP_HD hd )
256 if( !hd || !hd->initialized )
258 if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
259 sock_close( hd->sock );
260 iobuf_close( hd->fp_read );
261 iobuf_close( hd->fp_write );
262 release_parsed_uri( hd->uri );
270 * Parse an URI and put the result into the newly allocated ret_uri.
271 * The caller must always use release_parsed_uri to releases the
272 * resources (even on an error).
275 parse_uri( PARSED_URI *ret_uri, const char *uri )
277 *ret_uri = xmalloc_clear( sizeof(**ret_uri) + strlen(uri) );
278 strcpy( (*ret_uri)->buffer, uri );
279 return do_parse_uri( *ret_uri, 0 );
283 release_parsed_uri( PARSED_URI uri )
289 for( r = uri->query; r; r = r2 ) {
298 do_parse_uri( PARSED_URI uri, int only_local_part )
305 n = strlen( uri->buffer );
306 /* initialize all fields to an empty string or an empty list */
307 uri->scheme = uri->host = uri->path = p + n;
309 uri->params = uri->query = NULL;
311 /* a quick validity check */
312 if( strspn( p, VALID_URI_CHARS) != n )
313 return G10ERR_BAD_URI; /* invalid characters found */
315 if( !only_local_part ) {
316 /* find the scheme */
317 if( !(p2 = strchr( p, ':' ) ) || p2 == p )
318 return G10ERR_BAD_URI; /* No scheme */
322 if(strcmp(uri->scheme,"http")==0)
324 else if(strcmp(uri->scheme,"hkp")==0)
327 return G10ERR_INVALID_URI; /* Unsupported scheme */
331 /* find the hostname */
333 return G10ERR_INVALID_URI; /* does not start with a slash */
336 if( *p == '/' ) { /* there seems to be a hostname */
338 if( (p2 = strchr(p, '/')) )
341 /* Check for username/password encoding */
342 if((p3=strchr(p,'@')))
351 if( (p3=strchr( p, ':' )) ) {
353 uri->port = atoi( p3 );
357 if( (n = remove_escapes( uri->host )) < 0 )
358 return G10ERR_BAD_URI;
359 if( n != strlen( p ) )
360 return G10ERR_BAD_URI; /* hostname with a Nul in it */
363 } /* end global URI part */
365 /* parse the pathname part */
366 if( !p || !*p ) /* we don't have a path */
367 return 0; /* and this is okay */
369 /* todo: here we have to check params */
371 /* do we have a query part */
372 if( (p2 = strchr( p, '?' )) )
376 if( (n = remove_escapes( p )) < 0 )
377 return G10ERR_BAD_URI;
378 if( n != strlen( p ) )
379 return G10ERR_BAD_URI; /* path with a Nul in it */
382 if( !p || !*p ) /* we don't have a query string */
385 /* now parse the query string */
390 if( (p2 = strchr( p, '&' )) )
392 if( !(elem = parse_tuple( p )) )
393 return G10ERR_BAD_URI;
408 * Remove all %xx escapes; this is done inplace.
409 * Returns: new length of the string.
412 remove_escapes( byte *string )
417 for(p=s=string; *s ; s++ ) {
419 if( s[1] && s[2] && isxdigit(s[1]) && isxdigit(s[2]) ) {
421 *p = *s >= '0' && *s <= '9' ? *s - '0' :
422 *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
425 *p |= *s >= '0' && *s <= '9' ? *s - '0' :
426 *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
438 return -1; /* bad URI */
447 *p = 0; /* always keep a string terminator */
453 insert_escapes( byte *buffer, const byte *string, const byte *special )
457 for( ; *string; string++ ) {
458 if( strchr( VALID_URI_CHARS, *string )
459 && !strchr( special, *string ) ) {
466 sprintf( buffer, "%%%02X", *string );
477 parse_tuple( byte *string )
484 if( (p2 = strchr( p, '=' )) )
486 if( (n = remove_escapes( p )) < 0 )
487 return NULL; /* bad URI */
488 if( n != strlen( p ) )
489 return NULL; /* name with a Nul in it */
490 tuple = xmalloc_clear( sizeof *tuple );
493 /* we have only the name, so we assume an empty value string */
494 tuple->value = p + strlen(p);
497 else { /* name and value */
498 if( (n = remove_escapes( p2 )) < 0 ) {
500 return NULL; /* bad URI */
510 * Send a HTTP request to the server
511 * Returns 0 if the request was successful
514 send_request( HTTP_HD hd, const char *auth, const char *proxy )
520 char *proxy_authstr=NULL,*authstr=NULL;
522 server = *hd->uri->host? hd->uri->host : "localhost";
523 port = hd->uri->port? hd->uri->port : 80;
529 rc = parse_uri( &uri, proxy );
532 log_error("invalid HTTP proxy (%s): %s\n",proxy,g10_errstr(rc));
533 release_parsed_uri( uri );
534 return G10ERR_NETWORK;
536 hd->sock = connect_server( *uri->host? uri->host : "localhost",
537 uri->port? uri->port : 80, 0, NULL );
541 remove_escapes(uri->auth);
542 x=make_radix64_string(uri->auth,strlen(uri->auth));
543 proxy_authstr=xmalloc(52+strlen(x));
544 sprintf(proxy_authstr,"Proxy-Authorization: Basic %s\r\n",x);
548 release_parsed_uri( uri );
551 hd->sock = connect_server( server, port, hd->flags, hd->uri->scheme );
553 if(auth || hd->uri->auth)
555 char *x,*tempauth=NULL;
559 tempauth=xstrdup(auth);
560 remove_escapes(tempauth);
562 else if(hd->uri->auth)
563 remove_escapes(hd->uri->auth);
565 x=make_radix64_string(tempauth?tempauth:hd->uri->auth,
566 strlen(tempauth?tempauth:hd->uri->auth));
567 authstr=xmalloc(52+strlen(x));
568 sprintf(authstr,"Authorization: Basic %s\r\n",x);
574 return G10ERR_NETWORK;
576 p = build_rel_path( hd->uri );
578 request=xmalloc(strlen(server)*2 + strlen(p)
579 + (authstr?strlen(authstr):0)
580 + (proxy_authstr?strlen(proxy_authstr):0) + 65);
581 if( proxy && *proxy )
582 sprintf( request, "%s http://%s:%hu%s%s HTTP/1.0\r\n%s%s",
583 hd->req_type == HTTP_REQ_GET ? "GET" :
584 hd->req_type == HTTP_REQ_HEAD? "HEAD":
585 hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
586 server, port, *p == '/'? "":"/", p,
587 authstr?authstr:"",proxy_authstr?proxy_authstr:"" );
593 sprintf(portstr,":%u",port);
595 sprintf( request, "%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s",
596 hd->req_type == HTTP_REQ_GET ? "GET" :
597 hd->req_type == HTTP_REQ_HEAD? "HEAD":
598 hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
599 *p == '/'? "":"/", p, server, (port!=80)?portstr:"",
605 rc = write_server( hd->sock, request, strlen(request) );
607 xfree(proxy_authstr);
615 * Build the relative path from the parsed URI.
616 * Minimal implementation.
619 build_rel_path( PARSED_URI uri )
625 /* count the needed space */
626 n = insert_escapes( NULL, uri->path, "%;?&" );
627 /* todo: build params */
628 for( r=uri->query; r; r = r->next ) {
630 n += insert_escapes( NULL, r->name, "%;?&=" );
632 n += insert_escapes( NULL, r->value, "%;?&=" );
636 /* now allocate and copy */
637 p = rel_path = xmalloc( n );
638 n = insert_escapes( p, uri->path, "%;?&" );
640 /* todo: add params */
641 for( r=uri->query; r; r = r->next ) {
642 *p++ = r == uri->query? '?':'&';
643 n = insert_escapes( p, r->name, "%;?&=" );
646 /* todo: use valuelen */
647 n = insert_escapes( p, r->value, "%;?&=" );
656 /***********************
657 * Parse the response from a server.
658 * Returns: errorcode and sets some fileds in the handle
661 parse_response( HTTP_HD hd )
664 unsigned maxlen, len;
666 /* Wait for the status line */
668 maxlen = MAX_LINELEN;
669 len = iobuf_read_line( hd->fp_read, &hd->buffer,
670 &hd->buffer_size, &maxlen );
673 return -1; /* line has been truncated */
678 if( (p = strchr( line, '/')) )
680 if( !p || strcmp( line, "HTTP" ) )
681 return 0; /* assume http 0.9 */
683 if( (p2 = strpbrk( p, " \t" ) ) ) {
685 p2 += strspn( p2, " \t" );
688 return 0; /* assume http 0.9 */
690 /* todo: add HTTP version number check here */
691 if( (p2 = strpbrk( p, " \t" ) ) )
693 if( !isdigit(p[0]) || !isdigit(p[1]) || !isdigit(p[2]) || p[3] ) {
694 /* malformed HTTP statuscode - assume HTTP 0.9 */
696 hd->status_code = 200;
699 hd->status_code = atoi( p );
701 /* skip all the header lines and wait for the empty line */
703 maxlen = MAX_LINELEN;
704 len = iobuf_read_line( hd->fp_read, &hd->buffer,
705 &hd->buffer_size, &maxlen );
707 /* we ignore truncated lines */
710 /* time lineendings */
711 if( (*line == '\r' && line[1] == '\n') || *line == '\n' )
713 } while( len && *line );
722 struct sockaddr_in mya;
723 struct sockaddr_in peer;
729 if( (fd=socket(AF_INET,SOCK_STREAM, 0)) == -1 ) {
730 log_error("socket() failed: %s\n", strerror(errno));
734 if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (byte*)&i, sizeof(i) ) )
735 log_info("setsockopt(SO_REUSEADDR) failed: %s\n", strerror(errno) );
737 mya.sin_family=AF_INET;
738 memset(&mya.sin_addr, 0, sizeof(mya.sin_addr));
739 mya.sin_port=htons(11371);
741 if( bind( fd, (struct sockaddr *)&mya, sizeof(mya)) ) {
742 log_error("bind to port 11371 failed: %s\n", strerror(errno) );
747 if( listen( fd, 5 ) ) {
748 log_error("listen failed: %s\n", strerror(errno) );
757 if( select( fd+1, &rfds, NULL, NULL, NULL) <= 0 )
758 continue; /* ignore any errors */
760 if( !FD_ISSET( fd, &rfds ) )
763 addrlen = sizeof peer;
764 client = accept( fd, (struct sockaddr *)&peer, &addrlen);
768 log_info("connect from %s\n", inet_ntoa( peer.sin_addr ) );
776 fp = fdopen( client , "r" );
777 while( (c=getc(fp)) != EOF )
782 sock_close( client );
792 connect_server( const char *server, ushort port, unsigned int flags,
795 int sock=-1,srv,srvcount=0,connected=0,hostfound=0;
796 struct srventry *srvlist=NULL;
799 unsigned long inaddr;
802 /* Win32 gethostbyname doesn't handle IP addresses internally, so we
803 try inet_addr first on that platform only. */
804 if((inaddr=inet_addr(server))!=INADDR_NONE)
806 struct sockaddr_in addr;
808 memset(&addr,0,sizeof(addr));
810 if((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET)
812 log_error("error creating socket: ec=%d\n",(int)WSAGetLastError());
816 addr.sin_family=AF_INET;
817 addr.sin_port=htons(port);
818 memcpy(&addr.sin_addr,&inaddr,sizeof(inaddr));
820 if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
831 /* Do the SRV thing */
832 if(flags&HTTP_FLAG_TRY_SRV && srvtag)
834 /* We're using SRV, so append the tags */
835 if(1+strlen(srvtag)+6+strlen(server)+1<=MAXDNAME)
837 char srvname[MAXDNAME];
840 strcat(srvname,srvtag);
841 strcat(srvname,"._tcp.");
842 strcat(srvname,server);
843 srvcount=getsrv(srvname,&srvlist);
850 /* Either we're not using SRV, or the SRV lookup failed. Make
851 up a fake SRV record. */
852 srvlist=xmalloc_clear(sizeof(struct srventry));
854 strncpy(srvlist->target,server,MAXDNAME);
855 srvlist->target[MAXDNAME-1]='\0';
859 #ifdef HAVE_GETADDRINFO
861 for(srv=0;srv<srvcount;srv++)
863 struct addrinfo hints,*res,*ai;
866 sprintf(portstr,"%u",srvlist[srv].port);
867 memset(&hints,0,sizeof(hints));
868 hints.ai_socktype=SOCK_STREAM;
869 if(getaddrinfo(srvlist[srv].target,portstr,&hints,&res)==0)
874 for(ai=res;ai;ai=ai->ai_next)
876 if((sock=socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol))==-1)
878 log_error("error creating socket: %s\n",strerror(errno));
883 if(connect(sock,ai->ai_addr,ai->ai_addrlen)==0)
898 #else /* !HAVE_GETADDRINFO */
900 for(srv=0;srv<srvcount;srv++)
903 struct hostent *host=NULL;
904 struct sockaddr_in addr;
906 memset(&addr,0,sizeof(addr));
908 if((host=gethostbyname(srvlist[srv].target))==NULL)
913 if((sock=socket(host->h_addrtype,SOCK_STREAM,0))==-1)
915 log_error("error creating socket: %s\n",strerror(errno));
919 addr.sin_family=host->h_addrtype;
920 if(addr.sin_family!=AF_INET)
922 log_error("%s: unknown address family\n",srvlist[srv].target);
926 addr.sin_port=htons(srvlist[srv].port);
928 /* Try all A records until one responds. */
929 while(host->h_addr_list[i])
931 if(host->h_length!=4)
933 log_error("%s: illegal address length\n",srvlist[srv].target);
937 memcpy(&addr.sin_addr,host->h_addr_list[i],host->h_length);
939 if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
948 if(host->h_addr_list[i])
953 #endif /* !HAVE_GETADDRINFO */
962 log_error("%s: Unable to connect: ec=%d\n",server,(int)WSAGetLastError());
964 log_error("%s: Host not found: ec=%d\n",server,(int)WSAGetLastError());
967 log_error("%s: %s\n",server,strerror(err));
969 log_error("%s: Host not found\n",server);
982 write_server( int sock, const char *data, size_t length )
991 nwritten = send (sock, data, nleft, 0);
992 if ( nwritten == SOCKET_ERROR ) {
993 log_info ("write failed: ec=%d\n", (int)WSAGetLastError ());
994 return G10ERR_NETWORK;
997 int nwritten = write( sock, data, nleft );
998 if( nwritten == -1 ) {
1001 if( errno == EAGAIN ) {
1006 select(0, NULL, NULL, NULL, &tv);
1009 log_info("write failed: %s\n", strerror(errno));
1010 return G10ERR_NETWORK;
1020 /**** Test code ****/
1024 main(int argc, char **argv)
1029 struct http_context hd;
1032 log_set_name("http-test");
1039 fprintf(stderr,"usage: http-test uri\n");
1044 rc = parse_uri( &uri, *argv );
1046 log_error("`%s': %s\n", *argv, g10_errstr(rc));
1047 release_parsed_uri( uri );
1051 printf("Scheme: %s\n", uri->scheme );
1052 printf("Host : %s\n", uri->host );
1053 printf("Port : %u\n", uri->port );
1054 printf("Path : %s\n", uri->path );
1055 for( r=uri->params; r; r = r->next ) {
1056 printf("Params: %s=%s", r->name, r->value );
1057 if( strlen( r->value ) != r->valuelen )
1058 printf(" [real length=%d]", (int)r->valuelen );
1061 for( r=uri->query; r; r = r->next ) {
1062 printf("Query : %s=%s", r->name, r->value );
1063 if( strlen( r->value ) != r->valuelen )
1064 printf(" [real length=%d]", (int)r->valuelen );
1067 release_parsed_uri( uri ); uri = NULL;
1069 rc = http_open_document( &hd, *argv, 0, NULL );
1071 log_error("can't get `%s': %s\n", *argv, g10_errstr(rc));
1074 log_info("open_http_document succeeded; status=%u\n", hd.status_code );
1075 while( (c=iobuf_get( hd.fp_read)) != -1 )