See ChangeLog: Sat Jan 16 21:25:17 CET 1999 Werner Koch
authorWerner Koch <wk@gnupg.org>
Sat, 16 Jan 1999 20:26:09 +0000 (20:26 +0000)
committerWerner Koch <wk@gnupg.org>
Sat, 16 Jan 1999 20:26:09 +0000 (20:26 +0000)
TODO
g10/options.skel
include/http.h [new file with mode: 0644]
util/http.c [new file with mode: 0644]

diff --git a/TODO b/TODO
index 7157a98..55380b5 100644 (file)
--- a/TODO
+++ b/TODO
@@ -27,7 +27,11 @@ Important
 > gpg: keyblock resource `/home/jam/.gnupg/pubring.gpg': file open error
 > gpg: OOPS in close enum_keyblocks - ignored
 
-
+    > Indeed, comparing zero to 0xfe returns 2, not -something, and this is
+> the problem. This seems to fix it, but I don't know how you want to
+> handle this.
+>
+  I'll better write a autoconf test as memcmp is used all over the place.
 
 Needed
 ------
@@ -69,4 +73,6 @@ Nice to have
     * change the fake_data stuff to mpi_set_opaque
     * How about letting something like 'gpg --version -v', list the
       effective options.
+    * Stats about used random numbers.
+
 
index 4b6a3fc..3e6777b 100644 (file)
@@ -62,5 +62,6 @@ lock-once
 # you will be asked in such a case whether GnuPG should try to
 # import the key from that server (server do syncronize with each
 # others and DNS Round-Robin may give you a random server each time).
-#keyserver keys.pgp.net
+# Use "host -l pgp.net | grep www" to figure out a keyserver.
+#keyserver wwwkeys.eu.pgp.net
 
diff --git a/include/http.h b/include/http.h
new file mode 100644 (file)
index 0000000..a439b4b
--- /dev/null
@@ -0,0 +1,62 @@
+/* http.h  -  HTTP protocol handler
+ *     Copyright (C) 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+#ifndef G10_HTTP_H
+#define G10_HTTP_H 1
+
+#include "iobuf.h"
+
+struct uri_tuple {
+    struct uri_tuple *next;
+    const char *name;  /* a pointer into name */
+    char  *value;      /* a pointer to value (a Nul is always appended) */
+    size_t valuelen;   /* and the real length of the value */
+                       /* because the value may contain embedded Nuls */
+};
+typedef struct uri_tuple *URI_TUPLE;
+
+struct parsed_uri {
+    /* all these pointers point into buffer; most stuff is not escaped */
+    char *scheme;      /* pointer to the scheme string (lowercase) */
+    char *host;        /* host (converted to lowercase) */
+    ushort port;       /* port (always set if the host is set) */
+    char *path;        /* the path */
+    URI_TUPLE params;  /* ";xxxxx" */
+    URI_TUPLE query;   /* "?xxx=yyy" */
+    char buffer[1];    /* buffer which holds a (modified) copy of the URI */
+};
+typedef struct parsed_uri *PARSED_URI;
+
+struct http_context {
+    int initialized;
+    unsigned int status_code;
+    int socket;
+    IOBUF fp_read;
+    IOBUF fp_write;
+    int is_http_0_9;
+    PARSED_URI uri;
+    byte *buffer;         /* line buffer */
+    unsigned buffer_size;
+};
+typedef struct http_context *HTTP_HD;
+
+int open_http_document( HTTP_HD hd, const char *document, unsigned int flags );
+void close_http_document( HTTP_HD hd );
+
+#endif /*G10_HTTP_H*/
diff --git a/util/http.c b/util/http.c
new file mode 100644 (file)
index 0000000..949cdb3
--- /dev/null
@@ -0,0 +1,625 @@
+/* http.c  -  HTTP protocol handler
+ *     Copyright (C) 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include "util.h"
+#include "iobuf.h"
+#include "i18n.h"
+
+#include "http.h"
+
+#define MAX_LINELEN 20000  /* max. length of a HTTP line */
+#define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz"   \
+                       "ABCDEFGHIJKLMNOPQRSTUVWXYZ"   \
+                       "01234567890@"                 \
+                       "!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
+
+#ifndef EAGAIN
+  #define EAGAIN  EWOULDBLOCK
+#endif
+
+static int parse_uri( PARSED_URI *ret_uri, const char *uri );
+static void release_parsed_uri( PARSED_URI uri );
+static int do_parse_uri( PARSED_URI uri, int only_local_part );
+static int remove_escapes( byte *string );
+static int insert_escapes( byte *buffer, const byte *string,
+                                        const byte *special );
+static URI_TUPLE parse_tuple( byte *string );
+static int send_request( HTTP_HD hd );
+static byte *build_rel_path( PARSED_URI uri );
+static int parse_response( HTTP_HD hd );
+
+static int connect_server( const char *server, ushort port );
+static int write_server( int socket, const char *data, size_t length );
+
+
+
+int
+open_http_document( HTTP_HD hd, const char *document, unsigned int flags )
+{
+    int rc;
+
+    if( flags )
+       return G10ERR_INV_ARG;
+
+    /* initialize the handle */
+    memset( hd, 0, sizeof *hd );
+    hd->socket = -1;
+
+    rc = parse_uri( &hd->uri, document );
+    if( rc )
+       goto failure;
+
+    rc = send_request( hd );
+    if( rc )
+       goto failure;
+
+    hd->fp_read = iobuf_fdopen( hd->socket , "r" );
+    if( !hd->fp_read )
+       goto failure;
+
+    rc = parse_response( hd );
+    if( rc == -1 ) { /* no response from server */
+       /* Hmmm, should we set some errro variable or map errors */
+       goto failure;
+    }
+
+    if( !rc )
+       hd->is_http_0_9 = 1;
+    else
+       hd->status_code = rc ;
+
+    hd->initialized = 1;
+    return 0;
+
+  failure:
+    if( !hd->fp_read && !hd->fp_write )
+       close( hd->socket );
+    iobuf_close( hd->fp_read );
+    iobuf_close( hd->fp_write);
+    release_parsed_uri( hd->uri );
+
+    return rc;
+}
+
+void
+close_http_document( HTTP_HD hd )
+{
+    if( !hd || !hd->initialized )
+       return;
+    if( !hd->fp_read && !hd->fp_write )
+       close( hd->socket );
+    iobuf_close( hd->fp_read );
+    iobuf_close( hd->fp_write );
+    release_parsed_uri( hd->uri );
+    m_free( hd->buffer );
+    hd->initialized = 0;
+}
+
+
+
+/****************
+ * Parse an URI and put the result into the newly allocated ret_uri.
+ * The caller must always use release_parsed_uri to releases the
+ * resources (even on an error).
+ */
+static int
+parse_uri( PARSED_URI *ret_uri, const char *uri )
+{
+   *ret_uri = m_alloc_clear( sizeof(**ret_uri) + strlen(uri) );
+   strcpy( (*ret_uri)->buffer, uri );
+   return do_parse_uri( *ret_uri, 0 );
+}
+
+static void
+release_parsed_uri( PARSED_URI uri )
+{
+    if( uri )
+    {
+       URI_TUPLE r, r2;
+
+       for( r = uri->query; r; r = r2 ) {
+           r2 = r->next;
+           m_free( r );
+       }
+       m_free( uri );
+    }
+}
+
+static int
+do_parse_uri( PARSED_URI uri, int only_local_part )
+{
+    URI_TUPLE *tail;
+    char *p, *p2, *p3;
+    int n;
+
+    p = uri->buffer;
+    n = strlen( uri->buffer );
+    /* initialize all fields to an empty string or an empty list */
+    uri->scheme = uri->host = uri->path = p + n;
+    uri->port = 0;
+    uri->params = uri->query = NULL;
+
+    /* a quick validity check */
+    if( strspn( p, VALID_URI_CHARS) != n )
+       return G10ERR_BAD_URI; /* invalid characters found */
+
+    if( !only_local_part ) {
+       /* find the scheme */
+       if( !(p2 = strchr( p, ':' ) ) || p2 == p )
+          return G10ERR_BAD_URI; /* No scheme */
+       *p2++ = 0;
+       strlwr( p );
+       uri->scheme = p;
+       if( !strcmp( uri->scheme, "http" ) )
+           ;
+       else if( !strcmp( uri->scheme, "x-hkp" ) ) /* same as HTTP */
+           ;
+       else
+           return G10ERR_INVALID_URI; /* Unsupported scheme */
+
+       p = p2;
+
+       /* find the hostname */
+       if( *p != '/' )
+           return G10ERR_INVALID_URI; /* does not start with a slash */
+
+       p++;
+       if( *p == '/' ) {  /* there seems to be a hostname */
+           p++;
+           if( (p2 = strchr(p, '/')) )
+               *p2++ = 0;
+           strlwr( p );
+           uri->host = p;
+           if( (p3=strchr( p, ':' )) ) {
+               *p3++ = 0;
+               uri->port = atoi( p3 );
+           }
+           else
+              uri->port = 80;
+           uri->host = p;
+           if( (n = remove_escapes( uri->host )) < 0 )
+               return G10ERR_BAD_URI;
+           if( n != strlen( p ) )
+               return G10ERR_BAD_URI; /* hostname with a Nul in it */
+           p = p2 ? p2 : NULL;
+       }
+    } /* end global URI part */
+
+    /* parse the pathname part */
+    if( !p || !*p ) /* we don't have a path */
+       return 0; /* and this is okay */
+
+    /* fixme: here we have to check params */
+
+    /* do we have a query part */
+    if( (p2 = strchr( p, '?' )) )
+       *p2++ = 0;
+
+    uri->path = p;
+    if( (n = remove_escapes( p )) < 0 )
+       return G10ERR_BAD_URI;
+    if( n != strlen( p ) )
+       return G10ERR_BAD_URI; /* path with a Nul in it */
+    p = p2 ? p2 : NULL;
+
+    if( !p || !*p ) /* we don't have a query string */
+       return 0;   /* okay */
+
+    /* now parse the query string */
+    tail = &uri->query;
+    for(;;) {
+       URI_TUPLE elem;
+
+       if( (p2 = strchr( p, '&' )) )
+           *p2++ = 0;
+       if( !(elem = parse_tuple( p )) )
+           return G10ERR_BAD_URI;
+       *tail = elem;
+       tail = &elem->next;
+
+       if( !p2 )
+          break; /* ready */
+       p = p2;
+    }
+
+    return 0;
+}
+
+
+
+/****************
+ * Remove all %xx escapes; this is done inplace.
+ * Returns: new length of the string.
+ */
+static int
+remove_escapes( byte *string )
+{
+    int n = 0;
+    byte *p, *s;
+
+    for(p=s=string; *s ; s++ ) {
+       if( *s == '%' ) {
+           if( s[1] && s[2] && isxdigit(s[1]) && isxdigit(s[2]) ) {
+               s++;
+               *p  = *s >= '0' && *s <= '9' ? *s - '0' :
+                     *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
+               *p <<= 4;
+               s++;
+               *p |= *s >= '0' && *s <= '9' ? *s - '0' :
+                     *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
+               p++;
+               n++;
+           }
+           else {
+               *p++ = *s++;
+               if( *s )
+                  *p++ = *s++;
+               if( *s )
+                  *p++ = *s++;
+               if( *s )
+                  *p = 0;
+               return -1; /* bad URI */
+           }
+       }
+       else
+       {
+           *p++ = *s;
+           n++;
+       }
+    }
+    *p = 0; /* always keep a string terminator */
+    return n;
+}
+
+
+static int
+insert_escapes( byte *buffer, const byte *string, const byte *special )
+{
+    int n = 0;
+
+    for( ; *string; string++ ) {
+       if( strchr( VALID_URI_CHARS, *string )
+           && !strchr( special, *string ) )  {
+           if( buffer )
+               *buffer++ = *string;
+           n++;
+       }
+       else {
+           if( buffer ) {
+               sprintf( buffer, "%02X", *string );
+               buffer += 3;
+           }
+           n += 3;
+       }
+    }
+    return n;
+}
+
+
+
+
+
+static URI_TUPLE
+parse_tuple( byte *string )
+{
+    byte *p = string;
+    byte *p2;
+    int n;
+    URI_TUPLE tuple;
+
+    if( (p2 = strchr( p, '=' )) )
+       *p2++ = 0;
+    if( (n = remove_escapes( p )) < 0 )
+       return NULL; /* bad URI */
+    if( n != strlen( p ) )
+       return NULL; /* name with a Nul in it */
+    tuple = m_alloc_clear( sizeof *tuple );
+    tuple->name = p;
+    if( !p2 )  {
+       /* we have only the name, so we assume an empty value string */
+       tuple->value = p + strlen(p);
+       tuple->valuelen = 0;
+    }
+    else { /* name and value */
+       if( (n = remove_escapes( p2 )) < 0 ) {
+           m_free( tuple );
+           return NULL; /* bad URI */
+       }
+       tuple->value = p2;
+       tuple->valuelen = n;
+    }
+    return tuple;
+}
+
+
+/****************
+ * Send a HTTP request to the server
+ * Returns 0 if the request was successful
+ */
+static int
+send_request( HTTP_HD hd )
+{
+    const byte *server;
+    byte *request, *p;
+    ushort port;
+    int rc;
+
+    server = *hd->uri->host? hd->uri->host : "localhost";
+    port   = hd->uri->port?  hd->uri->port : 80;
+
+    hd->socket = connect_server( server, port );
+    if( hd->socket == -1 )
+       return G10ERR_NETWORK;
+
+    p = build_rel_path( hd->uri );
+    request = m_alloc( strlen(p) + 20 );
+    sprintf( request, "GET %s%s HTTP/1.0\r\n\r\n", *p == '/'? "":"/", p );
+    m_free(p);
+
+    rc = write_server( hd->socket, request, strlen(request) );
+    m_free( request );
+    shutdown( hd->socket, 1 );
+
+    return rc;
+}
+
+
+
+
+/****************
+ * Build the relative path from the parsed URI.
+ * Minimal implementation.
+ */
+static byte*
+build_rel_path( PARSED_URI uri )
+{
+    URI_TUPLE r;
+    byte *rel_path, *p;
+    int n;
+
+    /* count the needed space */
+    n = insert_escapes( NULL, uri->path, "%;?&" );
+    /* fixme: add params */
+    for( r=uri->query; r; r = r->next ) {
+       n++; /* '?'/'&' */
+       n += insert_escapes( NULL, r->name, "%;?&=" );
+       n++; /* '='*/
+       n += insert_escapes( NULL, r->value, "%;?&=" );
+    }
+    n++;
+
+    /* now  allocate and copy */
+    p = rel_path = m_alloc( n );
+    n = insert_escapes( p, uri->path, "%;?&" );
+    p += n;
+    /* fixme: add params */
+    for( r=uri->query; r; r = r->next ) {
+       *p++ = r == uri->query? '?':'&';
+       n = insert_escapes( p, r->name, "%;?&=" );
+       p += n;
+       *p++ = '=';
+       /* fixme: use valuelen */
+       n = insert_escapes( p, r->value, "%;?&=" );
+       p += n;
+    }
+    *p = 0;
+    return rel_path;
+}
+
+
+
+/***********************
+ * Parse the response from a server.
+ * Returns: -1 for no response or error
+ *          0  for a http 0.9 response
+ *        nnn  http 1.0 status code
+ */
+static int
+parse_response( HTTP_HD hd )
+{
+    byte *line, *p, *p2;
+    int status;
+    unsigned maxlen, len;
+
+    /* Wait for the status line */
+    do {
+       maxlen = MAX_LINELEN;
+       len = iobuf_read_line( hd->fp_read, &hd->buffer,
+                                           &hd->buffer_size, &maxlen );
+       line = hd->buffer;
+       if( !maxlen )
+           return -1; /* line has been truncated */
+       if( !len )
+           return -1; /* eof */
+    } while( !*line  );
+
+    if( (p = strchr( line, '/')) )
+       *p++ = 0;
+    if( !p || strcmp( line, "HTTP" ) )
+       return 0; /* assume http 0.9 */
+
+    if( (p2 = strpbrk( p, " \t" ) ) ) {
+       *p2++ = 0;
+       p2 += strspn( p2, " \t" );
+    }
+    if( !p2 )
+       return 0; /* assume http 0.9 */
+    p = p2;
+    /* fixme: add HTTP version number check here */
+    if( (p2 = strpbrk( p, " \t" ) ) )
+       *p2++ = 0;
+    if( !isdigit(p[0]) || !isdigit(p[1]) || !isdigit(p[2]) || p[3] )
+       return 0; /* malformed HTTP statuscode - assume HTTP 0.9 */
+    status = atoi( p );
+
+    /* skip all the header lines and wait for the empty line */
+    do {
+       maxlen = MAX_LINELEN;
+       len = iobuf_read_line( hd->fp_read, &hd->buffer,
+                              &hd->buffer_size, &maxlen );
+       line = hd->buffer;
+       /* we ignore truncated lines */
+       if( !len )
+           return -1; /* eof */
+       /* time lineendings */
+       if( (*line == '\r' && line[1] == '\n') || *line == '\n' )
+           *line = 0;
+    } while( len && *line  );
+
+    return status;
+}
+
+
+
+
+
+
+
+
+
+
+static int
+connect_server( const char *server, ushort port )
+{
+    struct sockaddr_in addr;
+    struct hostent *host;
+    int sd;
+
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+    host = gethostbyname((char*)server);
+    if( !host )
+        return -1;
+
+    addr.sin_addr = *(struct in_addr*)host->h_addr;
+
+    sd = socket(AF_INET, SOCK_STREAM, 0);
+    if( sd == -1 )
+       return -1;
+
+    if( connect( sd, (struct sockaddr *)&addr, sizeof addr) == -1 ) {
+       close(sd);
+       return -1;
+    }
+
+    return sd;
+}
+
+
+static int
+write_server( int socket, const char *data, size_t length )
+{
+    int nleft, nwritten;
+
+    nleft = length;
+    while( nleft > 0 ) {
+       nwritten = write( socket, data, nleft );
+       if( nwritten == -1 ) {
+           if( errno == EINTR )
+               continue;
+           if( errno == EAGAIN ) {
+               struct timeval tv;
+
+               tv.tv_sec =  0;
+               tv.tv_usec = 50000;
+               select(0, NULL, NULL, NULL, &tv);
+               continue;
+           }
+           return G10ERR_NETWORK;
+       }
+       nleft -=nwritten;
+       data += nwritten;
+    }
+
+    return 0;
+}
+
+
+/**** Test code ****/
+#ifdef TEST
+
+int
+main(int argc, char **argv)
+{
+    int rc;
+    PARSED_URI uri;
+    URI_TUPLE r;
+    struct http_context hd;
+    int c;
+
+    log_set_name("http-test");
+    if( argc != 2 ) {
+       fprintf(stderr,"usage: http-test uri\n");
+       return 1;
+    }
+    argc--; argv++;
+
+    rc = parse_uri( &uri, *argv );
+    if( rc ) {
+       log_error("`%s': %s\n", *argv, g10_errstr(rc));
+       release_parsed_uri( uri );
+       return 1;
+    }
+
+    printf("Scheme: %s\n", uri->scheme );
+    printf("Host  : %s\n", uri->host );
+    printf("Port  : %u\n", uri->port );
+    printf("Path  : %s\n", uri->path );
+    for( r=uri->params; r; r = r->next ) {
+       printf("Params: %s=%s", r->name, r->value );
+       if( strlen( r->value ) != r->valuelen )
+           printf(" [real length=%d]", (int)r->valuelen );
+       putchar('\n');
+    }
+    for( r=uri->query; r; r = r->next ) {
+       printf("Query : %s=%s", r->name, r->value );
+       if( strlen( r->value ) != r->valuelen )
+           printf(" [real length=%d]", (int)r->valuelen );
+       putchar('\n');
+    }
+    release_parsed_uri( uri ); uri = NULL;
+
+    rc = open_http_document( &hd, *argv, 0 );
+    if( rc ) {
+       log_error("can't get `%s': %s\n", *argv, g10_errstr(rc));
+       return 1;
+    }
+    log_info("open_http_document succeeded; status=%u\n", hd.status_code );
+    while( (c=iobuf_get( hd.fp_read)) != -1 )
+       putchar(c);
+    close_http_document( &hd );
+    return 0;
+}
+#endif /*TEST*/