diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/Makefile | 37 | ||||
| -rw-r--r-- | lib/events.h | 66 | ||||
| -rw-r--r-- | lib/events_glib.c | 137 | ||||
| -rw-r--r-- | lib/events_libevent.c | 247 | ||||
| -rw-r--r-- | lib/http_client.c | 453 | ||||
| -rw-r--r-- | lib/http_client.h | 57 | ||||
| -rw-r--r-- | lib/ini.c | 90 | ||||
| -rw-r--r-- | lib/ini.h | 43 | ||||
| -rw-r--r-- | lib/md5.c | 392 | ||||
| -rw-r--r-- | lib/md5.h | 85 | ||||
| -rw-r--r-- | lib/misc.c | 481 | ||||
| -rw-r--r-- | lib/misc.h | 51 | ||||
| -rw-r--r-- | lib/proxy.c | 556 | ||||
| -rw-r--r-- | lib/proxy.h | 53 | ||||
| -rw-r--r-- | lib/rc4.c | 179 | ||||
| -rw-r--r-- | lib/rc4.h | 35 | ||||
| -rw-r--r-- | lib/sha.c | 173 | ||||
| -rw-r--r-- | lib/sha.h | 21 | ||||
| -rw-r--r-- | lib/ssl_bogus.c | 57 | ||||
| -rw-r--r-- | lib/ssl_client.h | 42 | ||||
| -rw-r--r-- | lib/ssl_gnutls.c | 206 | ||||
| -rw-r--r-- | lib/ssl_nss.c | 190 | ||||
| -rw-r--r-- | lib/ssl_openssl.c | 221 | ||||
| -rw-r--r-- | lib/url.c | 107 | ||||
| -rw-r--r-- | lib/url.h | 44 | 
25 files changed, 4023 insertions, 0 deletions
| diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 00000000..80cdd9a5 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,37 @@ +########################### +## Makefile for BitlBee  ## +##                       ## +## Copyright 2006 Lintux ## +########################### + +### DEFINITIONS + +-include ../Makefile.settings + +# [SH] Program variables +objects = $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o rc4.o sha.o $(SSL_CLIENT) url.o + +CFLAGS += -Wall +LFLAGS += -r + +# [SH] Phony targets +all: lib.o + +.PHONY: all clean distclean + +clean: $(subdirs) +	rm -f *.o $(OUTFILE) core + +distclean: clean $(subdirs) + +### MAIN PROGRAM + +lib.o: $(objects) $(subdirs) +	@echo '*' Linking lib.o +	@$(LD) $(LFLAGS) $(objects) -o lib.o + +$(objects): ../Makefile.settings Makefile + +$(objects): %.o: %.c +	@echo '*' Compiling $< +	@$(CC) -c $(CFLAGS) $< -o $@ diff --git a/lib/events.h b/lib/events.h new file mode 100644 index 00000000..781fca6a --- /dev/null +++ b/lib/events.h @@ -0,0 +1,66 @@ +/* + * nogaim + * + * Copyright (C) 2006 Wilmer van der Gaast and others + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +/* + * Split off the event handling things from proxy.[ch] (and adding timer + * stuff. This to allow BitlBee to use other libs than GLib for event + * handling. + */ + + +#ifndef _EVENTS_H_ +#define _EVENTS_H_ + +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#endif +#include <glib.h> +#include <gmodule.h> + +typedef enum { +	GAIM_INPUT_READ = 1 << 1, +	GAIM_INPUT_WRITE = 1 << 2 +} b_input_condition; +typedef gboolean (*b_event_handler)(gpointer data, gint fd, b_input_condition cond); + +#define GAIM_READ_COND  (G_IO_IN | G_IO_HUP | G_IO_ERR) +#define GAIM_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL) +#define GAIM_ERR_COND   (G_IO_HUP | G_IO_ERR | G_IO_NVAL) + +// #define event_debug( x... ) printf( x ) +#define event_debug( x... ) + +G_MODULE_EXPORT void b_main_init(); +G_MODULE_EXPORT void b_main_run(); +G_MODULE_EXPORT void b_main_quit(); + +G_MODULE_EXPORT gint b_input_add(int fd, b_input_condition cond, b_event_handler func, gpointer data); +G_MODULE_EXPORT gint b_timeout_add(gint timeout, b_event_handler func, gpointer data); +G_MODULE_EXPORT void b_event_remove(gint id); + +#ifdef EVENTS_LIBEVENT +G_MODULE_EXPORT void closesocket(int fd); +#endif + +#endif /* _EVENTS_H_ */ diff --git a/lib/events_glib.c b/lib/events_glib.c new file mode 100644 index 00000000..620720cd --- /dev/null +++ b/lib/events_glib.c @@ -0,0 +1,137 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2006 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* + * Event handling (using GLib) + */ + +/* +  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 +*/ + +#define BITLBEE_CORE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#else +#include "sock.h" +#define ETIMEDOUT WSAETIMEDOUT +#define EINPROGRESS WSAEINPROGRESS +#endif +#include <fcntl.h> +#include <errno.h> +#include "proxy.h" + +typedef struct _GaimIOClosure { +	b_event_handler function; +	guint result; +	gpointer data; +} GaimIOClosure; + +static GMainLoop *loop; + +void b_main_init() +{ +	loop = g_main_new( FALSE ); +} + +void b_main_run() +{ +	g_main_run( loop ); +} + +void b_main_quit() +{ +	g_main_quit( loop ); +} + +static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data) +{ +	GaimIOClosure *closure = data; +	b_input_condition gaim_cond = 0; +	gboolean st; + +	if (condition & GAIM_READ_COND) +		gaim_cond |= GAIM_INPUT_READ; +	if (condition & GAIM_WRITE_COND) +		gaim_cond |= GAIM_INPUT_WRITE; +	 +	event_debug( "gaim_io_invoke( %d, %d, 0x%x )\n", g_io_channel_unix_get_fd(source), condition, data ); + +	st = closure->function(closure->data, g_io_channel_unix_get_fd(source), gaim_cond); +	 +	if( !st ) +		event_debug( "Returned FALSE, cancelling.\n" ); +	 +	return st; +} + +static void gaim_io_destroy(gpointer data) +{ +	event_debug( "gaim_io_destroy( 0x%x )\n", data ); +	g_free(data); +} + +gint b_input_add(gint source, b_input_condition condition, b_event_handler function, gpointer data) +{ +	GaimIOClosure *closure = g_new0(GaimIOClosure, 1); +	GIOChannel *channel; +	GIOCondition cond = 0; +	 +	closure->function = function; +	closure->data = data; +	 +	if (condition & GAIM_INPUT_READ) +		cond |= GAIM_READ_COND; +	if (condition & GAIM_INPUT_WRITE) +		cond |= GAIM_WRITE_COND; +	 +	channel = g_io_channel_unix_new(source); +	closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, +					      gaim_io_invoke, closure, gaim_io_destroy); +	 +	event_debug( "b_input_add( %d, %d, 0x%x, 0x%x ) = %d (0x%x)\n", source, condition, function, data, closure->result, closure ); +	 +	g_io_channel_unref(channel); +	return closure->result; +} + +gint b_timeout_add(gint timeout, b_event_handler func, gpointer data) +{ +	gint st = g_timeout_add(timeout, func, data); +	 +	event_debug( "b_timeout_add( %d, %d, %d ) = %d\n", timeout, func, data, st ); +	 +	return st; +} + +void b_event_remove(gint tag) +{ +	event_debug( "b_event_remove( %d )\n", tag ); +	 +	if (tag > 0) +		g_source_remove(tag); +} diff --git a/lib/events_libevent.c b/lib/events_libevent.c new file mode 100644 index 00000000..1119c2ab --- /dev/null +++ b/lib/events_libevent.c @@ -0,0 +1,247 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2006 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* + * Event handling (using libevent) + */ + +/* +  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 +*/ + +#define BITLBEE_CORE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include "proxy.h" + +#include <sys/time.h> +#include <event.h> + +static guint id_next; +static GHashTable *id_hash; +static int quitting = 0; + +/* Since libevent doesn't handle two event handlers for one fd-condition +   very well (which happens sometimes when BitlBee changes event handlers +   for a combination), let's buid some indexes so we can delete them here +   already, just in time. */ +static GHashTable *read_hash; +static GHashTable *write_hash; + +struct b_event_data +{ +	guint id; +	struct event evinfo; +	gint timeout; +	b_event_handler function; +	void *data; +}; + +void b_main_init() +{ +	event_init(); +	 +	id_next = 1; +	id_hash = g_hash_table_new( g_int_hash, g_int_equal ); +	read_hash = g_hash_table_new( g_int_hash, g_int_equal ); +	write_hash = g_hash_table_new( g_int_hash, g_int_equal ); +} + +void b_main_run() +{ +	event_dispatch(); +} + +void b_main_quit() +{ +	struct timeval tv; +	 +	/* libevent sometimes generates events before really quitting, +	   we want to stop them. */ +	quitting = 1; +	 +	memset( &tv, 0, sizeof( struct timeval ) ); +	event_loopexit( &tv ); +} + +static void b_event_passthrough( int fd, short event, void *data ) +{ +	struct b_event_data *b_ev = data; +	b_input_condition cond = 0; +	int id; +	 +	if( fd >= 0 ) +	{ +		if( event & EV_READ ) +			cond |= GAIM_INPUT_READ; +		if( event & EV_WRITE ) +			cond |= GAIM_INPUT_WRITE; +	} +	 +	event_debug( "b_event_passthrough( %d, %d, 0x%x ) (%d)\n", fd, event, (int) data, b_ev->id ); +	 +	/* Since the called function might cancel this handler already +	   (which free()s b_ev, we have to remember the ID here. */ +	id = b_ev->id; +	 +	if( quitting ) +	{ +		b_event_remove( id ); +		return; +	} +	 +	if( !b_ev->function( b_ev->data, fd, cond ) ) +	{ +		event_debug( "Handler returned FALSE: " ); +		b_event_remove( id ); +	} +	else if( fd == -1 ) +	{ +		struct timeval tv; +		 +		tv.tv_sec = b_ev->timeout / 1000; +		tv.tv_usec = ( b_ev->timeout % 1000 ) * 1000; +		 +		evtimer_add( &b_ev->evinfo, &tv ); +	} +} + +gint b_input_add( gint fd, b_input_condition condition, b_event_handler function, gpointer data ) +{ +	struct b_event_data *b_ev; +	 +	event_debug( "b_input_add( %d, %d, 0x%x, 0x%x ) ", fd, condition, function, data ); +	 +	if( ( condition & GAIM_INPUT_READ  && ( b_ev = g_hash_table_lookup( read_hash,  &fd ) ) ) || +	    ( condition & GAIM_INPUT_WRITE && ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) ) +	{ +		/* We'll stick with this libevent entry, but give it a new BitlBee id. */ +		g_hash_table_remove( id_hash, &b_ev->id ); +		 +		event_debug( "(replacing old handler (id = %d)) = %d\n", b_ev->id, id_next ); +		 +		b_ev->id = id_next++; +		b_ev->function = function; +		b_ev->data = data; +	} +	else +	{ +		GIOCondition out_cond; +		 +		event_debug( "(new) = %d\n", id_next ); +		 +		b_ev = g_new0( struct b_event_data, 1 ); +		b_ev->id = id_next++; +		b_ev->function = function; +		b_ev->data = data; +		 +		out_cond = EV_PERSIST; +		if( condition & GAIM_INPUT_READ ) +			out_cond |= EV_READ; +		if( condition & GAIM_INPUT_WRITE ) +			out_cond |= EV_WRITE; +		 +		event_set( &b_ev->evinfo, fd, out_cond, b_event_passthrough, b_ev ); +		event_add( &b_ev->evinfo, NULL ); +		 +		if( out_cond & EV_READ ) +			g_hash_table_insert( read_hash, &b_ev->evinfo.ev_fd, b_ev ); +		if( out_cond & EV_WRITE ) +			g_hash_table_insert( write_hash, &b_ev->evinfo.ev_fd, b_ev ); +	} +	 +	g_hash_table_insert( id_hash, &b_ev->id, b_ev ); +	return b_ev->id; +} + +/* TODO: Persistence for timers! */ +gint b_timeout_add( gint timeout, b_event_handler function, gpointer data ) +{ +	struct b_event_data *b_ev = g_new0( struct b_event_data, 1 ); +	struct timeval tv; +	 +	b_ev->id = id_next++; +	b_ev->timeout = timeout; +	b_ev->function = function; +	b_ev->data = data; +	 +	tv.tv_sec = timeout / 1000; +	tv.tv_usec = ( timeout % 1000 ) * 1000; +	 +	evtimer_set( &b_ev->evinfo, b_event_passthrough, b_ev ); +	evtimer_add( &b_ev->evinfo, &tv ); +	 +	event_debug( "b_timeout_add( %d, 0x%x, 0x%x ) = %d\n", timeout, function, data, b_ev->id ); +	 +	g_hash_table_insert( id_hash, &b_ev->id, b_ev ); +	 +	return b_ev->id; +} + +void b_event_remove( gint id ) +{ +	struct b_event_data *b_ev = g_hash_table_lookup( id_hash, &id ); +	 +	event_debug( "b_event_remove( %d )\n", id ); +	if( b_ev ) +	{ +		g_hash_table_remove( id_hash, &b_ev->id ); +		if( b_ev->evinfo.ev_fd >= 0 ) +		{ +			if( b_ev->evinfo.ev_events & EV_READ ) +				g_hash_table_remove( read_hash, &b_ev->evinfo.ev_fd ); +			if( b_ev->evinfo.ev_events & EV_WRITE ) +				g_hash_table_remove( write_hash, &b_ev->evinfo.ev_fd ); +		} +		 +		event_del( &b_ev->evinfo ); +		g_free( b_ev ); +	} +	else +	{ +		event_debug( "Already removed?\n" ); +	} +} + +void closesocket( int fd ) +{ +	struct b_event_data *b_ev; +	 +	/* Since epoll() (the main reason we use libevent) automatically removes sockets from +	   the epoll() list when a socket gets closed and some modules have a habit of +	   closing sockets before removing event handlers, our and libevent's administration +	   get a little bit messed up. So this little function will remove the handlers +	   properly before closing a socket. */ +	 +	if( ( b_ev = g_hash_table_lookup( read_hash, &fd ) ) ) +	{ +		event_debug( "Warning: fd %d still had a read event handler when shutting down.\n", fd ); +		b_event_remove( b_ev->id ); +	} +	if( ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) +	{ +		event_debug( "Warning: fd %d still had a write event handler when shutting down.\n", fd ); +		b_event_remove( b_ev->id ); +	} +	 +	close( fd ); +} diff --git a/lib/http_client.c b/lib/http_client.c new file mode 100644 index 00000000..b00fcf98 --- /dev/null +++ b/lib/http_client.c @@ -0,0 +1,453 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2005 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* HTTP(S) module                                                       */ + +/* +  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 <stdio.h> + +#include "http_client.h" +#include "url.h" +#include "sock.h" + + +static gboolean http_connected( gpointer data, int source, b_input_condition cond ); +static gboolean http_ssl_connected( gpointer data, void *source, b_input_condition cond ); +static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond ); + + +void *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, 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->func = func; +	req->data = data; +	req->request = g_strdup( request ); +	req->request_length = strlen( request ); +	 +	return( req ); +} + +void *http_dorequest_url( char *url_string, http_input_function func, gpointer data ) +{ +	url_t *url = g_new0( url_t, 1 ); +	char *request; +	void *ret; +	 +	if( !url_set( url, url_string ) ) +	{ +		g_free( url ); +		return NULL; +	} +	 +	if( url->proto != PROTO_HTTP && url->proto != PROTO_HTTPS ) +	{ +		g_free( url ); +		return NULL; +	} +	 +	request = g_strdup_printf( "GET %s HTTP/1.0\r\n" +	                           "Host: %s\r\n" +	                           "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n" +	                           "\r\n", url->file, url->host ); +	 +	ret = http_dorequest( url->host, url->port, +	                      url->proto == PROTO_HTTPS, request, func, data ); +	 +	g_free( url ); +	g_free( request ); +	return ret; +} + +/* This one is actually pretty simple... Might get more calls if we can't write  +   the whole request at once. */ +static gboolean http_connected( gpointer data, int source, b_input_condition cond ) +{ +	struct http_request *req = data; +	int st; +	 +	if( source < 0 ) +		goto error; +	 +	if( req->inpa > 0 ) +		b_event_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() ) +			{ +				closesocket( req->fd ); +				goto error; +			} +		} +	} +	 +	if( st > 0 ) +		req->bytes_written += st; +	 +	if( req->bytes_written < req->request_length ) +		req->inpa = b_input_add( source, +		                         req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_WRITE, +	        	                 http_connected, req ); +	else +		req->inpa = b_input_add( source, GAIM_INPUT_READ, http_incoming_data, req ); +	 +	return FALSE; +	 +error: +	req->status_string = g_strdup( "Error while writing HTTP request" ); +	 +	req->func( req ); +	 +	g_free( req->request ); +	g_free( req ); +	 +	return FALSE; +} + +static gboolean http_ssl_connected( gpointer data, void *source, b_input_condition 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 gboolean http_incoming_data( gpointer data, int source, b_input_condition cond ) +{ +	struct http_request *req = data; +	int evil_server = 0; +	char buffer[2048]; +	char *end1, *end2; +	int st; +	 +	if( req->inpa > 0 ) +		b_event_remove( req->inpa ); +	 +	if( req->ssl ) +	{ +		st = ssl_read( req->ssl, buffer, sizeof( buffer ) ); +		if( st < 0 ) +		{ +			if( ssl_errno != SSL_AGAIN ) +			{ +				/* goto cleanup; */ +				 +				/* YAY! We have to deal with crappy Microsoft +				   servers that LOVE to send invalid TLS +				   packets that abort connections! \o/ */ +				 +				goto got_reply; +			} +		} +		else if( st == 0 ) +		{ +			goto got_reply; +		} +	} +	else +	{ +		st = read( req->fd, buffer, sizeof( buffer ) ); +		if( st < 0 ) +		{ +			if( !sockerr_again() ) +			{ +				req->status_string = g_strdup( strerror( errno ) ); +				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 ); +		req->bytes_read += st; +	} +	 +	/* There will be more! */ +	req->inpa = b_input_add( req->fd, +	                         req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_READ, +	                         http_incoming_data, req ); +	 +	return FALSE; + +got_reply: +	/* Maybe if the webserver is overloaded, or when there's bad SSL +	   support... */ +	if( req->bytes_read == 0 ) +	{ +		req->status_string = g_strdup( "Empty HTTP reply" ); +		goto cleanup; +	} +	 +	/* 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 + 1; +		evil_server = 1; +	} +	else if( end1 ) +	{ +		end1 += 2; +	} +	else +	{ +		req->status_string = g_strdup( "Malformed HTTP reply" ); +		goto cleanup; +	} +	 +	*end1 = 0; +	 +	if( evil_server ) +		req->reply_body = end1 + 1; +	else +		req->reply_body = end1 + 2; +	 +	req->body_size = req->reply_headers + req->bytes_read - req->reply_body; +	 +	if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL ) +	{ +		if( sscanf( end1 + 1, "%d", &req->status_code ) != 1 ) +		{ +			req->status_string = g_strdup( "Can't parse status code" ); +			req->status_code = -1; +		} +		else +		{ +			char *eol; +			 +			if( evil_server ) +				eol = strchr( end1, '\n' ); +			else +				eol = strchr( end1, '\r' ); +			 +			req->status_string = g_strndup( end1 + 1, eol - end1 - 1 ); +			 +			/* Just to be sure... */ +			if( ( eol = strchr( req->status_string, '\r' ) ) ) +				*eol = 0; +			if( ( eol = strchr( req->status_string, '\n' ) ) ) +				*eol = 0; +		} +	} +	else +	{ +		req->status_string = g_strdup( "Can't locate status code" ); +		req->status_code = -1; +	} +	 +	if( req->status_code == 301 || req->status_code == 302 ) +	{ +		char *loc, *new_request, *new_host; +		int error = 0, new_port, new_proto; +		 +		/* We might fill it again, so let's not leak any memory. */ +		g_free( req->status_string ); +		req->status_string = NULL; +		 +		loc = strstr( req->reply_headers, "\nLocation: " ); +		if( loc == NULL ) /* We can't handle this redirect... */ +		{ +			req->status_string = g_strdup( "Can't locate Location: header" ); +			goto cleanup; +		} +		 +		loc += 11; +		while( *loc == ' ' ) +			loc ++; +		 +		/* TODO/FIXME: Possibly have to handle relative redirections, +		   and rewrite Host: headers. Not necessary for now, it's +		   enough for passport authentication like this. */ +		 +		if( *loc == '/' ) +		{ +			/* Just a different pathname... */ +			 +			/* Since we don't cache the servername, and since we +			   don't need this yet anyway, I won't implement it. */ +			 +			req->status_string = g_strdup( "Can't handle recursive redirects" ); +			 +			goto cleanup; +		} +		else +		{ +			/* A whole URL */ +			url_t *url; +			char *s; +			 +			s = strstr( loc, "\r\n" ); +			if( s == NULL ) +				goto cleanup; +			 +			url = g_new0( url_t, 1 ); +			*s = 0; +			 +			if( !url_set( url, loc ) ) +			{ +				req->status_string = g_strdup( "Malformed redirect URL" ); +				g_free( url ); +				goto cleanup; +			} +			 +			/* Okay, this isn't fun! We have to rebuild the request... :-( */ +			new_request = g_malloc( req->request_length + strlen( url->file ) ); +			 +			/* So, now I just allocated enough memory, so I'm +			   going to use strcat(), whether you like it or not. :-) */ +			 +			sprintf( new_request, "GET %s HTTP/1.0", url->file ); +			 +			s = strstr( req->request, "\r\n" ); +			if( s == NULL ) +			{ +				req->status_string = g_strdup( "Error while rebuilding request string" ); +				g_free( new_request ); +				g_free( url ); +				goto cleanup; +			} +			 +			strcat( new_request, s ); +			new_host = g_strdup( url->host ); +			new_port = url->port; +			new_proto = url->proto; +			 +			g_free( url ); +		} +		 +		if( req->ssl ) +			ssl_disconnect( req->ssl ); +		else +			closesocket( req->fd ); +		 +		req->fd = -1; +		req->ssl = NULL; +		 +		if( new_proto == PROTO_HTTPS ) +		{ +			req->ssl = ssl_connect( new_host, new_port, http_ssl_connected, req ); +			if( req->ssl == NULL ) +				error = 1; +		} +		else +		{ +			req->fd = proxy_connect( new_host, new_port, http_connected, req ); +			if( req->fd < 0 ) +				error = 1; +		} +		g_free( new_host ); +		 +		if( error ) +		{ +			req->status_string = g_strdup( "Connection problem during redirect" ); +			g_free( new_request ); +			goto cleanup; +		} +		 +		g_free( req->request ); +		g_free( req->reply_headers ); +		req->request = new_request; +		req->request_length = strlen( new_request ); +		req->bytes_read = req->bytes_written = req->inpa = 0; +		req->reply_headers = req->reply_body = NULL; +		 +		return FALSE; +	} +	 +	/* 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 +		closesocket( req->fd ); +	 +	req->func( req ); +	 +	g_free( req->request ); +	g_free( req->reply_headers ); +	g_free( req->status_string ); +	g_free( req ); +	 +	return FALSE; +} diff --git a/lib/http_client.h b/lib/http_client.h new file mode 100644 index 00000000..50ee80cf --- /dev/null +++ b/lib/http_client.h @@ -0,0 +1,57 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2005 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* HTTP(S) module                                                       */ + +/* +  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 *status_string; +	char *reply_headers; +	char *reply_body; +	int body_size; +	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, int ssl, char *request, http_input_function func, gpointer data ); +void *http_dorequest_url( char *url_string, http_input_function func, gpointer data ); diff --git a/lib/ini.c b/lib/ini.c new file mode 100644 index 00000000..c63a132e --- /dev/null +++ b/lib/ini.c @@ -0,0 +1,90 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2005 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* INI file reading code						*/ + +/* +  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 +*/ +#define BITLBEE_CORE +#include "bitlbee.h" + +ini_t *ini_open( char *file ) +{ +	ini_t *ini = g_new0( ini_t, 1 ); +	 +	if( ( ini->fp = fopen( file, "r" ) ) == NULL ) +	{ +		g_free( ini ); +		return( NULL ); +	} +	 +	return( ini ); +} + +int ini_read( ini_t *file ) +{ +	char key[MAX_STRING], s[MAX_STRING], *t; +	int i; +	 +	while( !feof( file->fp ) ) +	{ +		*s = 0; +		fscanf( file->fp, "%127[^\n#]s", s ); +		fscanf( file->fp, "%*[^\n]s" ); +		fgetc( file->fp );		/* Skip newline		*/ +		file->line ++; +		if( strchr( s, '=' ) ) +		{ +			sscanf( s, "%[^ =]s", key ); +			if( ( t = strchr( key, '.' ) ) ) +			{ +				*t = 0; +				strcpy( file->section, key ); +				t ++; +			} +			else +			{ +				strcpy( file->section, file->c_section ); +				t = key; +			} +			sscanf( t, "%s", file->key ); +			t = strchr( s, '=' ) + 1; +			for( i = 0; t[i] == ' '; i ++ ); +			strcpy( file->value, &t[i] ); +			for( i = strlen( file->value ) - 1; file->value[i] == 32; i -- ) +				file->value[i] = 0; +			 +			return( 1 ); +		} +		else if( ( t = strchr( s, '[' ) ) ) +		{ +			strcpy( file->c_section, t + 1 ); +			t = strchr( file->c_section, ']' ); +			*t = 0; +		} +	} +	return( 0 ); +} + +void ini_close( ini_t *file ) +{ +	fclose( file->fp ); +	g_free( file ); +} diff --git a/lib/ini.h b/lib/ini.h new file mode 100644 index 00000000..5eab472b --- /dev/null +++ b/lib/ini.h @@ -0,0 +1,43 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* INI file reading code						*/ + +/* +  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 +*/ + +#ifndef _INI_H +#define _INI_H + +typedef struct +{ +	FILE *fp; +	int line; +	char c_section[MAX_STRING]; +	char section[MAX_STRING]; +	char key[MAX_STRING]; +	char value[MAX_STRING]; +} ini_t; + +ini_t *ini_open( char *file ); +int ini_read( ini_t *file ); +void ini_close( ini_t *file ); + +#endif diff --git a/lib/md5.c b/lib/md5.c new file mode 100644 index 00000000..e6273585 --- /dev/null +++ b/lib/md5.c @@ -0,0 +1,392 @@ +/* +  Copyright (C) 1999 Aladdin Enterprises.  All rights reserved. + +  This software is provided 'as-is', without any express or implied +  warranty.  In no event will the authors be held liable for any damages +  arising from the use of this software. + +  Permission is granted to anyone to use this software for any purpose, +  including commercial applications, and to alter it and redistribute it +  freely, subject to the following restrictions: + +  1. The origin of this software must not be misrepresented; you must not +     claim that you wrote the original software. If you use this software +     in a product, an acknowledgment in the product documentation would be +     appreciated but is not required. +  2. Altered source versions must be plainly marked as such, and must not be +     misrepresented as being the original software. +  3. This notice may not be removed or altered from any source distribution. + +  L. Peter Deutsch +  ghost@aladdin.com + + */ +/* +  Independent implementation of MD5 (RFC 1321). + +  This code implements the MD5 Algorithm defined in RFC 1321. +  It is derived directly from the text of the RFC and not from the +  reference implementation. + +  The original and principal author of md5.c is L. Peter Deutsch +  <ghost@aladdin.com>.  Other authors are noted in the change history +  that follows (in reverse chronological order): + +  1999-11-04 lpd Edited comments slightly for automatic TOC extraction. +  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). +  1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include <string.h> + +#ifdef TEST +/* + * Compile with -DTEST to create a self-contained executable test program. + * The test program should print out the same values as given in section + * A.5 of RFC 1321, reproduced below. + */ +#include <string.h> +main() +{ +    static const char *const test[7] = { +	"", /*d41d8cd98f00b204e9800998ecf8427e*/ +	"945399884.61923487334tuvga", /*0cc175b9c0f1b6a831c399e269772661*/ +	"abc", /*900150983cd24fb0d6963f7d28e17f72*/ +	"message digest", /*f96b697d7cb7938d525a2f31aaf161d0*/ +	"abcdefghijklmnopqrstuvwxyz", /*c3fcd3d76192e4007dfb496cca67e13b*/ +	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", +				/*d174ab98d277d9f5a5611c2c9f419d9f*/ +	"12345678901234567890123456789012345678901234567890123456789012345678901234567890" /*57edf4a22be3c955ac49da2e2107b67a*/ +    }; +    int i; + +    for (i = 0; i < 7; ++i) { +	md5_state_t state; +	md5_byte_t digest[16]; +	int di; + +	md5_init(&state); +	md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i])); +	md5_finish(&state, digest); +	printf("MD5 (\"%s\") = ", test[i]); +	for (di = 0; di < 16; ++di) +	    printf("%02x", digest[di]); +	printf("\n"); +    } +    return 0; +} +#endif /* TEST */ + + +/* + * For reference, here is the program that computed the T values. + */ +#if 0 +#include <math.h> +main() +{ +    int i; +    for (i = 1; i <= 64; ++i) { +	unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i))); +	printf("#define T%d 0x%08lx\n", i, v); +    } +    return 0; +} +#endif +/* + * End of T computation program. + */ +#define T1 0xd76aa478 +#define T2 0xe8c7b756 +#define T3 0x242070db +#define T4 0xc1bdceee +#define T5 0xf57c0faf +#define T6 0x4787c62a +#define T7 0xa8304613 +#define T8 0xfd469501 +#define T9 0x698098d8 +#define T10 0x8b44f7af +#define T11 0xffff5bb1 +#define T12 0x895cd7be +#define T13 0x6b901122 +#define T14 0xfd987193 +#define T15 0xa679438e +#define T16 0x49b40821 +#define T17 0xf61e2562 +#define T18 0xc040b340 +#define T19 0x265e5a51 +#define T20 0xe9b6c7aa +#define T21 0xd62f105d +#define T22 0x02441453 +#define T23 0xd8a1e681 +#define T24 0xe7d3fbc8 +#define T25 0x21e1cde6 +#define T26 0xc33707d6 +#define T27 0xf4d50d87 +#define T28 0x455a14ed +#define T29 0xa9e3e905 +#define T30 0xfcefa3f8 +#define T31 0x676f02d9 +#define T32 0x8d2a4c8a +#define T33 0xfffa3942 +#define T34 0x8771f681 +#define T35 0x6d9d6122 +#define T36 0xfde5380c +#define T37 0xa4beea44 +#define T38 0x4bdecfa9 +#define T39 0xf6bb4b60 +#define T40 0xbebfbc70 +#define T41 0x289b7ec6 +#define T42 0xeaa127fa +#define T43 0xd4ef3085 +#define T44 0x04881d05 +#define T45 0xd9d4d039 +#define T46 0xe6db99e5 +#define T47 0x1fa27cf8 +#define T48 0xc4ac5665 +#define T49 0xf4292244 +#define T50 0x432aff97 +#define T51 0xab9423a7 +#define T52 0xfc93a039 +#define T53 0x655b59c3 +#define T54 0x8f0ccc92 +#define T55 0xffeff47d +#define T56 0x85845dd1 +#define T57 0x6fa87e4f +#define T58 0xfe2ce6e0 +#define T59 0xa3014314 +#define T60 0x4e0811a1 +#define T61 0xf7537e82 +#define T62 0xbd3af235 +#define T63 0x2ad7d2bb +#define T64 0xeb86d391 + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ +    md5_word_t +	a = pms->abcd[0], b = pms->abcd[1], +	c = pms->abcd[2], d = pms->abcd[3]; +    md5_word_t t; + +#ifndef ARCH_IS_BIG_ENDIAN +# define ARCH_IS_BIG_ENDIAN 1	/* slower, default implementation */ +#endif +#if ARCH_IS_BIG_ENDIAN + +    /* +     * On big-endian machines, we must arrange the bytes in the right +     * order.  (This also works on machines of unknown byte order.) +     */ +    md5_word_t X[16]; +    const md5_byte_t *xp = data; +    int i; + +    for (i = 0; i < 16; ++i, xp += 4) +	X[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + +#else  /* !ARCH_IS_BIG_ENDIAN */ + +    /* +     * On little-endian machines, we can process properly aligned data +     * without copying it. +     */ +    md5_word_t xbuf[16]; +    const md5_word_t *X; + +    if (!((data - (const md5_byte_t *)0) & 3)) { +	/* data are properly aligned */ +	X = (const md5_word_t *)data; +    } else { +	/* not aligned */ +	memcpy(xbuf, data, 64); +	X = xbuf; +    } +#endif + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + +    /* Round 1. */ +    /* Let [abcd k s i] denote the operation +       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ +  t = a + F(b,c,d) + X[k] + Ti;\ +  a = ROTATE_LEFT(t, s) + b +    /* Do the following 16 operations. */ +    SET(a, b, c, d,  0,  7,  T1); +    SET(d, a, b, c,  1, 12,  T2); +    SET(c, d, a, b,  2, 17,  T3); +    SET(b, c, d, a,  3, 22,  T4); +    SET(a, b, c, d,  4,  7,  T5); +    SET(d, a, b, c,  5, 12,  T6); +    SET(c, d, a, b,  6, 17,  T7); +    SET(b, c, d, a,  7, 22,  T8); +    SET(a, b, c, d,  8,  7,  T9); +    SET(d, a, b, c,  9, 12, T10); +    SET(c, d, a, b, 10, 17, T11); +    SET(b, c, d, a, 11, 22, T12); +    SET(a, b, c, d, 12,  7, T13); +    SET(d, a, b, c, 13, 12, T14); +    SET(c, d, a, b, 14, 17, T15); +    SET(b, c, d, a, 15, 22, T16); +#undef SET + +     /* Round 2. */ +     /* Let [abcd k s i] denote the operation +          a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ +  t = a + G(b,c,d) + X[k] + Ti;\ +  a = ROTATE_LEFT(t, s) + b +     /* Do the following 16 operations. */ +    SET(a, b, c, d,  1,  5, T17); +    SET(d, a, b, c,  6,  9, T18); +    SET(c, d, a, b, 11, 14, T19); +    SET(b, c, d, a,  0, 20, T20); +    SET(a, b, c, d,  5,  5, T21); +    SET(d, a, b, c, 10,  9, T22); +    SET(c, d, a, b, 15, 14, T23); +    SET(b, c, d, a,  4, 20, T24); +    SET(a, b, c, d,  9,  5, T25); +    SET(d, a, b, c, 14,  9, T26); +    SET(c, d, a, b,  3, 14, T27); +    SET(b, c, d, a,  8, 20, T28); +    SET(a, b, c, d, 13,  5, T29); +    SET(d, a, b, c,  2,  9, T30); +    SET(c, d, a, b,  7, 14, T31); +    SET(b, c, d, a, 12, 20, T32); +#undef SET + +     /* Round 3. */ +     /* Let [abcd k s t] denote the operation +          a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ +  t = a + H(b,c,d) + X[k] + Ti;\ +  a = ROTATE_LEFT(t, s) + b +     /* Do the following 16 operations. */ +    SET(a, b, c, d,  5,  4, T33); +    SET(d, a, b, c,  8, 11, T34); +    SET(c, d, a, b, 11, 16, T35); +    SET(b, c, d, a, 14, 23, T36); +    SET(a, b, c, d,  1,  4, T37); +    SET(d, a, b, c,  4, 11, T38); +    SET(c, d, a, b,  7, 16, T39); +    SET(b, c, d, a, 10, 23, T40); +    SET(a, b, c, d, 13,  4, T41); +    SET(d, a, b, c,  0, 11, T42); +    SET(c, d, a, b,  3, 16, T43); +    SET(b, c, d, a,  6, 23, T44); +    SET(a, b, c, d,  9,  4, T45); +    SET(d, a, b, c, 12, 11, T46); +    SET(c, d, a, b, 15, 16, T47); +    SET(b, c, d, a,  2, 23, T48); +#undef SET + +     /* Round 4. */ +     /* Let [abcd k s t] denote the operation +          a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ +  t = a + I(b,c,d) + X[k] + Ti;\ +  a = ROTATE_LEFT(t, s) + b +     /* Do the following 16 operations. */ +    SET(a, b, c, d,  0,  6, T49); +    SET(d, a, b, c,  7, 10, T50); +    SET(c, d, a, b, 14, 15, T51); +    SET(b, c, d, a,  5, 21, T52); +    SET(a, b, c, d, 12,  6, T53); +    SET(d, a, b, c,  3, 10, T54); +    SET(c, d, a, b, 10, 15, T55); +    SET(b, c, d, a,  1, 21, T56); +    SET(a, b, c, d,  8,  6, T57); +    SET(d, a, b, c, 15, 10, T58); +    SET(c, d, a, b,  6, 15, T59); +    SET(b, c, d, a, 13, 21, T60); +    SET(a, b, c, d,  4,  6, T61); +    SET(d, a, b, c, 11, 10, T62); +    SET(c, d, a, b,  2, 15, T63); +    SET(b, c, d, a,  9, 21, T64); +#undef SET + +     /* Then perform the following additions. (That is increment each +        of the four registers by the value it had before this block +        was started.) */ +    pms->abcd[0] += a; +    pms->abcd[1] += b; +    pms->abcd[2] += c; +    pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ +    pms->count[0] = pms->count[1] = 0; +    pms->abcd[0] = 0x67452301; +    pms->abcd[1] = 0xefcdab89; +    pms->abcd[2] = 0x98badcfe; +    pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ +    const md5_byte_t *p = data; +    int left = nbytes; +    int offset = (pms->count[0] >> 3) & 63; +    md5_word_t nbits = (md5_word_t)(nbytes << 3); + +    if (nbytes <= 0) +	return; + +    /* Update the message length. */ +    pms->count[1] += nbytes >> 29; +    pms->count[0] += nbits; +    if (pms->count[0] < nbits) +	pms->count[1]++; + +    /* Process an initial partial block. */ +    if (offset) { +	int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + +	memcpy(pms->buf + offset, p, copy); +	if (offset + copy < 64) +	    return; +	p += copy; +	left -= copy; +	md5_process(pms, pms->buf); +    } + +    /* Process full blocks. */ +    for (; left >= 64; p += 64, left -= 64) +	md5_process(pms, p); + +    /* Process a final partial block. */ +    if (left) +	memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ +    static const md5_byte_t pad[64] = { +	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +    }; +    md5_byte_t data[8]; +    int i; + +    /* Save the length before padding. */ +    for (i = 0; i < 8; ++i) +	data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); +    /* Pad to 56 bytes mod 64. */ +    md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); +    /* Append the length. */ +    md5_append(pms, data, 8); +    for (i = 0; i < 16; ++i) +	digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/lib/md5.h b/lib/md5.h new file mode 100644 index 00000000..f24f2ff1 --- /dev/null +++ b/lib/md5.h @@ -0,0 +1,85 @@ +/* +  Copyright (C) 1999 Aladdin Enterprises.  All rights reserved. +   +  This software is provided 'as-is', without any express or implied +  warranty.  In no event will the authors be held liable for any damages +  arising from the use of this software. + +  Permission is granted to anyone to use this software for any purpose, +  including commercial applications, and to alter it and redistribute it +  freely, subject to the following restrictions: + +  1. The origin of this software must not be misrepresented; you must not +     claim that you wrote the original software. If you use this software +     in a product, an acknowledgment in the product documentation would be +     appreciated but is not required. +  2. Altered source versions must be plainly marked as such, and must not be +     misrepresented as being the original software. +  3. This notice may not be removed or altered from any source distribution. + +  L. Peter Deutsch +  ghost@aladdin.com + + */ +/* +  Independent implementation of MD5 (RFC 1321). + +  This code implements the MD5 Algorithm defined in RFC 1321. +  It is derived directly from the text of the RFC and not from the +  reference implementation. + +  The original and principal author of md5.h is L. Peter Deutsch +  <ghost@aladdin.com>.  Other authors are noted in the change history +  that follows (in reverse chronological order): + +  2004-03-09 Jelmer Vernooij add G_MODULE_EXPORT for Bitlbee +  1999-11-04 lpd Edited comments slightly for automatic TOC extraction. +  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); +	added conditionalization for C++ compilation from Martin +	Purschke <purschke@bnl.gov>. +  1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +#  define md5_INCLUDED + +#include <glib.h> +#include <gmodule.h> + +/* + * This code has some adaptations for the Ghostscript environment, but it + * will compile and run correctly in any environment with 8-bit chars and + * 32-bit ints.  Specifically, it assumes that if the following are + * defined, they have the same meaning as in Ghostscript: P1, P2, P3, + * ARCH_IS_BIG_ENDIAN. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { +    md5_word_t count[2];	/* message length in bits, lsw first */ +    md5_word_t abcd[4];		/* digest buffer */ +    md5_byte_t buf[64];		/* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C"  +{ +#endif + +/* Initialize the algorithm. */ +G_MODULE_EXPORT void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +G_MODULE_EXPORT void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +G_MODULE_EXPORT void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +}  /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/lib/misc.c b/lib/misc.c new file mode 100644 index 00000000..d8d6a4c7 --- /dev/null +++ b/lib/misc.c @@ -0,0 +1,481 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* + * Various utility functions. Some are copied from Gaim to support the + * IM-modules, most are from BitlBee. + * + * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + *                          (and possibly other members of the Gaim team) + * Copyright 2002-2005 Wilmer van der Gaast <wilmer@gaast.net> + */ + +/* +  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 +*/ + +#define BITLBEE_CORE +#include "nogaim.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <glib.h> +#include <time.h> + +void strip_linefeed(gchar *text) +{ +	int i, j; +	gchar *text2 = g_malloc(strlen(text) + 1); + +	for (i = 0, j = 0; text[i]; i++) +		if (text[i] != '\r') +			text2[j++] = text[i]; +	text2[j] = '\0'; + +	strcpy(text, text2); +	g_free(text2); +} + +char *add_cr(char *text) +{ +	char *ret = NULL; +	int count = 0, j; +	unsigned int i; + +	if (text[0] == '\n') +		count++; +	for (i = 1; i < strlen(text); i++) +		if (text[i] == '\n' && text[i - 1] != '\r') +			count++; + +	if (count == 0) +		return g_strdup(text); + +	ret = g_malloc0(strlen(text) + count + 1); + +	i = 0; j = 0; +	if (text[i] == '\n') +		ret[j++] = '\r'; +	ret[j++] = text[i++]; +	for (; i < strlen(text); i++) { +		if (text[i] == '\n' && text[i - 1] != '\r') +			ret[j++] = '\r'; +		ret[j++] = text[i]; +	} + +	return ret; +} + +char *tobase64(const char *text) +{ +	char *out; +	int len; +	 +	len = strlen(text); +	out = g_malloc((len + 2)    /* the == padding */ +	                    / 3     /* every 3-byte block */ +	                    * 4     /* becomes a 4-byte one */ +	                    + 1);   /* and of course, ASCIIZ! */ +	 +	base64_encode_real(text, len, out, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="); +	 +	return out; +} + +void base64_encode_real(const unsigned char *in, int inlen, unsigned char *out, char *b64digits) +{ +	for (; inlen >= 3; inlen -= 3) +	{ +		*out++ = b64digits[in[0] >> 2]; +		*out++ = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)]; +		*out++ = b64digits[((in[1]<<2) & 0x3c) | (in[2]>>6)]; +		*out++ = b64digits[in[2] & 0x3f]; +		in += 3; +	} +	if (inlen > 0) +	{ +		*out++ = b64digits[in[0] >> 2]; +		if (inlen > 1) +		{ +			*out++ = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)]; +			*out++ = b64digits[((in[1]<<2) & 0x3c)]; +		} +		else +		{ +			*out++ = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)]; +			*out++ = b64digits[64]; +		} +		*out++ = b64digits[64]; +	} +	*out = '\0'; +} + +char *normalize(const char *s) +{ +	static char buf[BUF_LEN]; +	char *t, *u; +	int x = 0; + +	g_return_val_if_fail((s != NULL), NULL); + +	u = t = g_strdup(s); + +	strcpy(t, s); +	g_strdown(t); + +	while (*t && (x < BUF_LEN - 1)) { +		if (*t != ' ') { +			buf[x] = *t; +			x++; +		} +		t++; +	} +	buf[x] = '\0'; +	g_free(u); +	return buf; +} + +time_t get_time(int year, int month, int day, int hour, int min, int sec) +{ +	struct tm tm; + +	tm.tm_year = year - 1900; +	tm.tm_mon = month - 1; +	tm.tm_mday = day; +	tm.tm_hour = hour; +	tm.tm_min = min; +	tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60; +	return mktime(&tm); +} + +typedef struct htmlentity +{ +	char code[8]; +	char is[4]; +} htmlentity_t; + +/* FIXME: This is ISO8859-1(5) centric, so might cause problems with other charsets. */ + +static const htmlentity_t ent[] = +{ +	{ "lt",     "<" }, +	{ "gt",     ">" }, +	{ "amp",    "&" }, +	{ "quot",   "\"" }, +	{ "aacute", "á" }, +	{ "eacute", "é" }, +	{ "iacute", "é" }, +	{ "oacute", "ó" }, +	{ "uacute", "ú" }, +	{ "agrave", "à" }, +	{ "egrave", "è" }, +	{ "igrave", "ì" }, +	{ "ograve", "ò" }, +	{ "ugrave", "ù" }, +	{ "acirc",  "â" }, +	{ "ecirc",  "ê" }, +	{ "icirc",  "î" }, +	{ "ocirc",  "ô" }, +	{ "ucirc",  "û" }, +	{ "auml",   "ä" }, +	{ "euml",   "ë" }, +	{ "iuml",   "ï" }, +	{ "ouml",   "ö" }, +	{ "uuml",   "ü" }, +	{ "nbsp",   " " }, +	{ "",        ""  } +}; + +void strip_html( char *in ) +{ +	char *start = in; +	char *out = g_malloc( strlen( in ) + 1 ); +	char *s = out, *cs; +	int i, matched; +	 +	memset( out, 0, strlen( in ) + 1 ); +	 +	while( *in ) +	{ +		if( *in == '<' && ( isalpha( *(in+1) ) || *(in+1) == '/' ) ) +		{ +			/* If in points at a < and in+1 points at a letter or a slash, this is probably +			   a HTML-tag. Try to find a closing > and continue there. If the > can't be +			   found, assume that it wasn't a HTML-tag after all. */ +			 +			cs = in; +			 +			while( *in && *in != '>' ) +				in ++; +			 +			if( *in ) +			{ +				if( g_strncasecmp( cs+1, "br", 2) == 0 ) +					*(s++) = '\n'; +				in ++; +			} +			else +			{ +				in = cs; +				*(s++) = *(in++); +			} +		} +		else if( *in == '&' ) +		{ +			cs = ++in; +			while( *in && isalpha( *in ) ) +				in ++; +			 +			if( *in == ';' ) in ++; +			matched = 0; +			 +			for( i = 0; *ent[i].code; i ++ ) +				if( g_strncasecmp( ent[i].code, cs, strlen( ent[i].code ) ) == 0 ) +				{ +					int j; +					 +					for( j = 0; ent[i].is[j]; j ++ ) +						*(s++) = ent[i].is[j]; +					 +					matched = 1; +					break; +				} + +			/* None of the entities were matched, so return the string */ +			if( !matched ) +			{ +				in = cs - 1; +				*(s++) = *(in++); +			} +		} +		else +		{ +			*(s++) = *(in++); +		} +	} +	 +	strcpy( start, out ); +	g_free( out ); +} + +char *escape_html( const char *html ) +{ +	const char *c = html; +	GString *ret; +	char *str; +	 +	if( html == NULL ) +		return( NULL ); +	 +	ret = g_string_new( "" ); +	 +	while( *c ) +	{ +		switch( *c ) +		{ +			case '&': +				ret = g_string_append( ret, "&" ); +				break; +			case '<': +				ret = g_string_append( ret, "<" ); +				break; +			case '>': +				ret = g_string_append( ret, ">" ); +				break; +			case '"': +				ret = g_string_append( ret, """ ); +				break; +			default: +				ret = g_string_append_c( ret, *c ); +		} +		c ++; +	} +	 +	str = ret->str; +	g_string_free( ret, FALSE ); +	return( str ); +} + +void info_string_append(GString *str, char *newline, char *name, char *value) +{ +	if( value && value[0] ) +		g_string_sprintfa( str, "%s%s: %s", newline, name, value ); +} + +/* Decode%20a%20file%20name						*/ +void http_decode( char *s ) +{ +	char *t; +	int i, j, k; +	 +	t = g_new( char, strlen( s ) + 1 ); +	 +	for( i = j = 0; s[i]; i ++, j ++ ) +	{ +		if( s[i] == '%' ) +		{ +			if( sscanf( s + i + 1, "%2x", &k ) ) +			{ +				t[j] = k; +				i += 2; +			} +			else +			{ +				*t = 0; +				break; +			} +		} +		else +		{ +			t[j] = s[i]; +		} +	} +	t[j] = 0; +	 +	strcpy( s, t ); +	g_free( t ); +} + +/* Warning: This one explodes the string. Worst-cases can make the string 3x its original size! */ +/* This fuction is safe, but make sure you call it safely as well! */ +void http_encode( char *s ) +{ +	char *t; +	int i, j; +	 +	t = g_strdup( s ); +	 +	for( i = j = 0; t[i]; i ++, j ++ ) +	{ +		/* if( t[i] <= ' ' || ((unsigned char *)t)[i] >= 128 || t[i] == '%' ) */ +		if( !isalnum( t[i] ) ) +		{ +			sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] ); +			j += 2; +		} +		else +		{ +			s[j] = t[i]; +		} +	} +	s[j] = 0; +	 +	g_free( t ); +} + +/* Strip newlines from a string. Modifies the string passed to it. */  +char *strip_newlines( char *source ) +{ +	int i;	 + +	for( i = 0; source[i] != '\0'; i ++ ) +		if( source[i] == '\n' || source[i] == '\r' ) +			source[i] = ' '; +	 +	return source; +} + +#ifdef IPV6 +/* Wrap an IPv4 address into IPv6 space. Not thread-safe... */ +char *ipv6_wrap( char *src ) +{ +	static char dst[64]; +	int i; +	 +	for( i = 0; src[i]; i ++ ) +		if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' ) +			break; +	 +	/* Hmm, it's not even an IP... */ +	if( src[i] ) +		return src; +	 +	g_snprintf( dst, sizeof( dst ), "::ffff:%s", src ); +	 +	return dst; +} + +/* Unwrap an IPv4 address into IPv6 space. Thread-safe, because it's very simple. :-) */ +char *ipv6_unwrap( char *src ) +{ +	int i; +	 +	if( g_strncasecmp( src, "::ffff:", 7 ) != 0 ) +		return src; +	 +	for( i = 7; src[i]; i ++ ) +		if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' ) +			break; +	 +	/* Hmm, it's not even an IP... */ +	if( src[i] ) +		return src; +	 +	return ( src + 7 ); +} +#endif + +/* Convert from one charset to another. +    +   from_cs, to_cs: Source and destination charsets +   src, dst: Source and destination strings +   size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though. +   maxbuf: Maximum number of bytes to write to dst +    +   Returns the number of bytes written to maxbuf or -1 on an error. +*/ +signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf ) +{ +	GIConv cd; +	size_t res; +	size_t inbytesleft, outbytesleft; +	char *inbuf = src; +	char *outbuf = dst; +	 +	cd = g_iconv_open( to_cs, from_cs ); +	if( cd == (GIConv) -1 ) +		return( -1 ); +	 +	inbytesleft = size ? size : strlen( src ); +	outbytesleft = maxbuf - 1; +	res = g_iconv( cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft ); +	*outbuf = '\0'; +	g_iconv_close( cd ); +	 +	if( res == (size_t) -1 ) +		return( -1 ); +	else +		return( outbuf - dst ); +} + +char *set_eval_charset( irc_t *irc, set_t *set, char *value ) +{ +	GIConv cd; + +	if ( g_strncasecmp( value, "none", 4 ) == 0 ) +		return( value ); + +	cd = g_iconv_open( "UTF-8", value ); +	if( cd == (GIConv) -1 ) +		return( NULL ); + +	g_iconv_close( cd ); +	return( value ); +} diff --git a/lib/misc.h b/lib/misc.h new file mode 100644 index 00000000..c7eec19b --- /dev/null +++ b/lib/misc.h @@ -0,0 +1,51 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* Misc. functions                                                      */ + +/* +  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 +*/ + +#ifndef _UTIL_H +#define _UTIL_H + +G_MODULE_EXPORT void strip_linefeed( gchar *text ); +G_MODULE_EXPORT char *add_cr( char *text ); +G_MODULE_EXPORT char *strip_newlines(char *source); +G_MODULE_EXPORT char *tobase64( const char *text ); +G_MODULE_EXPORT void base64_encode_real( const unsigned char *in, int inlen, unsigned char *out, char *b64digits ); +G_MODULE_EXPORT char *normalize( const char *s ); +G_MODULE_EXPORT void info_string_append( GString *str, char *newline, char *name, char *value ); + +G_MODULE_EXPORT time_t get_time( int year, int month, int day, int hour, int min, int sec ); +double gettime( void ); + +G_MODULE_EXPORT void strip_html( char *msg ); +G_MODULE_EXPORT char *escape_html( const char *html ); +G_MODULE_EXPORT void http_decode( char *s ); +G_MODULE_EXPORT void http_encode( char *s ); + +G_MODULE_EXPORT char *ipv6_wrap( char *src ); +G_MODULE_EXPORT char *ipv6_unwrap( char *src ); + +G_MODULE_EXPORT signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf ); +char *set_eval_charset( irc_t *irc, set_t *set, char *value ); + +#endif diff --git a/lib/proxy.c b/lib/proxy.c new file mode 100644 index 00000000..b8aa304d --- /dev/null +++ b/lib/proxy.c @@ -0,0 +1,556 @@ +/* + * gaim + * + * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * Copyright (C) 2002-2004, Wilmer van der Gaast, Jelmer Vernooij + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#define BITLBEE_CORE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#else +#include "sock.h" +#define ETIMEDOUT WSAETIMEDOUT +#define EINPROGRESS WSAEINPROGRESS +#endif +#include <fcntl.h> +#include <errno.h> +#include "nogaim.h" +#include "proxy.h" + +char proxyhost[128] = ""; +int proxyport = 0; +int proxytype = PROXY_NONE; +char proxyuser[128] = ""; +char proxypass[128] = ""; + +struct PHB { +	b_event_handler func, proxy_func; +	gpointer data, proxy_data; +	char *host; +	int port; +	int fd; +	gint inpa; +}; + + + +static struct sockaddr_in *gaim_gethostbyname(const char *host, int port) +{ +	static struct sockaddr_in sin; + +	if (!inet_aton(host, &sin.sin_addr)) { +		struct hostent *hp; +		if (!(hp = gethostbyname(host))) { +			return NULL; +		} +		memset(&sin, 0, sizeof(struct sockaddr_in)); +		memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); +		sin.sin_family = hp->h_addrtype; +	} else +		sin.sin_family = AF_INET; +	sin.sin_port = htons(port); + +	return &sin; +} + +static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition cond) +{ +	struct PHB *phb = data; +	unsigned int len; +	int error = ETIMEDOUT; +	len = sizeof(error); +	 +#ifndef _WIN32 +	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { +		closesocket(source); +		b_event_remove(phb->inpa); +		if( phb->proxy_func ) +			phb->proxy_func(phb->proxy_data, -1, GAIM_INPUT_READ); +		else { +			phb->func(phb->data, -1, GAIM_INPUT_READ); +			g_free(phb); +		} +		return FALSE; +	} +#endif +	sock_make_blocking(source); +	b_event_remove(phb->inpa); +	if( phb->proxy_func ) +		phb->proxy_func(phb->proxy_data, source, GAIM_INPUT_READ); +	else { +		phb->func(phb->data, source, GAIM_INPUT_READ); +		g_free(phb); +	} +	 +	return FALSE; +} + +static int proxy_connect_none(const char *host, unsigned short port, struct PHB *phb) +{ +	struct sockaddr_in *sin; +	int fd = -1; + +	if (!(sin = gaim_gethostbyname(host, port))) { +		g_free(phb); +		return -1; +	} + +	if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) { +		g_free(phb); +		return -1; +	} + +	sock_make_nonblocking(fd); +	 +	event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd); +	 +	if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) { +		if (sockerr_again()) { +			phb->inpa = b_input_add(fd, GAIM_INPUT_WRITE, gaim_io_connected, phb); +			phb->fd = fd; +		} else { +			closesocket(fd); +			g_free(phb); +			return -1; +		} +	} + +	return fd; +} + + +/* Connecting to HTTP proxies */ + +#define HTTP_GOODSTRING "HTTP/1.0 200 Connection established" +#define HTTP_GOODSTRING2 "HTTP/1.1 200 Connection established" + +static gboolean http_canread(gpointer data, gint source, b_input_condition cond) +{ +	int nlc = 0; +	int pos = 0; +	struct PHB *phb = data; +	char inputline[8192]; + +	b_event_remove(phb->inpa); + +	while ((pos < sizeof(inputline)-1) && (nlc != 2) && (read(source, &inputline[pos++], 1) == 1)) { +		if (inputline[pos - 1] == '\n') +			nlc++; +		else if (inputline[pos - 1] != '\r') +			nlc = 0; +	} +	inputline[pos] = '\0'; + +	if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) || +	    (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) { +		phb->func(phb->data, source, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	close(source); +	phb->func(phb->data, -1, GAIM_INPUT_READ); +	g_free(phb->host); +	g_free(phb); +	 +	return FALSE; +} + +static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond) +{ +	char cmd[384]; +	struct PHB *phb = data; +	unsigned int len; +	int error = ETIMEDOUT; +	if (phb->inpa > 0) +		b_event_remove(phb->inpa); +	len = sizeof(error); +	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} +	sock_make_blocking(source); + +	g_snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port, +		   phb->host, phb->port); +	if (send(source, cmd, strlen(cmd), 0) < 0) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	if (proxyuser && *proxyuser) { +		char *t1, *t2; +		t1 = g_strdup_printf("%s:%s", proxyuser, proxypass); +		t2 = tobase64(t1); +		g_free(t1); +		g_snprintf(cmd, sizeof(cmd), "Proxy-Authorization: Basic %s\r\n", t2); +		g_free(t2); +		if (send(source, cmd, strlen(cmd), 0) < 0) { +			close(source); +			phb->func(phb->data, -1, GAIM_INPUT_READ); +			g_free(phb->host); +			g_free(phb); +			return FALSE; +		} +	} + +	g_snprintf(cmd, sizeof(cmd), "\r\n"); +	if (send(source, cmd, strlen(cmd), 0) < 0) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	phb->inpa = b_input_add(source, GAIM_INPUT_READ, http_canread, phb); +	 +	return FALSE; +} + +static int proxy_connect_http(const char *host, unsigned short port, struct PHB *phb) +{ +	phb->host = g_strdup(host); +	phb->port = port; +	phb->proxy_func = http_canwrite; +	phb->proxy_data = phb; +	 +	return( proxy_connect_none( proxyhost, proxyport, phb ) ); +} + + +/* Connecting to SOCKS4 proxies */ + +static gboolean s4_canread(gpointer data, gint source, b_input_condition cond) +{ +	unsigned char packet[12]; +	struct PHB *phb = data; + +	b_event_remove(phb->inpa); + +	memset(packet, 0, sizeof(packet)); +	if (read(source, packet, 9) >= 4 && packet[1] == 90) { +		phb->func(phb->data, source, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	close(source); +	phb->func(phb->data, -1, GAIM_INPUT_READ); +	g_free(phb->host); +	g_free(phb); +	 +	return FALSE; +} + +static gboolean s4_canwrite(gpointer data, gint source, b_input_condition cond) +{ +	unsigned char packet[12]; +	struct hostent *hp; +	struct PHB *phb = data; +	unsigned int len; +	int error = ETIMEDOUT; +	if (phb->inpa > 0) +		b_event_remove(phb->inpa); +	len = sizeof(error); +	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} +	sock_make_blocking(source); + +	/* XXX does socks4 not support host name lookups by the proxy? */ +	if (!(hp = gethostbyname(phb->host))) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	packet[0] = 4; +	packet[1] = 1; +	packet[2] = phb->port >> 8; +	packet[3] = phb->port & 0xff; +	packet[4] = (unsigned char)(hp->h_addr_list[0])[0]; +	packet[5] = (unsigned char)(hp->h_addr_list[0])[1]; +	packet[6] = (unsigned char)(hp->h_addr_list[0])[2]; +	packet[7] = (unsigned char)(hp->h_addr_list[0])[3]; +	packet[8] = 0; +	if (write(source, packet, 9) != 9) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	phb->inpa = b_input_add(source, GAIM_INPUT_READ, s4_canread, phb); +	 +	return FALSE; +} + +static int proxy_connect_socks4(const char *host, unsigned short port, struct PHB *phb) +{ +	phb->host = g_strdup(host); +	phb->port = port; +	phb->proxy_func = s4_canwrite; +	phb->proxy_data = phb; +	 +	return( proxy_connect_none( proxyhost, proxyport, phb ) ); +} + + +/* Connecting to SOCKS5 proxies */ + +static gboolean s5_canread_again(gpointer data, gint source, b_input_condition cond) +{ +	unsigned char buf[512]; +	struct PHB *phb = data; + +	b_event_remove(phb->inpa); + +	if (read(source, buf, 10) < 10) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} +	if ((buf[0] != 0x05) || (buf[1] != 0x00)) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	phb->func(phb->data, source, GAIM_INPUT_READ); +	g_free(phb->host); +	g_free(phb); +	 +	return FALSE; +} + +static void s5_sendconnect(gpointer data, gint source) +{ +	unsigned char buf[512]; +	struct PHB *phb = data; +	int hlen = strlen(phb->host); +	 +	buf[0] = 0x05; +	buf[1] = 0x01;		/* CONNECT */ +	buf[2] = 0x00;		/* reserved */ +	buf[3] = 0x03;		/* address type -- host name */ +	buf[4] = hlen; +	memcpy(buf + 5, phb->host, hlen); +	buf[5 + strlen(phb->host)] = phb->port >> 8; +	buf[5 + strlen(phb->host) + 1] = phb->port & 0xff; + +	if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return; +	} + +	phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_canread_again, phb); +} + +static gboolean s5_readauth(gpointer data, gint source, b_input_condition cond) +{ +	unsigned char buf[512]; +	struct PHB *phb = data; + +	b_event_remove(phb->inpa); + +	if (read(source, buf, 2) < 2) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	if ((buf[0] != 0x01) || (buf[1] != 0x00)) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	s5_sendconnect(phb, source); +	 +	return FALSE; +} + +static gboolean s5_canread(gpointer data, gint source, b_input_condition cond) +{ +	unsigned char buf[512]; +	struct PHB *phb = data; + +	b_event_remove(phb->inpa); + +	if (read(source, buf, 2) < 2) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	if ((buf[0] != 0x05) || (buf[1] == 0xff)) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	if (buf[1] == 0x02) { +		unsigned int i = strlen(proxyuser), j = strlen(proxypass); +		buf[0] = 0x01;	/* version 1 */ +		buf[1] = i; +		memcpy(buf + 2, proxyuser, i); +		buf[2 + i] = j; +		memcpy(buf + 2 + i + 1, proxypass, j); +		if (write(source, buf, 3 + i + j) < 3 + i + j) { +			close(source); +			phb->func(phb->data, -1, GAIM_INPUT_READ); +			g_free(phb->host); +			g_free(phb); +			return FALSE; +		} + +		phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_readauth, phb); +	} else { +		s5_sendconnect(phb, source); +	} +	 +	return FALSE; +} + +static gboolean s5_canwrite(gpointer data, gint source, b_input_condition cond) +{ +	unsigned char buf[512]; +	int i; +	struct PHB *phb = data; +	unsigned int len; +	int error = ETIMEDOUT; +	if (phb->inpa > 0) +		b_event_remove(phb->inpa); +	len = sizeof(error); +	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} +	sock_make_blocking(source); + +	i = 0; +	buf[0] = 0x05;		/* SOCKS version 5 */ +	if (proxyuser[0]) { +		buf[1] = 0x02;	/* two methods */ +		buf[2] = 0x00;	/* no authentication */ +		buf[3] = 0x02;	/* username/password authentication */ +		i = 4; +	} else { +		buf[1] = 0x01; +		buf[2] = 0x00; +		i = 3; +	} + +	if (write(source, buf, i) < i) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_canread, phb); +	 +	return FALSE; +} + +static int proxy_connect_socks5(const char *host, unsigned short port, struct PHB *phb) +{ +	phb->host = g_strdup(host); +	phb->port = port; +	phb->proxy_func = s5_canwrite; +	phb->proxy_data = phb; +	 +	return( proxy_connect_none( proxyhost, proxyport, phb ) ); +} + + +/* Export functions */ + +int proxy_connect(const char *host, int port, b_event_handler func, gpointer data) +{ +	struct PHB *phb; +	 +	if (!host || !port || (port == -1) || !func || strlen(host) > 128) { +		return -1; +	} +	 +	phb = g_new0(struct PHB, 1); +	phb->func = func; +	phb->data = data; +	 +#ifndef _WIN32 +	sethostent(1); +#endif +	 +	if ((proxytype == PROXY_NONE) || !proxyhost || !proxyhost[0] || !proxyport || (proxyport == -1)) +		return proxy_connect_none(host, port, phb); +	else if (proxytype == PROXY_HTTP) +		return proxy_connect_http(host, port, phb); +	else if (proxytype == PROXY_SOCKS4) +		return proxy_connect_socks4(host, port, phb); +	else if (proxytype == PROXY_SOCKS5) +		return proxy_connect_socks5(host, port, phb); +	 +	if (phb->host) g_free(phb); +	g_free(phb); +	return -1; +} diff --git a/lib/proxy.h b/lib/proxy.h new file mode 100644 index 00000000..680790a5 --- /dev/null +++ b/lib/proxy.h @@ -0,0 +1,53 @@ +/* + * nogaim + * + * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +/* this is the export part of the proxy.c file. it does a little +   prototype-ing stuff and redefine some net function to mask them +   with some kind of transparent layer */  + +#ifndef _PROXY_H_ +#define _PROXY_H_ + +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#endif +#include <glib.h> +#include <gmodule.h> + +#include "events.h" + +#define PROXY_NONE 0 +#define PROXY_HTTP 1 +#define PROXY_SOCKS4 2 +#define PROXY_SOCKS5 3 + +extern char proxyhost[128]; +extern int  proxyport; +extern int  proxytype; +extern char proxyuser[128]; +extern char proxypass[128]; + +G_MODULE_EXPORT int proxy_connect(const char *host, int port, b_event_handler func, gpointer data); + +#endif /* _PROXY_H_ */ diff --git a/lib/rc4.c b/lib/rc4.c new file mode 100644 index 00000000..3559f71e --- /dev/null +++ b/lib/rc4.c @@ -0,0 +1,179 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple (but secure) RC4 implementation for safer password storage.       * +*                                                                           * +*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*                                                                           * +*  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 along  * +*  with this program; if not, write to the Free Software Foundation, Inc.,  * +*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              * +*                                                                           * +\***************************************************************************/ + +/*  +   This file implements RC4-encryption, which will mainly be used to save IM +   passwords safely in the new XML-format. Possibly other uses will come up +   later. It's supposed to be quite reliable (thanks to the use of a 6-byte +   IV/seed), certainly compared to the old format. The only realistic way to +   crack BitlBee passwords now is to use a sniffer to get your hands on the +   user's password. +    +   If you see that something's wrong in this implementation (I asked a +   couple of people to look at it already, but who knows), please tell me. +    +   The reason I chose for RC4 is because it's pretty simple but effective, +   so it will work without adding several KBs or an extra library dependency. +*/ + + +#include <glib.h> +#include <stdlib.h> +#include <string.h> +#include "rc4.h" + +/* Add some seed to the password, to make sure we *never* use the same key. +   This defines how many byes we use as a seed. */ +#define RC4_IV_LEN 6 + +/* To defend against a "Fluhrer, Mantin and Shamir attack", it is recommended +   to shuffle S[] just a bit more before you start to use it. This defines how +   many bytes we'll request before we'll really use them for encryption. */ +#define RC4_CYCLES 1024 + +struct rc4_state *rc4_keymaker( unsigned char *key, int kl, int cycles ) +{ +	struct rc4_state *st; +	int i, j, tmp; +	 +	st = g_malloc( sizeof( struct rc4_state ) ); +	st->i = st->j = 0; +	for( i = 0; i < 256; i ++ ) +		st->S[i] = i; +	 +	if( kl <= 0 ) +		kl = strlen( (char*) key ); +	 +	for( i = j = 0; i < 256; i ++ ) +	{ +		j = ( j + st->S[i] + key[i%kl] ) & 0xff; +		tmp = st->S[i]; +		st->S[i] = st->S[j]; +		st->S[j] = tmp; +	} +	 +	for( i = 0; i < cycles; i ++ ) +		rc4_getbyte( st ); +	 +	return st; +} + +/* +   For those who don't know, RC4 is basically an algorithm that generates a +   stream of bytes after you give it a key. Just get a byte from it and xor +   it with your cleartext. To decrypt, just give it the same key again and +   start xorring. +    +   The function above initializes the RC4 byte generator, the next function +   can be used to get bytes from the generator (and shuffle things a bit). +*/ + +unsigned char rc4_getbyte( struct rc4_state *st ) +{ +	unsigned char tmp; +	 +	/* Unfortunately the st-> stuff doesn't really improve readability here... */ +	st->i ++; +	st->j += st->S[st->i]; +	tmp = st->S[st->i]; +	st->S[st->i] = st->S[st->j]; +	st->S[st->j] = tmp; +	 +	return st->S[(st->S[st->i] + st->S[st->j]) & 0xff]; +} + +/* +   The following two functions can be used for reliable encryption and +   decryption. Known plaintext attacks are prevented by adding some (6, +   by default) random bytes to the password before setting up the RC4 +   structures. These 6 bytes are also saved in the results, because of +   course we'll need them in rc4_decode(). +    +   Because the length of the resulting string is unknown to the caller, +   it should pass a char**. Since the encode/decode functions allocate +   memory for the string, make sure the char** points at a NULL-pointer +   (or at least to something you already free()d), or you'll leak +   memory. And of course, don't forget to free() the result when you +   don't need it anymore. +    +   Both functions return the number of bytes in the result string. +*/ + +int rc4_encode( unsigned char *clear, int clear_len, unsigned char **crypt, char *password ) +{ +	struct rc4_state *st; +	unsigned char *key; +	int key_len, i; +	 +	key_len = strlen( password ) + RC4_IV_LEN; +	if( clear_len <= 0 ) +		clear_len = strlen( (char*) clear ); +	 +	/* Prepare buffers and the key + IV */ +	*crypt = g_malloc( clear_len + RC4_IV_LEN ); +	key = g_malloc( key_len ); +	strcpy( (char*) key, password ); +	for( i = 0; i < RC4_IV_LEN; i ++ ) +		key[key_len-RC4_IV_LEN+i] = crypt[0][i] = rand() & 0xff; +	 +	/* Generate the initial S[] from the IVed key. */ +	st = rc4_keymaker( key, key_len, RC4_CYCLES ); +	g_free( key ); +	 +	for( i = 0; i < clear_len; i ++ ) +		crypt[0][i+RC4_IV_LEN] = clear[i] ^ rc4_getbyte( st ); +	 +	g_free( st ); +	 +	return clear_len + RC4_IV_LEN; +} + +int rc4_decode( unsigned char *crypt, int crypt_len, unsigned char **clear, char *password ) +{ +	struct rc4_state *st; +	unsigned char *key; +	int key_len, clear_len, i; +	 +	key_len = strlen( password ) + RC4_IV_LEN; +	clear_len = crypt_len - RC4_IV_LEN; +	 +	/* Prepare buffers and the key + IV */ +	*clear = g_malloc( clear_len + 1 ); +	key = g_malloc( key_len ); +	strcpy( (char*) key, password ); +	for( i = 0; i < RC4_IV_LEN; i ++ ) +		key[key_len-RC4_IV_LEN+i] = crypt[i]; +	 +	/* Generate the initial S[] from the IVed key. */ +	st = rc4_keymaker( key, key_len, RC4_CYCLES ); +	g_free( key ); +	 +	for( i = 0; i < clear_len; i ++ ) +		clear[0][i] = crypt[i+RC4_IV_LEN] ^ rc4_getbyte( st ); +	clear[0][i] = 0; /* Nice to have for plaintexts. */ +	 +	g_free( st ); +	 +	return clear_len; +} diff --git a/lib/rc4.h b/lib/rc4.h new file mode 100644 index 00000000..8d50b508 --- /dev/null +++ b/lib/rc4.h @@ -0,0 +1,35 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple (but secure) RC4 implementation for safer password storage.       * +*                                                                           * +*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*                                                                           * +*  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 along  * +*  with this program; if not, write to the Free Software Foundation, Inc.,  * +*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              * +*                                                                           * +\***************************************************************************/ + + +struct rc4_state +{ +	unsigned char S[256]; +	unsigned char i, j; +}; + +struct rc4_state *rc4_keymaker( unsigned char *key, int kl, int cycles ); +unsigned char rc4_getbyte( struct rc4_state *st ); +int rc4_encode( unsigned char *clear, int clear_len, unsigned char **crypt, char *password ); +int rc4_decode( unsigned char *crypt, int crypt_len, unsigned char **clear, char *password ); diff --git a/lib/sha.c b/lib/sha.c new file mode 100644 index 00000000..895505a1 --- /dev/null +++ b/lib/sha.c @@ -0,0 +1,173 @@ +/*  + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + *  + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + *  + * The Original Code is SHA 180-1 Reference Implementation (Compact version) + *  + * The Initial Developer of the Original Code is Paul Kocher of + * Cryptography Research.  Portions created by Paul Kocher are  + * Copyright (C) 1995-9 by Cryptography Research, Inc.  All + * Rights Reserved. + *  + * Contributor(s): + * + */ + +#define BITLBEE_CORE +#include "nogaim.h" + +static void shaHashBlock(SHA_CTX *ctx); + +void shaInit(SHA_CTX *ctx) { +  int i; + +  ctx->lenW = 0; +  ctx->sizeHi = ctx->sizeLo = 0; + +  /* Initialize H with the magic constants (see FIPS180 for constants) +   */ +  ctx->H[0] = 0x67452301L; +  ctx->H[1] = 0xefcdab89L; +  ctx->H[2] = 0x98badcfeL; +  ctx->H[3] = 0x10325476L; +  ctx->H[4] = 0xc3d2e1f0L; + +  for (i = 0; i < 80; i++) +    ctx->W[i] = 0; +} + + +void shaUpdate(SHA_CTX *ctx, unsigned char *dataIn, int len) { +  int i; + +  /* Read the data into W and process blocks as they get full +   */ +  for (i = 0; i < len; i++) { +    ctx->W[ctx->lenW / 4] <<= 8; +    ctx->W[ctx->lenW / 4] |= (guint32)dataIn[i]; +    if ((++ctx->lenW) % 64 == 0) { +      shaHashBlock(ctx); +      ctx->lenW = 0; +    } +    ctx->sizeLo += 8; +    ctx->sizeHi += (ctx->sizeLo < 8); +  } +} + + +void shaFinal(SHA_CTX *ctx, unsigned char hashout[20]) { +  unsigned char pad0x80 = 0x80; +  unsigned char pad0x00 = 0x00; +  unsigned char padlen[8]; +  int i; + +  /* Pad with a binary 1 (e.g. 0x80), then zeroes, then length +   */ +  padlen[0] = (unsigned char)((ctx->sizeHi >> 24) & 255); +  padlen[1] = (unsigned char)((ctx->sizeHi >> 16) & 255); +  padlen[2] = (unsigned char)((ctx->sizeHi >> 8) & 255); +  padlen[3] = (unsigned char)((ctx->sizeHi >> 0) & 255); +  padlen[4] = (unsigned char)((ctx->sizeLo >> 24) & 255); +  padlen[5] = (unsigned char)((ctx->sizeLo >> 16) & 255); +  padlen[6] = (unsigned char)((ctx->sizeLo >> 8) & 255); +  padlen[7] = (unsigned char)((ctx->sizeLo >> 0) & 255); +  shaUpdate(ctx, &pad0x80, 1); +  while (ctx->lenW != 56) +    shaUpdate(ctx, &pad0x00, 1); +  shaUpdate(ctx, padlen, 8); + +  /* Output hash +   */ +  for (i = 0; i < 20; i++) { +    hashout[i] = (unsigned char)(ctx->H[i / 4] >> 24); +    ctx->H[i / 4] <<= 8; +  } + +  /* +   *  Re-initialize the context (also zeroizes contents) +   */ +  shaInit(ctx);  +} + + +void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]) { +  SHA_CTX ctx; + +  shaInit(&ctx); +  shaUpdate(&ctx, dataIn, len); +  shaFinal(&ctx, hashout); +} + + +#define SHA_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xffffffffL) + +static void shaHashBlock(SHA_CTX *ctx) { +  int t; +  guint32 A,B,C,D,E,TEMP; + +  for (t = 16; t <= 79; t++) +    ctx->W[t] = +      SHA_ROTL(ctx->W[t-3] ^ ctx->W[t-8] ^ ctx->W[t-14] ^ ctx->W[t-16], 1); + +  A = ctx->H[0]; +  B = ctx->H[1]; +  C = ctx->H[2]; +  D = ctx->H[3]; +  E = ctx->H[4]; + +  for (t = 0; t <= 19; t++) { +    TEMP = (SHA_ROTL(A,5) + (((C^D)&B)^D)     + E + ctx->W[t] + 0x5a827999L) & 0xffffffffL; +    E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; +  } +  for (t = 20; t <= 39; t++) { +    TEMP = (SHA_ROTL(A,5) + (B^C^D)           + E + ctx->W[t] + 0x6ed9eba1L) & 0xffffffffL; +    E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; +  } +  for (t = 40; t <= 59; t++) { +    TEMP = (SHA_ROTL(A,5) + ((B&C)|(D&(B|C))) + E + ctx->W[t] + 0x8f1bbcdcL) & 0xffffffffL; +    E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; +  } +  for (t = 60; t <= 79; t++) { +    TEMP = (SHA_ROTL(A,5) + (B^C^D)           + E + ctx->W[t] + 0xca62c1d6L) & 0xffffffffL; +    E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; +  } + +  ctx->H[0] += A; +  ctx->H[1] += B; +  ctx->H[2] += C; +  ctx->H[3] += D; +  ctx->H[4] += E; +} + +/*---------------------------------------------------------------------------- + * + * This code added by Thomas "temas" Muldowney for Jabber compatability + * + *---------------------------------------------------------------------------*/ +char *shahash(char *str) +{ +    static char final[41]; +    char *pos; +    unsigned char hashval[20]; +    int x; + +    if(!str || strlen(str) == 0) +        return NULL; + +    shaBlock((unsigned char *)str, strlen(str), hashval); + +    pos = final; +    for(x=0;x<20;x++) +    { +        g_snprintf(pos, 3, "%02x", hashval[x]); +        pos += 2; +    } +    return (char *)final; +} diff --git a/lib/sha.h b/lib/sha.h new file mode 100644 index 00000000..e8152b1b --- /dev/null +++ b/lib/sha.h @@ -0,0 +1,21 @@ +#ifndef __SHA_H__ +#define __SHA_H__ + +#include <gmodule.h> + +G_MODULE_EXPORT int strprintsha(char *dest, int *hashval); +  +typedef struct { +  guint32 H[5]; +  guint32 W[80]; +  int lenW; +  guint32 sizeHi,sizeLo; +} SHA_CTX; +  +G_MODULE_EXPORT void shaInit(SHA_CTX *ctx); +G_MODULE_EXPORT void shaUpdate(SHA_CTX *ctx, unsigned char *dataIn, int len); +G_MODULE_EXPORT void shaFinal(SHA_CTX *ctx, unsigned char hashout[20]); +G_MODULE_EXPORT void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]); +G_MODULE_EXPORT char *shahash(char *str); + +#endif diff --git a/lib/ssl_bogus.c b/lib/ssl_bogus.c new file mode 100644 index 00000000..00aaa7c4 --- /dev/null +++ b/lib/ssl_bogus.c @@ -0,0 +1,57 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* SSL module - dummy version                                           */ + +/* +  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 "ssl_client.h" + +int ssl_errno; + +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +{ +	return( NULL ); +} + +int ssl_read( void *conn, char *buf, int len ) +{ +	return( -1 ); +} + +int ssl_write( void *conn, const char *buf, int len ) +{ +	return( -1 ); +} + +void ssl_disconnect( void *conn_ ) +{ +} + +int ssl_getfd( void *conn ) +{ +	return( -1 ); +} + +b_input_condition ssl_getdirection( void *conn ) +{ +	return GAIM_INPUT_READ; +} diff --git a/lib/ssl_client.h b/lib/ssl_client.h new file mode 100644 index 00000000..1a9c79e9 --- /dev/null +++ b/lib/ssl_client.h @@ -0,0 +1,42 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* SSL module                                                           */ + +/* +  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 "proxy.h" + +#define SSL_OK            0 +#define SSL_NOHANDSHAKE   1 +#define SSL_AGAIN         2 + +extern int ssl_errno; + +typedef gboolean (*ssl_input_function)(gpointer, void*, b_input_condition); + +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 b_input_condition ssl_getdirection( void *conn ); diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c new file mode 100644 index 00000000..3ebe1756 --- /dev/null +++ b/lib/ssl_gnutls.c @@ -0,0 +1,206 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* SSL module - GnuTLS version                                          */ + +/* +  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 <gnutls/gnutls.h> +#include <fcntl.h> +#include <unistd.h> +#include "proxy.h" +#include "ssl_client.h" +#include "sock.h" +#include "stdlib.h" + +int ssl_errno = 0; + +static gboolean initialized = FALSE; + +struct scd +{ +	ssl_input_function func; +	gpointer data; +	int fd; +	gboolean established; +	int inpa; +	 +	gnutls_session session; +	gnutls_certificate_credentials xcred; +}; + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ); + + +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +{ +	struct scd *conn = g_new0( struct scd, 1 ); +	 +	conn->fd = proxy_connect( host, port, ssl_connected, conn ); +	conn->func = func; +	conn->data = data; +	conn->inpa = -1; +	 +	if( conn->fd < 0 ) +	{ +		g_free( conn ); +		return( NULL ); +	} +	 +	if( !initialized ) +	{ +		gnutls_global_init(); +		initialized = TRUE; +		atexit( gnutls_global_deinit ); +	} +	 +	gnutls_certificate_allocate_credentials( &conn->xcred ); +	gnutls_init( &conn->session, GNUTLS_CLIENT ); +	gnutls_set_default_priority( conn->session ); +	gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred ); +	 +	return( conn ); +} + +static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; +	 +	if( source == -1 ) +	{ +		conn->func( conn->data, NULL, cond ); +		 +		gnutls_deinit( conn->session ); +		gnutls_certificate_free_credentials( conn->xcred ); +		 +		g_free( conn ); +		 +		return FALSE; +	} +	 +	sock_make_nonblocking( conn->fd ); +	gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) conn->fd ); +	 +	return ssl_handshake( data, source, cond ); +} + +static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; +	int st; +	 +	if( ( st = gnutls_handshake( conn->session ) ) < 0 ) +	{ +		if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED ) +		{ +			conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ), +			                          ssl_handshake, data ); +		} +		else +		{ +			conn->func( conn->data, NULL, cond ); +			 +			gnutls_deinit( conn->session ); +			gnutls_certificate_free_credentials( conn->xcred ); +			closesocket( conn->fd ); +			 +			g_free( conn ); +		} +	} +	else +	{ +		/* For now we can't handle non-blocking perfectly everywhere... */ +		sock_make_blocking( conn->fd ); +		 +		conn->established = TRUE; +		conn->func( conn->data, conn, cond ); +	} +	 +	return FALSE; +} + +int ssl_read( void *conn, char *buf, int len ) +{ +	int st; +	 +	if( !((struct scd*)conn)->established ) +	{ +		ssl_errno = SSL_NOHANDSHAKE; +		return( -1 ); +	} +	 +	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 ); +	} +	 +	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_ ) +{ +	struct scd *conn = conn_; +	 +	if( conn->inpa != -1 ) +		b_event_remove( conn->inpa ); +	 +	if( conn->established ) +		gnutls_bye( conn->session, GNUTLS_SHUT_WR ); +	 +	closesocket( conn->fd ); +	 +	gnutls_deinit( conn->session ); +	gnutls_certificate_free_credentials( conn->xcred ); +	g_free( conn ); +} + +int ssl_getfd( void *conn ) +{ +	return( ((struct scd*)conn)->fd ); +} + +b_input_condition ssl_getdirection( void *conn ) +{ +	return( gnutls_record_get_direction( ((struct scd*)conn)->session ) ? +	        GAIM_INPUT_WRITE : GAIM_INPUT_READ ); +} diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c new file mode 100644 index 00000000..218b3a80 --- /dev/null +++ b/lib/ssl_nss.c @@ -0,0 +1,190 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2005 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* SSL module - NSS version                                             */ + +/* Copyright 2005 Jelmer Vernooij                                       */ + +/* +  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 "bitlbee.h" +#include "proxy.h" +#include "ssl_client.h" +#include "sock.h" +#include <nspr.h> +#include <prio.h> +#include <sslproto.h> +#include <nss.h> +#include <private/pprio.h> +#include <ssl.h> +#include <secerr.h> +#include <sslerr.h> + +int ssl_errno = 0; + +static gboolean initialized = FALSE; + +struct scd +{ +	ssl_input_function func; +	gpointer data; +	int fd; +	PRFileDesc *prfd; +	gboolean established; +}; + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ); + + +static SECStatus nss_auth_cert (void *arg, PRFileDesc *socket, PRBool checksig, PRBool isserver) +{ +	return SECSuccess; +} + +static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket)  +{ +	PRErrorCode err; + +	if(!arg) return SECFailure; + +	*(PRErrorCode *)arg = err = PORT_GetError(); + +	switch(err) { +	case SEC_ERROR_INVALID_AVA: +	case SEC_ERROR_INVALID_TIME: +	case SEC_ERROR_BAD_SIGNATURE: +	case SEC_ERROR_EXPIRED_CERTIFICATE: +	case SEC_ERROR_UNKNOWN_ISSUER: +	case SEC_ERROR_UNTRUSTED_CERT: +	case SEC_ERROR_CERT_VALID: +	case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: +	case SEC_ERROR_CRL_EXPIRED: +	case SEC_ERROR_CRL_BAD_SIGNATURE: +	case SEC_ERROR_EXTENSION_VALUE_INVALID: +	case SEC_ERROR_CA_CERT_INVALID: +	case SEC_ERROR_CERT_USAGES_INVALID: +	case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION: +		return SECSuccess; + +	default: +		return SECFailure; +	} +} + + +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +{ +	struct scd *conn = g_new0( struct scd, 1 ); +	 +	conn->fd = proxy_connect( host, port, ssl_connected, conn ); +	conn->func = func; +	conn->data = data; +	 +	if( conn->fd < 0 ) +	{ +		g_free( conn ); +		return( NULL ); +	} +	 +	if( !initialized ) +	{ +		PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); +		NSS_NoDB_Init(NULL); +		NSS_SetDomesticPolicy(); +	} + +	 +	return( conn ); +} + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; +	 +	if( source == -1 ) +		goto ssl_connected_failure; +	 +	/* Until we find out how to handle non-blocking I/O with NSS... */ +	sock_make_blocking( conn->fd ); +	 +	conn->prfd = SSL_ImportFD(NULL, PR_ImportTCPSocket(source)); +	SSL_OptionSet(conn->prfd, SSL_SECURITY, PR_TRUE); +	SSL_OptionSet(conn->prfd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); +	SSL_BadCertHook(conn->prfd, (SSLBadCertHandler)nss_bad_cert, NULL); +	SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate)nss_auth_cert, (void *)CERT_GetDefaultCertDB()); +	SSL_ResetHandshake(conn->prfd, PR_FALSE); + +	if (SSL_ForceHandshake(conn->prfd)) { +		goto ssl_connected_failure; +	} +	 +	 +	conn->established = TRUE; +	conn->func( conn->data, conn, cond ); +	return FALSE; +	 +	ssl_connected_failure: +	 +	conn->func( conn->data, NULL, cond ); +	 +	PR_Close( conn -> prfd ); +	if( source >= 0 ) closesocket( source ); +	g_free( conn ); +	 +	return FALSE; +} + +int ssl_read( void *conn, char *buf, int len ) +{ +	if( !((struct scd*)conn)->established ) +		return( 0 ); +	 +	return( PR_Read( ((struct scd*)conn)->prfd, buf, len ) ); +} + +int ssl_write( void *conn, const char *buf, int len ) +{ +	if( !((struct scd*)conn)->established ) +		return( 0 ); +	 +	return( PR_Write ( ((struct scd*)conn)->prfd, buf, len ) ); +} + +void ssl_disconnect( void *conn_ ) +{ +	struct scd *conn = conn_; +	 +	PR_Close( conn->prfd ); +	closesocket( conn->fd ); +	 +	g_free( conn ); +} + +int ssl_getfd( void *conn ) +{ +	return( ((struct scd*)conn)->fd ); +} + +b_input_condition ssl_getdirection( void *conn ) +{ +	/* Just in case someone calls us, let's return the most likely case: */ +	return GAIM_INPUT_READ; +} diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c new file mode 100644 index 00000000..b6f6c520 --- /dev/null +++ b/lib/ssl_openssl.c @@ -0,0 +1,221 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* SSL module - OpenSSL version                                         */ + +/* +  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 <openssl/crypto.h> +#include <openssl/rand.h> +#include <openssl/x509.h> +#include <openssl/pem.h> +#include <openssl/ssl.h> +#include <openssl/err.h> + +#include "proxy.h" +#include "ssl_client.h" +#include "sock.h" + +int ssl_errno = 0; + +static gboolean initialized = FALSE; + +struct scd +{ +	ssl_input_function func; +	gpointer data; +	int fd; +	gboolean established; +	 +	int inpa; +	int lasterr;		/* Necessary for SSL_get_error */ +	SSL *ssl; +	SSL_CTX *ssl_ctx; +}; + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ); + + +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +{ +	struct scd *conn = g_new0( struct scd, 1 ); +	SSL_METHOD *meth; +	 +	conn->fd = proxy_connect( host, port, ssl_connected, conn ); +	conn->func = func; +	conn->data = data; +	 +	if( conn->fd < 0 ) +	{ +		g_free( conn ); +		return( NULL ); +	} +	 +	if( !initialized ) +	{ +		initialized = TRUE; +		SSLeay_add_ssl_algorithms(); +	} +	 +	meth = TLSv1_client_method(); +	conn->ssl_ctx = SSL_CTX_new( meth ); +	if( conn->ssl_ctx == NULL ) +	{ +		conn->fd = -1; +		return( NULL ); +	} +	 +	conn->ssl = SSL_new( conn->ssl_ctx ); +	if( conn->ssl == NULL ) +	{ +		conn->fd = -1; +		return( NULL ); +	} +	 +	return( conn ); +} + +static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; +	 +	if( source == -1 ) +		return ssl_handshake( data, -1, cond ); +	 +	/* We can do at least the handshake with non-blocking I/O */ +	sock_make_nonblocking( conn->fd ); +	SSL_set_fd( conn->ssl, conn->fd ); +	 +	return ssl_handshake( data, source, cond ); +}	 + +static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; +	int st; +	 +	if( ( st = SSL_connect( conn->ssl ) ) < 0 ) +	{ +		conn->lasterr = SSL_get_error( conn->ssl, st ); +		if( conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE ) +			goto ssl_connected_failure; +		 +		conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ), ssl_handshake, data ); +		return FALSE; +	} +	 +	conn->established = TRUE; +	sock_make_blocking( conn->fd );		/* For now... */ +	conn->func( conn->data, conn, cond ); +	return FALSE; +	 +ssl_connected_failure: +	conn->func( conn->data, NULL, cond ); +	 +	if( conn->ssl ) +	{ +		SSL_shutdown( conn->ssl ); +		SSL_free( conn->ssl ); +	} +	if( conn->ssl_ctx ) +	{ +		SSL_CTX_free( conn->ssl_ctx ); +	} +	if( source >= 0 ) closesocket( source ); +	g_free( conn ); +	 +	return FALSE; +} + +int ssl_read( void *conn, char *buf, int len ) +{ +	int st; +	 +	if( !((struct scd*)conn)->established ) +	{ +		ssl_errno = SSL_NOHANDSHAKE; +		return -1; +	} +	 +	st = SSL_read( ((struct scd*)conn)->ssl, buf, len ); +	 +	ssl_errno = SSL_OK; +	if( st <= 0 ) +	{ +		((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st ); +		if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ) +			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; +	} +	 +	st = SSL_write( ((struct scd*)conn)->ssl, buf, len ); +	 +	ssl_errno = SSL_OK; +	if( st <= 0 ) +	{ +		((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st ); +		if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ) +			ssl_errno = SSL_AGAIN; +	} +	 +	return st; +} + +void ssl_disconnect( void *conn_ ) +{ +	struct scd *conn = conn_; +	 +	if( conn->inpa != -1 ) +		b_event_remove( conn->inpa ); +	 +	if( conn->established ) +		SSL_shutdown( conn->ssl ); +	 +	closesocket( conn->fd ); +	 +	SSL_free( conn->ssl ); +	SSL_CTX_free( conn->ssl_ctx ); +	g_free( conn ); +} + +int ssl_getfd( void *conn ) +{ +	return( ((struct scd*)conn)->fd ); +} + +b_input_condition ssl_getdirection( void *conn ) +{ +	return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? GAIM_INPUT_WRITE : GAIM_INPUT_READ ); +} diff --git a/lib/url.c b/lib/url.c new file mode 100644 index 00000000..e4deac78 --- /dev/null +++ b/lib/url.c @@ -0,0 +1,107 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2001-2005 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* URL/mirror stuff - Stolen from Axel                                  */ + +/* +  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 "url.h" + +/* Convert an URL to a url_t structure					*/ +int url_set( url_t *url, char *set_url ) +{ +	char s[MAX_STRING]; +	char *i; +	 +	/* protocol://							*/ +	if( ( i = strstr( set_url, "://" ) ) == NULL ) +	{ +		url->proto = PROTO_DEFAULT; +		strncpy( s, set_url, MAX_STRING ); +	} +	else +	{ +		if( g_strncasecmp( set_url, "http", i - set_url ) == 0 ) +			url->proto = PROTO_HTTP; +		else if( g_strncasecmp( set_url, "https", i - set_url ) == 0 ) +			url->proto = PROTO_HTTPS; +		else if( g_strncasecmp( set_url, "socks4", i - set_url ) == 0 ) +			url->proto = PROTO_SOCKS4; +		else if( g_strncasecmp( set_url, "socks5", i - set_url ) == 0 ) +			url->proto = PROTO_SOCKS5; +		else +		{ +			return( 0 ); +		} +		strncpy( s, i + 3, MAX_STRING ); +	} +	 +	/* Split							*/ +	if( ( i = strchr( s, '/' ) ) == NULL ) +	{ +		strcpy( url->file, "/" ); +	} +	else +	{ +		strncpy( url->file, i, MAX_STRING ); +		*i = 0; +	} +	strncpy( url->host, s, MAX_STRING ); +	 +	/* Check for username in host field				*/ +	if( strrchr( url->host, '@' ) != NULL ) +	{ +		strncpy( url->user, url->host, MAX_STRING ); +		i = strrchr( url->user, '@' ); +		*i = 0; +		strcpy( url->host, i + 1 ); +		*url->pass = 0; +	} +	/* If not: Fill in defaults					*/ +	else +	{ +		*url->user = *url->pass = 0; +	} +	 +	/* Password?							*/ +	if( ( i = strchr( url->user, ':' ) ) != NULL ) +	{ +		*i = 0; +		strcpy( url->pass, i + 1 ); +	} +	/* Port number?							*/ +	if( ( i = strchr( url->host, ':' ) ) != NULL ) +	{ +		*i = 0; +		sscanf( i + 1, "%d", &url->port ); +	} +	else +	{ +		if( url->proto == PROTO_HTTP ) +			url->port = 80; +		else if( url->proto == PROTO_HTTPS ) +			url->port = 443; +		else if( url->proto == PROTO_SOCKS4 || url->proto == PROTO_SOCKS4 ) +			url->port = 1080; +	} +	 +	return( url->port > 0 ); +} diff --git a/lib/url.h b/lib/url.h new file mode 100644 index 00000000..e9e1ecfe --- /dev/null +++ b/lib/url.h @@ -0,0 +1,44 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2001-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* URL/mirror stuff - Stolen from Axel                                  */ + +/* +  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 "bitlbee.h" + +#define PROTO_HTTP		2 +#define PROTO_HTTPS		5 +#define PROTO_SOCKS4	3 +#define PROTO_SOCKS5	4 +#define PROTO_DEFAULT	PROTO_HTTP + +typedef struct url +{ +	int proto; +	int port; +	char host[MAX_STRING]; +	char file[MAX_STRING]; +	char user[MAX_STRING]; +	char pass[MAX_STRING]; +} url_t; + +int url_set( url_t *url, char *set_url ); | 
