1 /* http.c - HTTP protocol handler
2 * Copyright (C) 1999, 2001, 2002, 2003, 2004,
3 * 2005, 2009 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 3 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, see <http://www.gnu.org/licenses/>.
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 *auth, 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 modified to do the
120 "=" padding. Better to use a single copy in strgutil.c ? */
122 make_radix64_string( const byte *data, size_t len )
126 buffer = p = xmalloc( (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)];
139 else if( len == 1 ) {
140 *p++ = bintoasc[(data[0] >> 2) & 077];
141 *p++ = bintoasc[(data[0] <<4)&060];
150 http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url,
151 char *auth, unsigned int flags, const char *proxy )
155 if( !(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST) )
156 return G10ERR_INV_ARG;
158 /* initialize the handle */
159 memset( hd, 0, sizeof *hd );
162 hd->req_type = reqtype;
165 rc = parse_uri( &hd->uri, url );
167 rc = send_request( hd, auth, proxy );
169 hd->fp_write = iobuf_sockopen( hd->sock , "w" );
176 if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
177 sock_close( hd->sock );
178 iobuf_close( hd->fp_read );
179 iobuf_close( hd->fp_write);
180 release_parsed_uri( hd->uri );
188 http_start_data( HTTP_HD hd )
190 iobuf_flush ( hd->fp_write );
192 write_server (hd->sock, "\r\n", 2);
199 http_wait_response( HTTP_HD hd, unsigned int *ret_status )
203 http_start_data( hd ); /* make sure that we are in the data */
206 hd->sock = dup( hd->sock );
208 return G10ERR_GENERAL;
210 iobuf_ioctl (hd->fp_write, 1, 1, NULL); /* keep the socket open */
211 iobuf_close (hd->fp_write);
213 /* We do not want the shutdown code anymore. It used to be there
214 to support old versions of pksd. These versions are anyway
215 unusable and the latest releases haven been fixed to properly
217 /* if ( !(hd->flags & HTTP_FLAG_NO_SHUTDOWN) ) */
218 /* shutdown( hd->sock, 1 ); */
221 hd->fp_read = iobuf_sockopen( hd->sock , "r" );
223 return G10ERR_GENERAL;
225 rc = parse_response( hd );
226 if( !rc && ret_status )
227 *ret_status = hd->status_code;
234 http_open_document( HTTP_HD hd, const char *document, char *auth,
235 unsigned int flags, const char *proxy )
239 rc = http_open(hd, HTTP_REQ_GET, document, auth, flags, proxy );
243 rc = http_wait_response( hd, NULL );
252 http_close( HTTP_HD hd )
254 if( !hd || !hd->initialized )
256 if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
257 sock_close( hd->sock );
258 iobuf_close( hd->fp_read );
259 iobuf_close( hd->fp_write );
260 release_parsed_uri( hd->uri );
268 * Parse an URI and put the result into the newly allocated ret_uri.
269 * The caller must always use release_parsed_uri to releases the
270 * resources (even on an error).
273 parse_uri( PARSED_URI *ret_uri, const char *uri )
275 *ret_uri = xmalloc_clear( sizeof(**ret_uri) + strlen(uri) );
276 strcpy( (*ret_uri)->buffer, uri );
277 return do_parse_uri( *ret_uri, 0 );
281 release_parsed_uri( PARSED_URI uri )
287 for( r = uri->query; r; r = r2 ) {
296 do_parse_uri( PARSED_URI uri, int only_local_part )
303 n = strlen( uri->buffer );
304 /* initialize all fields to an empty string or an empty list */
305 uri->scheme = uri->host = uri->path = p + n;
307 uri->params = uri->query = NULL;
309 /* a quick validity check */
310 if( strspn( p, VALID_URI_CHARS) != n )
311 return G10ERR_BAD_URI; /* invalid characters found */
313 if( !only_local_part ) {
314 /* find the scheme */
315 if( !(p2 = strchr( p, ':' ) ) || p2 == p )
316 return G10ERR_BAD_URI; /* No scheme */
320 if(strcmp(uri->scheme,"http")==0)
323 return G10ERR_INVALID_URI; /* Unsupported scheme */
327 /* find the hostname */
329 return G10ERR_INVALID_URI; /* does not start with a slash */
332 if( *p == '/' ) { /* there seems to be a hostname */
334 if( (p2 = strchr(p, '/')) )
337 /* Check for username/password encoding */
338 if((p3=strchr(p,'@')))
347 /* Handle a host of [IP] so that [IP:V6]:port works */
348 if( *p == '[' && (p3=strchr( p, ']' )) )
351 /* worst case, uri->host should have length 0, points to \0 */
358 if( (p3=strchr( p, ':' )) )
361 uri->port = atoi( p3 );
364 if( (n = remove_escapes( uri->host )) < 0 )
365 return G10ERR_BAD_URI;
366 if( n != strlen( uri->host ) )
367 return G10ERR_BAD_URI; /* hostname with a Nul in it */
370 } /* end global URI part */
372 /* parse the pathname part */
373 if( !p || !*p ) /* we don't have a path */
374 return 0; /* and this is okay */
376 /* todo: here we have to check params */
378 /* do we have a query part */
379 if( (p2 = strchr( p, '?' )) )
383 if( (n = remove_escapes( p )) < 0 )
384 return G10ERR_BAD_URI;
385 if( n != strlen( p ) )
386 return G10ERR_BAD_URI; /* path with a Nul in it */
389 if( !p || !*p ) /* we don't have a query string */
392 /* now parse the query string */
397 if( (p2 = strchr( p, '&' )) )
399 if( !(elem = parse_tuple( p )) )
400 return G10ERR_BAD_URI;
415 * Remove all %xx escapes; this is done inplace.
416 * Returns: new length of the string.
419 remove_escapes( byte *string )
424 for(p=s=string; *s ; s++ ) {
426 if( s[1] && s[2] && isxdigit(s[1]) && isxdigit(s[2]) ) {
428 *p = *s >= '0' && *s <= '9' ? *s - '0' :
429 *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
432 *p |= *s >= '0' && *s <= '9' ? *s - '0' :
433 *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
445 return -1; /* bad URI */
454 *p = 0; /* always keep a string terminator */
460 insert_escapes( byte *buffer, const byte *string, const byte *special )
464 for( ; *string; string++ ) {
465 if( strchr( VALID_URI_CHARS, *string )
466 && !strchr( special, *string ) ) {
473 sprintf( buffer, "%%%02X", *string );
484 parse_tuple( byte *string )
491 if( (p2 = strchr( p, '=' )) )
493 if( (n = remove_escapes( p )) < 0 )
494 return NULL; /* bad URI */
495 if( n != strlen( p ) )
496 return NULL; /* name with a Nul in it */
497 tuple = xmalloc_clear( sizeof *tuple );
500 /* we have only the name, so we assume an empty value string */
501 tuple->value = p + strlen(p);
504 else { /* name and value */
505 if( (n = remove_escapes( p2 )) < 0 ) {
507 return NULL; /* bad URI */
517 * Send a HTTP request to the server
518 * Returns 0 if the request was successful
521 send_request( HTTP_HD hd, const char *auth, const char *proxy )
527 char *proxy_authstr=NULL,*authstr=NULL;
529 server = *hd->uri->host? hd->uri->host : "localhost";
530 port = hd->uri->port? hd->uri->port : 80;
536 rc = parse_uri( &uri, proxy );
539 log_error("invalid HTTP proxy (%s): %s\n",proxy,g10_errstr(rc));
540 release_parsed_uri( uri );
541 return G10ERR_NETWORK;
543 hd->sock = connect_server( *uri->host? uri->host : "localhost",
544 uri->port? uri->port : 80, 0, NULL );
548 remove_escapes(uri->auth);
549 x=make_radix64_string(uri->auth,strlen(uri->auth));
550 proxy_authstr=xmalloc(52+strlen(x));
551 sprintf(proxy_authstr,"Proxy-Authorization: Basic %s\r\n",x);
555 release_parsed_uri( uri );
558 hd->sock = connect_server( server, port, hd->flags, hd->uri->scheme );
560 if(auth || hd->uri->auth)
562 char *x,*tempauth=NULL;
566 tempauth=xstrdup(auth);
567 remove_escapes(tempauth);
569 else if(hd->uri->auth)
570 remove_escapes(hd->uri->auth);
572 x=make_radix64_string(tempauth?tempauth:hd->uri->auth,
573 strlen(tempauth?tempauth:hd->uri->auth));
574 authstr=xmalloc(52+strlen(x));
575 sprintf(authstr,"Authorization: Basic %s\r\n",x);
581 return G10ERR_NETWORK;
583 p = build_rel_path( hd->uri );
585 request=xmalloc(strlen(server)*2 + strlen(p)
586 + (authstr?strlen(authstr):0)
587 + (proxy_authstr?strlen(proxy_authstr):0) + 65);
588 if( proxy && *proxy )
589 sprintf( request, "%s http://%s:%hu%s%s HTTP/1.0\r\n%s%s",
590 hd->req_type == HTTP_REQ_GET ? "GET" :
591 hd->req_type == HTTP_REQ_HEAD? "HEAD":
592 hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
593 server, port, *p == '/'? "":"/", p,
594 authstr?authstr:"",proxy_authstr?proxy_authstr:"" );
600 sprintf(portstr,":%u",port);
602 sprintf( request, "%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s",
603 hd->req_type == HTTP_REQ_GET ? "GET" :
604 hd->req_type == HTTP_REQ_HEAD? "HEAD":
605 hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
606 *p == '/'? "":"/", p, server, (port!=80)?portstr:"",
612 rc = write_server( hd->sock, request, strlen(request) );
614 xfree(proxy_authstr);
622 * Build the relative path from the parsed URI.
623 * Minimal implementation.
626 build_rel_path( PARSED_URI uri )
632 /* count the needed space */
633 n = insert_escapes( NULL, uri->path, "%;?&" );
634 /* todo: build params */
635 for( r=uri->query; r; r = r->next ) {
637 n += insert_escapes( NULL, r->name, "%;?&=" );
639 n += insert_escapes( NULL, r->value, "%;?&=" );
643 /* now allocate and copy */
644 p = rel_path = xmalloc( n );
645 n = insert_escapes( p, uri->path, "%;?&" );
647 /* todo: add params */
648 for( r=uri->query; r; r = r->next ) {
649 *p++ = r == uri->query? '?':'&';
650 n = insert_escapes( p, r->name, "%;?&=" );
653 /* todo: use valuelen */
654 n = insert_escapes( p, r->value, "%;?&=" );
663 /***********************
664 * Parse the response from a server.
665 * Returns: errorcode and sets some fileds in the handle
668 parse_response( HTTP_HD hd )
671 unsigned maxlen, len;
673 /* Wait for the status line */
675 maxlen = MAX_LINELEN;
676 len = iobuf_read_line( hd->fp_read, &hd->buffer,
677 &hd->buffer_size, &maxlen );
680 return -1; /* line has been truncated */
685 if( (p = strchr( line, '/')) )
687 if( !p || strcmp( line, "HTTP" ) )
688 return 0; /* assume http 0.9 */
690 if( (p2 = strpbrk( p, " \t" ) ) ) {
692 p2 += strspn( p2, " \t" );
695 return 0; /* assume http 0.9 */
697 /* todo: add HTTP version number check here */
698 if( (p2 = strpbrk( p, " \t" ) ) )
700 if( !isdigit(p[0]) || !isdigit(p[1]) || !isdigit(p[2]) || p[3] ) {
701 /* malformed HTTP statuscode - assume HTTP 0.9 */
703 hd->status_code = 200;
706 hd->status_code = atoi( p );
708 /* skip all the header lines and wait for the empty line */
710 maxlen = MAX_LINELEN;
711 len = iobuf_read_line( hd->fp_read, &hd->buffer,
712 &hd->buffer_size, &maxlen );
714 /* we ignore truncated lines */
717 /* time lineendings */
718 if( (*line == '\r' && line[1] == '\n') || *line == '\n' )
720 } while( len && *line );
729 struct sockaddr_in mya;
730 struct sockaddr_in peer;
736 if( (fd=socket(AF_INET,SOCK_STREAM, 0)) == -1 ) {
737 log_error("socket() failed: %s\n", strerror(errno));
741 if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (byte*)&i, sizeof(i) ) )
742 log_info("setsockopt(SO_REUSEADDR) failed: %s\n", strerror(errno) );
744 mya.sin_family=AF_INET;
745 memset(&mya.sin_addr, 0, sizeof(mya.sin_addr));
746 mya.sin_port=htons(11371);
748 if( bind( fd, (struct sockaddr *)&mya, sizeof(mya)) ) {
749 log_error("bind to port 11371 failed: %s\n", strerror(errno) );
754 if( listen( fd, 5 ) ) {
755 log_error("listen failed: %s\n", strerror(errno) );
764 if( select( fd+1, &rfds, NULL, NULL, NULL) <= 0 )
765 continue; /* ignore any errors */
767 if( !FD_ISSET( fd, &rfds ) )
770 addrlen = sizeof peer;
771 client = accept( fd, (struct sockaddr *)&peer, &addrlen);
775 log_info("connect from %s\n", inet_ntoa( peer.sin_addr ) );
783 fp = fdopen( client , "r" );
784 while( (c=getc(fp)) != EOF )
789 sock_close( client );
799 connect_server( const char *server, ushort port, unsigned int flags,
802 int sock=-1,srv,srvcount=0,connected=0,hostfound=0;
803 struct srventry *srvlist=NULL;
806 unsigned long inaddr;
809 /* Win32 gethostbyname doesn't handle IP addresses internally, so we
810 try inet_addr first on that platform only. */
811 if((inaddr=inet_addr(server))!=INADDR_NONE)
813 struct sockaddr_in addr;
815 memset(&addr,0,sizeof(addr));
817 if((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET)
819 log_error("error creating socket: ec=%d\n",(int)WSAGetLastError());
823 addr.sin_family=AF_INET;
824 addr.sin_port=htons(port);
825 memcpy(&addr.sin_addr,&inaddr,sizeof(inaddr));
827 if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
838 /* Do the SRV thing */
839 if(flags&HTTP_FLAG_TRY_SRV && srvtag)
841 /* We're using SRV, so append the tags */
842 if(1+strlen(srvtag)+6+strlen(server)+1<=MAXDNAME)
844 char srvname[MAXDNAME];
847 strcat(srvname,srvtag);
848 strcat(srvname,"._tcp.");
849 strcat(srvname,server);
850 srvcount=getsrv(srvname,&srvlist);
857 /* Either we're not using SRV, or the SRV lookup failed. Make
858 up a fake SRV record. */
859 srvlist=xmalloc_clear(sizeof(struct srventry));
861 strncpy(srvlist->target,server,MAXDNAME);
862 srvlist->target[MAXDNAME-1]='\0';
866 #ifdef HAVE_GETADDRINFO
868 for(srv=0;srv<srvcount;srv++)
870 struct addrinfo hints,*res,*ai;
873 sprintf(portstr,"%u",srvlist[srv].port);
874 memset(&hints,0,sizeof(hints));
875 hints.ai_socktype=SOCK_STREAM;
876 if(getaddrinfo(srvlist[srv].target,portstr,&hints,&res)==0)
881 for(ai=res;ai;ai=ai->ai_next)
883 if((sock=socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol))==-1)
885 log_error("error creating socket: %s\n",strerror(errno));
890 if(connect(sock,ai->ai_addr,ai->ai_addrlen)==0)
905 #else /* !HAVE_GETADDRINFO */
907 for(srv=0;srv<srvcount;srv++)
910 struct hostent *host=NULL;
911 struct sockaddr_in addr;
913 memset(&addr,0,sizeof(addr));
915 if((host=gethostbyname(srvlist[srv].target))==NULL)
920 if((sock=socket(host->h_addrtype,SOCK_STREAM,0))==-1)
922 log_error("error creating socket: %s\n",strerror(errno));
926 addr.sin_family=host->h_addrtype;
927 if(addr.sin_family!=AF_INET)
929 log_error("%s: unknown address family\n",srvlist[srv].target);
933 addr.sin_port=htons(srvlist[srv].port);
935 /* Try all A records until one responds. */
936 while(host->h_addr_list[i])
938 if(host->h_length!=4)
940 log_error("%s: illegal address length\n",srvlist[srv].target);
944 memcpy(&addr.sin_addr,host->h_addr_list[i],host->h_length);
946 if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
955 if(host->h_addr_list[i])
960 #endif /* !HAVE_GETADDRINFO */
969 log_error("%s: Unable to connect: ec=%d\n",server,(int)WSAGetLastError());
971 log_error("%s: Host not found: ec=%d\n",server,(int)WSAGetLastError());
974 log_error("%s: %s\n",server,strerror(err));
976 log_error("%s: Host not found\n",server);
989 write_server( int sock, const char *data, size_t length )
998 nwritten = send (sock, data, nleft, 0);
999 if ( nwritten == SOCKET_ERROR ) {
1000 log_info ("write failed: ec=%d\n", (int)WSAGetLastError ());
1001 return G10ERR_NETWORK;
1004 int nwritten = write( sock, data, nleft );
1005 if( nwritten == -1 ) {
1006 if( errno == EINTR )
1008 if( errno == EAGAIN ) {
1013 select(0, NULL, NULL, NULL, &tv);
1016 log_info("write failed: %s\n", strerror(errno));
1017 return G10ERR_NETWORK;
1027 /**** Test code ****/
1031 main(int argc, char **argv)
1036 struct http_context hd;
1039 log_set_name("http-test");
1046 fprintf(stderr,"usage: http-test uri\n");
1051 rc = parse_uri( &uri, *argv );
1053 log_error("`%s': %s\n", *argv, g10_errstr(rc));
1054 release_parsed_uri( uri );
1058 printf("Scheme: %s\n", uri->scheme );
1059 printf("Host : %s\n", uri->host );
1060 printf("Port : %u\n", uri->port );
1061 printf("Path : %s\n", uri->path );
1062 for( r=uri->params; r; r = r->next ) {
1063 printf("Params: %s=%s", r->name, r->value );
1064 if( strlen( r->value ) != r->valuelen )
1065 printf(" [real length=%d]", (int)r->valuelen );
1068 for( r=uri->query; r; r = r->next ) {
1069 printf("Query : %s=%s", r->name, r->value );
1070 if( strlen( r->value ) != r->valuelen )
1071 printf(" [real length=%d]", (int)r->valuelen );
1074 release_parsed_uri( uri ); uri = NULL;
1076 rc = http_open_document( &hd, *argv, NULL, 0, NULL );
1078 log_error("can't get `%s': %s\n", *argv, g10_errstr(rc));
1081 log_info("open_http_document succeeded; status=%u\n", hd.status_code );
1082 while( (c=iobuf_get( hd.fp_read)) != -1 )