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);
76 static int write_server( int sock, const char *data, size_t length );
88 static int initialized;
89 static WSADATA wsdata;
94 if( WSAStartup( 0x0101, &wsdata ) ) {
95 log_error ("error initializing socket library: ec=%d\n",
96 (int)WSAGetLastError () );
99 if( wsdata.wVersion < 0x0001 ) {
100 log_error ("socket library version is %x.%x - but 1.1 needed\n",
101 LOBYTE(wsdata.wVersion), HIBYTE(wsdata.wVersion));
105 atexit ( deinit_sockets );
110 static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
111 "abcdefghijklmnopqrstuvwxyz"
115 * create a radix64 encoded string.
118 /* TODO: This is a duplicate of code in g10/armor.c. Better to use a
119 single copy in strgutil.c */
121 make_radix64_string( const byte *data, size_t len )
125 buffer = p = m_alloc( (len+2)/3*4 + 1 );
126 for( ; len >= 3 ; len -= 3, data += 3 ) {
127 *p++ = bintoasc[(data[0] >> 2) & 077];
128 *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
129 *p++ = bintoasc[(((data[1]<<2)&074)|((data[2]>>6)&03))&077];
130 *p++ = bintoasc[data[2]&077];
133 *p++ = bintoasc[(data[0] >> 2) & 077];
134 *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
135 *p++ = bintoasc[((data[1]<<2)&074)];
137 else if( len == 1 ) {
138 *p++ = bintoasc[(data[0] >> 2) & 077];
139 *p++ = bintoasc[(data[0] <<4)&060];
146 http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url,
147 unsigned int flags, const char *proxy )
151 if( !(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST) )
152 return G10ERR_INV_ARG;
154 /* initialize the handle */
155 memset( hd, 0, sizeof *hd );
158 hd->req_type = reqtype;
161 rc = parse_uri( &hd->uri, url );
163 rc = send_request( hd, proxy );
165 hd->fp_write = iobuf_sockopen( hd->sock , "w" );
172 if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
173 sock_close( hd->sock );
174 iobuf_close( hd->fp_read );
175 iobuf_close( hd->fp_write);
176 release_parsed_uri( hd->uri );
184 http_start_data( HTTP_HD hd )
186 iobuf_flush ( hd->fp_write );
188 write_server (hd->sock, "\r\n", 2);
195 http_wait_response( HTTP_HD hd, unsigned int *ret_status )
199 http_start_data( hd ); /* make sure that we are in the data */
202 hd->sock = dup( hd->sock );
204 return G10ERR_GENERAL;
206 iobuf_ioctl (hd->fp_write, 1, 1, NULL); /* keep the socket open */
207 iobuf_close (hd->fp_write);
209 if ( !(hd->flags & HTTP_FLAG_NO_SHUTDOWN) )
210 shutdown( hd->sock, 1 );
213 hd->fp_read = iobuf_sockopen( hd->sock , "r" );
215 return G10ERR_GENERAL;
217 rc = parse_response( hd );
218 if( !rc && ret_status )
219 *ret_status = hd->status_code;
226 http_open_document( HTTP_HD hd, const char *document,
227 unsigned int flags, const char *proxy )
231 rc = http_open( hd, HTTP_REQ_GET, document, flags, proxy );
235 rc = http_wait_response( hd, NULL );
244 http_close( HTTP_HD hd )
246 if( !hd || !hd->initialized )
248 if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
249 sock_close( hd->sock );
250 iobuf_close( hd->fp_read );
251 iobuf_close( hd->fp_write );
252 release_parsed_uri( hd->uri );
253 m_free( hd->buffer );
260 * Parse an URI and put the result into the newly allocated ret_uri.
261 * The caller must always use release_parsed_uri to releases the
262 * resources (even on an error).
265 parse_uri( PARSED_URI *ret_uri, const char *uri )
267 *ret_uri = m_alloc_clear( sizeof(**ret_uri) + strlen(uri) );
268 strcpy( (*ret_uri)->buffer, uri );
269 return do_parse_uri( *ret_uri, 0 );
273 release_parsed_uri( PARSED_URI uri )
279 for( r = uri->query; r; r = r2 ) {
288 do_parse_uri( PARSED_URI uri, int only_local_part )
295 n = strlen( uri->buffer );
296 /* initialize all fields to an empty string or an empty list */
297 uri->scheme = uri->host = uri->path = p + n;
299 uri->params = uri->query = NULL;
301 /* a quick validity check */
302 if( strspn( p, VALID_URI_CHARS) != n )
303 return G10ERR_BAD_URI; /* invalid characters found */
305 if( !only_local_part ) {
306 /* find the scheme */
307 if( !(p2 = strchr( p, ':' ) ) || p2 == p )
308 return G10ERR_BAD_URI; /* No scheme */
313 if( !strcmp( uri->scheme, "http" ) )
315 else if( !strcmp( uri->scheme, "x-hkp" ) ) /* same as HTTP */
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 );
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 );
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) + 50);
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:"" );
564 sprintf( request, "%s %s%s HTTP/1.0\r\nHost: %s\r\n%s",
565 hd->req_type == HTTP_REQ_GET ? "GET" :
566 hd->req_type == HTTP_REQ_HEAD? "HEAD":
567 hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
568 *p == '/'? "":"/", p, server, auth?auth:"");
572 rc = write_server( hd->sock, request, strlen(request) );
581 * Build the relative path from the parsed URI.
582 * Minimal implementation.
585 build_rel_path( PARSED_URI uri )
591 /* count the needed space */
592 n = insert_escapes( NULL, uri->path, "%;?&" );
593 /* todo: build params */
594 for( r=uri->query; r; r = r->next ) {
596 n += insert_escapes( NULL, r->name, "%;?&=" );
598 n += insert_escapes( NULL, r->value, "%;?&=" );
602 /* now allocate and copy */
603 p = rel_path = m_alloc( n );
604 n = insert_escapes( p, uri->path, "%;?&" );
606 /* todo: add params */
607 for( r=uri->query; r; r = r->next ) {
608 *p++ = r == uri->query? '?':'&';
609 n = insert_escapes( p, r->name, "%;?&=" );
612 /* todo: use valuelen */
613 n = insert_escapes( p, r->value, "%;?&=" );
622 /***********************
623 * Parse the response from a server.
624 * Returns: errorcode and sets some fileds in the handle
627 parse_response( HTTP_HD hd )
630 unsigned maxlen, len;
632 /* Wait for the status line */
634 maxlen = MAX_LINELEN;
635 len = iobuf_read_line( hd->fp_read, &hd->buffer,
636 &hd->buffer_size, &maxlen );
639 return -1; /* line has been truncated */
644 if( (p = strchr( line, '/')) )
646 if( !p || strcmp( line, "HTTP" ) )
647 return 0; /* assume http 0.9 */
649 if( (p2 = strpbrk( p, " \t" ) ) ) {
651 p2 += strspn( p2, " \t" );
654 return 0; /* assume http 0.9 */
656 /* todo: add HTTP version number check here */
657 if( (p2 = strpbrk( p, " \t" ) ) )
659 if( !isdigit(p[0]) || !isdigit(p[1]) || !isdigit(p[2]) || p[3] ) {
660 /* malformed HTTP statuscode - assume HTTP 0.9 */
662 hd->status_code = 200;
665 hd->status_code = atoi( p );
667 /* skip all the header lines and wait for the empty line */
669 maxlen = MAX_LINELEN;
670 len = iobuf_read_line( hd->fp_read, &hd->buffer,
671 &hd->buffer_size, &maxlen );
673 /* we ignore truncated lines */
676 /* time lineendings */
677 if( (*line == '\r' && line[1] == '\n') || *line == '\n' )
679 } while( len && *line );
688 struct sockaddr_in mya;
689 struct sockaddr_in peer;
695 if( (fd=socket(AF_INET,SOCK_STREAM, 0)) == -1 ) {
696 log_error("socket() failed: %s\n", strerror(errno));
700 if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (byte*)&i, sizeof(i) ) )
701 log_info("setsockopt(SO_REUSEADDR) failed: %s\n", strerror(errno) );
703 mya.sin_family=AF_INET;
704 memset(&mya.sin_addr, 0, sizeof(mya.sin_addr));
705 mya.sin_port=htons(11371);
707 if( bind( fd, (struct sockaddr *)&mya, sizeof(mya)) ) {
708 log_error("bind to port 11371 failed: %s\n", strerror(errno) );
713 if( listen( fd, 5 ) ) {
714 log_error("listen failed: %s\n", strerror(errno) );
723 if( select( fd+1, &rfds, NULL, NULL, NULL) <= 0 )
724 continue; /* ignore any errors */
726 if( !FD_ISSET( fd, &rfds ) )
729 addrlen = sizeof peer;
730 client = accept( fd, (struct sockaddr *)&peer, &addrlen);
734 log_info("connect from %s\n", inet_ntoa( peer.sin_addr ) );
742 fp = fdopen( client , "r" );
743 while( (c=getc(fp)) != EOF )
748 sock_close( client );
758 connect_server( const char *server, ushort port, unsigned int flags )
760 int sock=-1,srv,srvcount=0,connected=0,hostfound=0;
761 struct srventry *srvlist=NULL;
764 unsigned long inaddr;
767 /* Win32 gethostbyname doesn't handle IP addresses internally, so we
768 try inet_addr first on that platform only. */
769 if((inaddr=inet_addr(server))!=SOCKET_ERROR)
771 struct sockaddr_in addr;
773 memset(&addr,0,sizeof(addr));
775 if((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET)
777 log_error("error creating socket: ec=%d\n",(int)WSAGetLastError());
781 addr.sin_family=AF_INET;
782 addr.sin_port=htons(port);
783 memcpy(&addr.sin_addr,&inaddr,sizeof(inaddr));
785 if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
796 /* Do the SRV thing */
797 if(flags&HTTP_FLAG_TRY_SRV)
799 /* We're using SRV, so append the tags */
800 char srvname[MAXDNAME];
801 strcpy(srvname,"_hkp._tcp.");
802 strncat(srvname,server,MAXDNAME-11);
803 srvname[MAXDNAME-1]='\0';
804 srvcount=getsrv(srvname,&srvlist);
810 /* Either we're not using SRV, or the SRV lookup failed. Make
811 up a fake SRV record. */
812 srvlist=m_alloc_clear(sizeof(struct srventry));
814 strncpy(srvlist->target,server,MAXDNAME);
818 #ifdef HAVE_GETADDRINFO
820 for(srv=0;srv<srvcount;srv++)
822 struct addrinfo hints,*res,*ai;
825 sprintf(portstr,"%u",srvlist[srv].port);
826 memset(&hints,0,sizeof(hints));
827 hints.ai_socktype=SOCK_STREAM;
828 if(getaddrinfo(srvlist[srv].target,portstr,&hints,&res)==0)
833 for(ai=res;ai;ai=ai->ai_next)
835 if((sock=socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol))==-1)
837 log_error("error creating socket: %s\n",strerror(errno));
842 if(connect(sock,ai->ai_addr,ai->ai_addrlen)==0)
855 #else /* !HAVE_GETADDRINFO */
857 for(srv=0;srv<srvcount;srv++)
860 struct hostent *host=NULL;
861 struct sockaddr_in addr;
863 memset(&addr,0,sizeof(addr));
865 if((host=gethostbyname(srvlist[srv].target))==NULL)
868 if((sock=socket(host->h_addrtype,SOCK_STREAM,0))==-1)
870 log_error("error creating socket: %s\n",strerror(errno));
874 addr.sin_family=host->h_addrtype;
875 if(addr.sin_family!=AF_INET)
877 log_error("%s: unknown address family\n",srvlist[srv].target);
881 addr.sin_port=htons(srvlist[srv].port);
883 /* Try all A records until one responds. */
884 while(host->h_addr_list[i])
886 if(host->h_length!=4)
888 log_error("%s: illegal address length\n",srvlist[srv].target);
892 memcpy(&addr.sin_addr,host->h_addr_list[i],host->h_length);
894 if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
903 if(host->h_addr_list[i])
906 #endif /* !HAVE_GETADDRINFO */
914 log_error("%s: Unable to connect: ec=%d\n",server,(int)WSAGetLastError());
916 log_error("%s: Host not found: ec=%d\n",server,(int)WSAGetLastError());
919 log_error("%s: %s\n",server,strerror(errno));
921 log_error("%s: Host not found\n",server);
933 write_server( int sock, const char *data, size_t length )
942 nwritten = send (sock, data, nleft, 0);
943 if ( nwritten == SOCKET_ERROR ) {
944 log_info ("write failed: ec=%d\n", (int)WSAGetLastError ());
945 return G10ERR_NETWORK;
948 int nwritten = write( sock, data, nleft );
949 if( nwritten == -1 ) {
952 if( errno == EAGAIN ) {
957 select(0, NULL, NULL, NULL, &tv);
960 log_info("write failed: %s\n", strerror(errno));
961 return G10ERR_NETWORK;
971 /**** Test code ****/
975 main(int argc, char **argv)
980 struct http_context hd;
983 log_set_name("http-test");
990 fprintf(stderr,"usage: http-test uri\n");
995 rc = parse_uri( &uri, *argv );
997 log_error("`%s': %s\n", *argv, g10_errstr(rc));
998 release_parsed_uri( uri );
1002 printf("Scheme: %s\n", uri->scheme );
1003 printf("Host : %s\n", uri->host );
1004 printf("Port : %u\n", uri->port );
1005 printf("Path : %s\n", uri->path );
1006 for( r=uri->params; r; r = r->next ) {
1007 printf("Params: %s=%s", r->name, r->value );
1008 if( strlen( r->value ) != r->valuelen )
1009 printf(" [real length=%d]", (int)r->valuelen );
1012 for( r=uri->query; r; r = r->next ) {
1013 printf("Query : %s=%s", r->name, r->value );
1014 if( strlen( r->value ) != r->valuelen )
1015 printf(" [real length=%d]", (int)r->valuelen );
1018 release_parsed_uri( uri ); uri = NULL;
1020 rc = http_open_document( &hd, *argv, 0, NULL );
1022 log_error("can't get `%s': %s\n", *argv, g10_errstr(rc));
1025 log_info("open_http_document succeeded; status=%u\n", hd.status_code );
1026 while( (c=iobuf_get( hd.fp_read)) != -1 )