1 /* http.c - HTTP protocol handler
2 * Copyright (C) 1999, 2001, 2002, 2003 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 HTTP_PROXY_ENV "GnuPG$HttpProxy"
50 #define HTTP_PROXY_ENV_PRINTABLE "<GnuPG$HttpProxy>"
52 #define HTTP_PROXY_ENV "http_proxy"
53 #define HTTP_PROXY_ENV_PRINTABLE "$http_proxy"
57 #define sock_close(a) closesocket(a)
59 #define sock_close(a) close(a)
62 #define MAX_LINELEN 20000 /* max. length of a HTTP line */
63 #define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
64 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
66 "!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
69 #define EAGAIN EWOULDBLOCK
72 static int parse_uri( PARSED_URI *ret_uri, const char *uri );
73 static void release_parsed_uri( PARSED_URI uri );
74 static int do_parse_uri( PARSED_URI uri, int only_local_part );
75 static int remove_escapes( byte *string );
76 static int insert_escapes( byte *buffer, const byte *string,
77 const byte *special );
78 static URI_TUPLE parse_tuple( byte *string );
79 static int send_request( HTTP_HD hd );
80 static byte *build_rel_path( PARSED_URI uri );
81 static int parse_response( HTTP_HD hd );
83 static int connect_server(const char *server, ushort port, unsigned int flags);
84 static int write_server( int sock, const char *data, size_t length );
96 static int initialized;
97 static WSADATA wsdata;
102 if( WSAStartup( 0x0101, &wsdata ) ) {
103 log_error ("error initializing socket library: ec=%d\n",
104 (int)WSAGetLastError () );
107 if( wsdata.wVersion < 0x0001 ) {
108 log_error ("socket library version is %x.%x - but 1.1 needed\n",
109 LOBYTE(wsdata.wVersion), HIBYTE(wsdata.wVersion));
113 atexit ( deinit_sockets );
120 http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url,
125 if( !(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST) )
126 return G10ERR_INV_ARG;
128 /* initialize the handle */
129 memset( hd, 0, sizeof *hd );
132 hd->req_type = reqtype;
135 rc = parse_uri( &hd->uri, url );
137 rc = send_request( hd );
139 hd->fp_write = iobuf_sockopen( hd->sock , "w" );
146 if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
147 sock_close( hd->sock );
148 iobuf_close( hd->fp_read );
149 iobuf_close( hd->fp_write);
150 release_parsed_uri( hd->uri );
158 http_start_data( HTTP_HD hd )
160 iobuf_flush ( hd->fp_write );
162 write_server (hd->sock, "\r\n", 2);
169 http_wait_response( HTTP_HD hd, unsigned int *ret_status )
173 http_start_data( hd ); /* make sure that we are in the data */
176 hd->sock = dup( hd->sock );
178 return G10ERR_GENERAL;
180 iobuf_ioctl (hd->fp_write, 1, 1, NULL); /* keep the socket open */
181 iobuf_close (hd->fp_write);
183 if ( !(hd->flags & HTTP_FLAG_NO_SHUTDOWN) )
184 shutdown( hd->sock, 1 );
187 hd->fp_read = iobuf_sockopen( hd->sock , "r" );
189 return G10ERR_GENERAL;
191 rc = parse_response( hd );
192 if( !rc && ret_status )
193 *ret_status = hd->status_code;
200 http_open_document( HTTP_HD hd, const char *document, unsigned int flags )
204 rc = http_open( hd, HTTP_REQ_GET, document, flags );
208 rc = http_wait_response( hd, NULL );
219 http_close( HTTP_HD hd )
221 if( !hd || !hd->initialized )
223 if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
224 sock_close( hd->sock );
225 iobuf_close( hd->fp_read );
226 iobuf_close( hd->fp_write );
227 release_parsed_uri( hd->uri );
228 m_free( hd->buffer );
235 * Parse an URI and put the result into the newly allocated ret_uri.
236 * The caller must always use release_parsed_uri to releases the
237 * resources (even on an error).
240 parse_uri( PARSED_URI *ret_uri, const char *uri )
242 *ret_uri = m_alloc_clear( sizeof(**ret_uri) + strlen(uri) );
243 strcpy( (*ret_uri)->buffer, uri );
244 return do_parse_uri( *ret_uri, 0 );
248 release_parsed_uri( PARSED_URI uri )
254 for( r = uri->query; r; r = r2 ) {
263 do_parse_uri( PARSED_URI uri, int only_local_part )
270 n = strlen( uri->buffer );
271 /* initialize all fields to an empty string or an empty list */
272 uri->scheme = uri->host = uri->path = p + n;
274 uri->params = uri->query = NULL;
276 /* a quick validity check */
277 if( strspn( p, VALID_URI_CHARS) != n )
278 return G10ERR_BAD_URI; /* invalid characters found */
280 if( !only_local_part ) {
281 /* find the scheme */
282 if( !(p2 = strchr( p, ':' ) ) || p2 == p )
283 return G10ERR_BAD_URI; /* No scheme */
288 if( !strcmp( uri->scheme, "http" ) )
290 else if( !strcmp( uri->scheme, "x-hkp" ) ) /* same as HTTP */
293 return G10ERR_INVALID_URI; /* Unsupported scheme */
297 /* find the hostname */
299 return G10ERR_INVALID_URI; /* does not start with a slash */
302 if( *p == '/' ) { /* there seems to be a hostname */
304 if( (p2 = strchr(p, '/')) )
308 if( (p3=strchr( p, ':' )) ) {
310 uri->port = atoi( p3 );
314 if( (n = remove_escapes( uri->host )) < 0 )
315 return G10ERR_BAD_URI;
316 if( n != strlen( p ) )
317 return G10ERR_BAD_URI; /* hostname with a Nul in it */
320 } /* end global URI part */
322 /* parse the pathname part */
323 if( !p || !*p ) /* we don't have a path */
324 return 0; /* and this is okay */
326 /* todo: here we have to check params */
328 /* do we have a query part */
329 if( (p2 = strchr( p, '?' )) )
333 if( (n = remove_escapes( p )) < 0 )
334 return G10ERR_BAD_URI;
335 if( n != strlen( p ) )
336 return G10ERR_BAD_URI; /* path with a Nul in it */
339 if( !p || !*p ) /* we don't have a query string */
342 /* now parse the query string */
347 if( (p2 = strchr( p, '&' )) )
349 if( !(elem = parse_tuple( p )) )
350 return G10ERR_BAD_URI;
365 * Remove all %xx escapes; this is done inplace.
366 * Returns: new length of the string.
369 remove_escapes( byte *string )
374 for(p=s=string; *s ; s++ ) {
376 if( s[1] && s[2] && isxdigit(s[1]) && isxdigit(s[2]) ) {
378 *p = *s >= '0' && *s <= '9' ? *s - '0' :
379 *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
382 *p |= *s >= '0' && *s <= '9' ? *s - '0' :
383 *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
395 return -1; /* bad URI */
404 *p = 0; /* always keep a string terminator */
410 insert_escapes( byte *buffer, const byte *string, const byte *special )
414 for( ; *string; string++ ) {
415 if( strchr( VALID_URI_CHARS, *string )
416 && !strchr( special, *string ) ) {
423 sprintf( buffer, "%02X", *string );
437 parse_tuple( byte *string )
444 if( (p2 = strchr( p, '=' )) )
446 if( (n = remove_escapes( p )) < 0 )
447 return NULL; /* bad URI */
448 if( n != strlen( p ) )
449 return NULL; /* name with a Nul in it */
450 tuple = m_alloc_clear( sizeof *tuple );
453 /* we have only the name, so we assume an empty value string */
454 tuple->value = p + strlen(p);
457 else { /* name and value */
458 if( (n = remove_escapes( p2 )) < 0 ) {
460 return NULL; /* bad URI */
470 * Send a HTTP request to the server
471 * Returns 0 if the request was successful
474 send_request( HTTP_HD hd )
480 const char *http_proxy = NULL;
482 server = *hd->uri->host? hd->uri->host : "localhost";
483 port = hd->uri->port? hd->uri->port : 80;
485 if( (hd->flags & HTTP_FLAG_TRY_PROXY)
486 && (http_proxy = getenv( HTTP_PROXY_ENV )) ) {
489 rc = parse_uri( &uri, http_proxy );
491 log_error("invalid " HTTP_PROXY_ENV_PRINTABLE ": %s\n",
493 release_parsed_uri( uri );
494 return G10ERR_NETWORK;
496 hd->sock = connect_server( *uri->host? uri->host : "localhost",
497 uri->port? uri->port : 80, 0 );
498 release_parsed_uri( uri );
501 hd->sock = connect_server( server, port, hd->flags );
504 return G10ERR_NETWORK;
506 p = build_rel_path( hd->uri );
507 request = m_alloc( strlen(server)*2 + strlen(p) + 50 );
509 sprintf( request, "%s http://%s:%hu%s%s HTTP/1.0\r\n",
510 hd->req_type == HTTP_REQ_GET ? "GET" :
511 hd->req_type == HTTP_REQ_HEAD? "HEAD":
512 hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
513 server, port, *p == '/'? "":"/", p );
516 sprintf( request, "%s %s%s HTTP/1.0\r\nHost: %s\r\n",
517 hd->req_type == HTTP_REQ_GET ? "GET" :
518 hd->req_type == HTTP_REQ_HEAD? "HEAD":
519 hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
520 *p == '/'? "":"/", p, server);
524 rc = write_server( hd->sock, request, strlen(request) );
532 * Build the relative path from the parsed URI.
533 * Minimal implementation.
536 build_rel_path( PARSED_URI uri )
542 /* count the needed space */
543 n = insert_escapes( NULL, uri->path, "%;?&" );
544 /* todo: build params */
545 for( r=uri->query; r; r = r->next ) {
547 n += insert_escapes( NULL, r->name, "%;?&=" );
549 n += insert_escapes( NULL, r->value, "%;?&=" );
553 /* now allocate and copy */
554 p = rel_path = m_alloc( n );
555 n = insert_escapes( p, uri->path, "%;?&" );
557 /* todo: add params */
558 for( r=uri->query; r; r = r->next ) {
559 *p++ = r == uri->query? '?':'&';
560 n = insert_escapes( p, r->name, "%;?&=" );
563 /* todo: use valuelen */
564 n = insert_escapes( p, r->value, "%;?&=" );
573 /***********************
574 * Parse the response from a server.
575 * Returns: errorcode and sets some fileds in the handle
578 parse_response( HTTP_HD hd )
581 unsigned maxlen, len;
583 /* Wait for the status line */
585 maxlen = MAX_LINELEN;
586 len = iobuf_read_line( hd->fp_read, &hd->buffer,
587 &hd->buffer_size, &maxlen );
590 return -1; /* line has been truncated */
595 if( (p = strchr( line, '/')) )
597 if( !p || strcmp( line, "HTTP" ) )
598 return 0; /* assume http 0.9 */
600 if( (p2 = strpbrk( p, " \t" ) ) ) {
602 p2 += strspn( p2, " \t" );
605 return 0; /* assume http 0.9 */
607 /* todo: add HTTP version number check here */
608 if( (p2 = strpbrk( p, " \t" ) ) )
610 if( !isdigit(p[0]) || !isdigit(p[1]) || !isdigit(p[2]) || p[3] ) {
611 /* malformed HTTP statuscode - assume HTTP 0.9 */
613 hd->status_code = 200;
616 hd->status_code = atoi( p );
618 /* skip all the header lines and wait for the empty line */
620 maxlen = MAX_LINELEN;
621 len = iobuf_read_line( hd->fp_read, &hd->buffer,
622 &hd->buffer_size, &maxlen );
624 /* we ignore truncated lines */
627 /* time lineendings */
628 if( (*line == '\r' && line[1] == '\n') || *line == '\n' )
630 } while( len && *line );
639 struct sockaddr_in mya;
640 struct sockaddr_in peer;
646 if( (fd=socket(AF_INET,SOCK_STREAM, 0)) == -1 ) {
647 log_error("socket() failed: %s\n", strerror(errno));
651 if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (byte*)&i, sizeof(i) ) )
652 log_info("setsockopt(SO_REUSEADDR) failed: %s\n", strerror(errno) );
654 mya.sin_family=AF_INET;
655 memset(&mya.sin_addr, 0, sizeof(mya.sin_addr));
656 mya.sin_port=htons(11371);
658 if( bind( fd, (struct sockaddr *)&mya, sizeof(mya)) ) {
659 log_error("bind to port 11371 failed: %s\n", strerror(errno) );
664 if( listen( fd, 5 ) ) {
665 log_error("listen failed: %s\n", strerror(errno) );
674 if( select( fd+1, &rfds, NULL, NULL, NULL) <= 0 )
675 continue; /* ignore any errors */
677 if( !FD_ISSET( fd, &rfds ) )
680 addrlen = sizeof peer;
681 client = accept( fd, (struct sockaddr *)&peer, &addrlen);
685 log_info("connect from %s\n", inet_ntoa( peer.sin_addr ) );
693 fp = fdopen( client , "r" );
694 while( (c=getc(fp)) != EOF )
699 sock_close( client );
709 connect_server( const char *server, ushort port, unsigned int flags )
711 int sock=-1,srv,srvcount=0,connected=0,hostfound=0;
712 struct srventry *srvlist=NULL;
715 unsigned long inaddr;
718 /* Win32 gethostbyname doesn't handle IP addresses internally, so we
719 try inet_addr first on that platform only. */
720 if((inaddr=inet_addr(server))!=SOCKET_ERROR)
722 struct sockaddr_in addr;
724 memset(&addr,0,sizeof(addr));
726 if((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET)
728 log_error("error creating socket: ec=%d\n",(int)WSAGetLastError());
732 addr.sin_family=AF_INET;
733 addr.sin_port=htons(port);
735 if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
746 /* Do the SRV thing */
747 if(flags&HTTP_FLAG_TRY_SRV)
749 /* We're using SRV, so append the tags */
750 char srvname[MAXDNAME];
752 strcpy(srvname,"_hkp._tcp.");
753 strncat(srvname,server,MAXDNAME-11);
754 srvname[MAXDNAME-1]='\0';
755 srvcount=getsrv(srvname,&srvlist);
761 /* Either we're not using SRV, or the SRV lookup failed. Make
762 up a fake SRV record. */
763 srvlist=m_alloc_clear(sizeof(struct srventry));
765 strncpy(srvlist->target,server,MAXDNAME);
769 #ifdef HAVE_GETADDRINFO
771 for(srv=0;srv<srvcount;srv++)
773 struct addrinfo hints,*res,*ai;
776 sprintf(portstr,"%u",srvlist[srv].port);
777 memset(&hints,0,sizeof(hints));
778 hints.ai_socktype=SOCK_STREAM;
779 if(getaddrinfo(srvlist[srv].target,portstr,&hints,&res)==0)
784 for(ai=res;ai;ai=ai->ai_next)
786 if((sock=socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol))==-1)
788 log_error("error creating socket: %s\n",strerror(errno));
793 if(connect(sock,ai->ai_addr,ai->ai_addrlen)==0)
806 #else /* !HAVE_GETADDRINFO */
808 for(srv=0;srv<srvcount;srv++)
811 struct hostent *host=NULL;
812 struct sockaddr_in addr;
814 memset(&addr,0,sizeof(addr));
816 if((host=gethostbyname(srvlist[srv].target))==NULL)
819 if((sock=socket(host->h_addrtype,SOCK_STREAM,0))==-1)
821 log_error("error creating socket: %s\n",strerror(errno));
825 addr.sin_family=host->h_addrtype;
826 addr.sin_port=htons(srvlist[srv].port);
828 /* Try all A records until one responds. */
829 while(host->h_addr_list[i])
831 memcpy(&addr.sin_addr,host->h_addr_list[i],host->h_length);
833 if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
842 if(host->h_addr_list[i])
845 #endif /* !HAVE_GETADDRINFO */
853 log_error("%s: Unable to connect: ec=%d\n",server,(int)WSAGetLastError());
855 log_error("%s: Host not found: ec=%d\n",server,(int)WSAGetLastError());
858 log_error("%s: %s\n",server,strerror(errno));
860 log_error("%s: Host not found\n",server);
872 write_server( int sock, const char *data, size_t length )
881 nwritten = send (sock, data, nleft, 0);
882 if ( nwritten == SOCKET_ERROR ) {
883 log_info ("write failed: ec=%d\n", (int)WSAGetLastError ());
884 return G10ERR_NETWORK;
887 int nwritten = write( sock, data, nleft );
888 if( nwritten == -1 ) {
891 if( errno == EAGAIN ) {
896 select(0, NULL, NULL, NULL, &tv);
899 log_info("write failed: %s\n", strerror(errno));
900 return G10ERR_NETWORK;
910 /**** Test code ****/
914 main(int argc, char **argv)
919 struct http_context hd;
922 log_set_name("http-test");
929 fprintf(stderr,"usage: http-test uri\n");
934 rc = parse_uri( &uri, *argv );
936 log_error("`%s': %s\n", *argv, g10_errstr(rc));
937 release_parsed_uri( uri );
941 printf("Scheme: %s\n", uri->scheme );
942 printf("Host : %s\n", uri->host );
943 printf("Port : %u\n", uri->port );
944 printf("Path : %s\n", uri->path );
945 for( r=uri->params; r; r = r->next ) {
946 printf("Params: %s=%s", r->name, r->value );
947 if( strlen( r->value ) != r->valuelen )
948 printf(" [real length=%d]", (int)r->valuelen );
951 for( r=uri->query; r; r = r->next ) {
952 printf("Query : %s=%s", r->name, r->value );
953 if( strlen( r->value ) != r->valuelen )
954 printf(" [real length=%d]", (int)r->valuelen );
957 release_parsed_uri( uri ); uri = NULL;
959 rc = http_open_document( &hd, *argv, 0 );
961 log_error("can't get `%s': %s\n", *argv, g10_errstr(rc));
964 log_info("open_http_document succeeded; status=%u\n", hd.status_code );
965 while( (c=iobuf_get( hd.fp_read)) != -1 )