diff options
-rw-r--r-- | protocols/Makefile | 2 | ||||
-rw-r--r-- | protocols/http_client.c | 239 | ||||
-rw-r--r-- | protocols/http_client.h | 54 | ||||
-rw-r--r-- | protocols/proxy.c | 2 | ||||
-rw-r--r-- | protocols/ssl_bogus.c | 2 | ||||
-rw-r--r-- | protocols/ssl_client.h | 5 | ||||
-rw-r--r-- | protocols/ssl_gnutls.c | 33 | ||||
-rw-r--r-- | protocols/ssl_nss.c | 4 | ||||
-rw-r--r-- | protocols/ssl_openssl.c | 4 | ||||
-rw-r--r-- | sock.h | 3 |
10 files changed, 331 insertions, 17 deletions
diff --git a/protocols/Makefile b/protocols/Makefile index c5f938fd..1ed6b52e 100644 --- a/protocols/Makefile +++ b/protocols/Makefile @@ -9,7 +9,7 @@ -include ../Makefile.settings # [SH] Program variables -objects = md5.o nogaim.o proxy.o sha.o util.o $(SSL_CLIENT) +objects = http_client.o md5.o nogaim.o proxy.o sha.o $(SSL_CLIENT) util.o # [SH] The next two lines should contain the directory name (in $(subdirs)) # and the name of the object file, which should be linked into diff --git a/protocols/http_client.c b/protocols/http_client.c new file mode 100644 index 00000000..f631981f --- /dev/null +++ b/protocols/http_client.c @@ -0,0 +1,239 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2005 Wilmer van der Gaast and others * + \********************************************************************/ + +/* HTTP(S) module (actually, it only does HTTPS right now) */ + +/* + This program 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. + + This program 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <string.h> + +#include "sock.h" +#include "http_client.h" + + +static void http_connected( gpointer data, int source, GaimInputCondition cond ); +static void http_ssl_connected( gpointer data, void *source, GaimInputCondition cond ); +static void http_incoming_data( gpointer data, int source, GaimInputCondition cond ); + + +void *http_dorequest( char *host, int port, http_input_function func, int ssl, char *request, gpointer data ) +{ + struct http_request *req; + int error = 0; + + req = g_new0( struct http_request, 1 ); + + if( ssl ) + { + req->ssl = ssl_connect( host, port, http_ssl_connected, req ); + if( req->ssl == NULL ) + error = 1; + } + else + { + req->fd = proxy_connect( host, port, http_connected, req ); + if( req->fd < 0 ) + error = 1; + } + + if( error ) + { + g_free( req ); + return( NULL ); + } + + req->request = g_strdup( request ); + req->request_length = strlen( request ); + + return( req ); +} + +/* This one is actually pretty simple... Might get more calls if we can't write + the whole request at once. */ +static void http_connected( gpointer data, int source, GaimInputCondition cond ) +{ + struct http_request *req = data; + int st; + + if( source < 0 ) + goto error; + + if( req->inpa > 0 ) + gaim_input_remove( req->inpa ); + + sock_make_nonblocking( req->fd ); + + if( req->ssl ) + { + st = ssl_write( req->ssl, req->request + req->bytes_written, + req->request_length - req->bytes_written ); + if( st < 0 ) + { + if( ssl_errno != SSL_AGAIN ) + { + ssl_disconnect( req->ssl ); + goto error; + } + } + } + else + { + st = write( source, req->request + req->bytes_written, + req->request_length - req->bytes_written ); + if( st < 0 ) + { + if( !sockerr_again() ) + { + close( req->fd ); + goto error; + } + } + } + + if( st > 0 ) + req->bytes_written += st; + + if( req->bytes_written < req->request_length ) + req->inpa = gaim_input_add( source, + req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_WRITE, + http_connected, req ); + else + req->inpa = gaim_input_add( source, GAIM_INPUT_READ, http_incoming_data, req ); + + return; + +error: + req->func( req ); + + g_free( req->request ); + g_free( req ); + + return; +} + +static void http_ssl_connected( gpointer data, void *source, GaimInputCondition cond ) +{ + struct http_request *req = data; + + if( source == NULL ) + return http_connected( data, -1, cond ); + + req->fd = ssl_getfd( source ); + + return http_connected( data, req->fd, cond ); +} + +static void http_incoming_data( gpointer data, int source, GaimInputCondition cond ) +{ + struct http_request *req = data; + int evil_server = 0; + char buffer[2048]; + char *end1, *end2; + int st; + + if( req->inpa > 0 ) + gaim_input_remove( req->inpa ); + + if( req->ssl ) + { + st = ssl_read( req->ssl, buffer, sizeof( buffer ) ); + if( st < 0 ) + { + if( ssl_errno != SSL_AGAIN ) + { + goto cleanup; + } + } + else if( st == 0 ) + { + goto got_reply; + } + } + else + { + st = read( req->fd, buffer, sizeof( buffer ) ); + if( st < 0 ) + { + if( !sockerr_again() ) + { + goto cleanup; + } + } + else if( st == 0 ) + { + goto got_reply; + } + } + + if( st > 0 ) + { + req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + st + 1 ); + memcpy( req->reply_headers + req->bytes_read, buffer, st ); + } + + /* There will be more! */ + req->inpa = gaim_input_add( req->fd, + req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_READ, + http_incoming_data, req ); + + return; + +got_reply: + /* Zero termination is very convenient. */ + req->reply_headers[req->bytes_read] = 0; + + /* Find the separation between headers and body, and keep stupid + webservers in mind. */ + end1 = strstr( req->reply_headers, "\r\n\r\n" ); + end2 = strstr( req->reply_headers, "\n\n" ); + + if( end2 && end2 < end1 ) + { + end1 = end2; + evil_server = 1; + } + + if( end1 ) + { + *end1 = 0; + + if( evil_server ) + req->reply_body = end1 + 2; + else + req->reply_body = end1 + 4; + } + + /* Assume that a closed connection means we're finished, this indeed + breaks with keep-alive connections and faulty connections. */ + req->finished = 1; + +cleanup: + if( req->ssl ) + ssl_disconnect( req->ssl ); + else + close( req->fd ); + + req->func( req ); + + g_free( req->request ); + g_free( req->reply_headers ); + g_free( req ); +} diff --git a/protocols/http_client.h b/protocols/http_client.h new file mode 100644 index 00000000..440fdc9d --- /dev/null +++ b/protocols/http_client.h @@ -0,0 +1,54 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2005 Wilmer van der Gaast and others * + \********************************************************************/ + +/* HTTP(S) module (actually, it only does HTTPS right now) */ + +/* + This program 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. + + This program 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <glib.h> + +#include "ssl_client.h" + +struct http_request; + +typedef void (*http_input_function)( struct http_request * ); + +struct http_request +{ + char *request; + int request_length; + int status_code; + char *reply_headers; + char *reply_body; + int finished; + + void *ssl; + int fd; + + int inpa; + int bytes_written; + int bytes_read; + + http_input_function func; + gpointer data; +}; + +void *http_dorequest( char *host, int port, http_input_function func, int ssl, char *request, gpointer data ); diff --git a/protocols/proxy.c b/protocols/proxy.c index c658a163..1ca35dfe 100644 --- a/protocols/proxy.c +++ b/protocols/proxy.c @@ -105,8 +105,6 @@ static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpoin gaim_cond |= GAIM_INPUT_READ; if (condition & GAIM_WRITE_COND) gaim_cond |= GAIM_INPUT_WRITE; -// if (condition & GAIM_ERR_COND) -// fprintf( stderr, "ERROR! fd=%d\n", g_io_channel_unix_get_fd( source ) ); closure->function(closure->data, g_io_channel_unix_get_fd(source), gaim_cond); diff --git a/protocols/ssl_bogus.c b/protocols/ssl_bogus.c index 1ee0df4c..3766baaa 100644 --- a/protocols/ssl_bogus.c +++ b/protocols/ssl_bogus.c @@ -27,7 +27,7 @@ int ssl_errno; -void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data ) +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) { return( NULL ); } diff --git a/protocols/ssl_client.h b/protocols/ssl_client.h index 719cd0c4..89189db9 100644 --- a/protocols/ssl_client.h +++ b/protocols/ssl_client.h @@ -32,10 +32,11 @@ extern int ssl_errno; -typedef void (*SslInputFunction)(gpointer, void*, GaimInputCondition); +typedef void (*ssl_input_function)(gpointer, void*, GaimInputCondition); -G_MODULE_EXPORT void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data ); +G_MODULE_EXPORT void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ); G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len ); G_MODULE_EXPORT int ssl_write( void *conn, const char *buf, int len ); G_MODULE_EXPORT void ssl_disconnect( void *conn_ ); G_MODULE_EXPORT int ssl_getfd( void *conn ); +G_MODULE_EXPORT GaimInputCondition ssl_getdirection( void *conn ); diff --git a/protocols/ssl_gnutls.c b/protocols/ssl_gnutls.c index c2eb6906..2e307aab 100644 --- a/protocols/ssl_gnutls.c +++ b/protocols/ssl_gnutls.c @@ -37,7 +37,7 @@ static gboolean initialized = FALSE; struct scd { - SslInputFunction func; + ssl_input_function func; gpointer data; int fd; gboolean established; @@ -50,7 +50,7 @@ struct scd static void ssl_connected( gpointer data, gint source, GaimInputCondition cond ); -void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data ) +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) { struct scd *conn = g_new0( struct scd, 1 ); @@ -116,9 +116,7 @@ static void ssl_handshake( gpointer data, gint source, GaimInputCondition cond ) { if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED ) { - conn->inpa = gaim_input_add( conn->fd, - gnutls_record_get_direction( conn->session ) ? - GAIM_INPUT_WRITE : GAIM_INPUT_READ, + conn->inpa = gaim_input_add( conn->fd, ssl_getdirection( conn ), ssl_handshake, data ); } else @@ -144,25 +142,40 @@ static void ssl_handshake( gpointer data, gint source, GaimInputCondition cond ) int ssl_read( void *conn, char *buf, int len ) { + int st; + if( !((struct scd*)conn)->established ) { ssl_errno = SSL_NOHANDSHAKE; return( -1 ); } - return( gnutls_record_recv( ((struct scd*)conn)->session, buf, len ) ); + st = gnutls_record_recv( ((struct scd*)conn)->session, buf, len ); + + ssl_errno = SSL_OK; + if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED ) + ssl_errno = SSL_AGAIN; + return st; } int ssl_write( void *conn, const char *buf, int len ) { + int st; + if( !((struct scd*)conn)->established ) { ssl_errno = SSL_NOHANDSHAKE; return( -1 ); } - return( gnutls_record_send( ((struct scd*)conn)->session, buf, len ) ); + st = gnutls_record_send( ((struct scd*)conn)->session, buf, len ); + + ssl_errno = SSL_OK; + if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED ) + ssl_errno = SSL_AGAIN; + + return st; } void ssl_disconnect( void *conn_ ) @@ -183,3 +196,9 @@ int ssl_getfd( void *conn ) { return( ((struct scd*)conn)->fd ); } + +GaimInputCondition ssl_getdirection( void *conn ) +{ + return( gnutls_record_get_direction( ((struct scd*)conn)->session ) ? + GAIM_INPUT_WRITE : GAIM_INPUT_READ ); +} diff --git a/protocols/ssl_nss.c b/protocols/ssl_nss.c index d28983fc..dfd32622 100644 --- a/protocols/ssl_nss.c +++ b/protocols/ssl_nss.c @@ -44,7 +44,7 @@ static gboolean initialized = FALSE; struct scd { - SslInputFunction func; + ssl_input_function func; gpointer data; int fd; PRFileDesc *prfd; @@ -90,7 +90,7 @@ static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket) } -void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data ) +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) { struct scd *conn = g_new0( struct scd, 1 ); diff --git a/protocols/ssl_openssl.c b/protocols/ssl_openssl.c index bf87ab73..5a107fc5 100644 --- a/protocols/ssl_openssl.c +++ b/protocols/ssl_openssl.c @@ -40,7 +40,7 @@ static gboolean initialized = FALSE; struct scd { - SslInputFunction func; + ssl_input_function func; gpointer data; int fd; gboolean established; @@ -53,7 +53,7 @@ static void ssl_connected( gpointer data, gint source, GaimInputCondition cond ) -void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data ) +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) { struct scd *conn = g_new0( struct scd, 1 ); SSL_METHOD *meth; @@ -1,3 +1,6 @@ +#include <errno.h> +#include <fcntl.h> + #ifndef _WIN32 #include <unistd.h> #include <sys/socket.h> |