diff options
Diffstat (limited to 'protocols')
| -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 | 
9 files changed, 328 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; | 
