diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/Makefile | 46 | ||||
| -rw-r--r-- | lib/arc.c | 223 | ||||
| -rw-r--r-- | lib/arc.h | 40 | ||||
| -rw-r--r-- | lib/base64.c | 153 | ||||
| -rw-r--r-- | lib/base64.h | 33 | ||||
| -rw-r--r-- | lib/des.c | 646 | ||||
| -rw-r--r-- | lib/des.h | 51 | ||||
| -rw-r--r-- | lib/events.h | 87 | ||||
| -rw-r--r-- | lib/events_glib.c | 153 | ||||
| -rw-r--r-- | lib/events_libevent.c | 294 | ||||
| -rw-r--r-- | lib/ftutil.c | 144 | ||||
| -rw-r--r-- | lib/ftutil.h | 40 | ||||
| -rw-r--r-- | lib/http_client.c | 456 | ||||
| -rw-r--r-- | lib/http_client.h | 84 | ||||
| -rw-r--r-- | lib/ini.c | 145 | ||||
| -rw-r--r-- | lib/ini.h | 45 | ||||
| -rw-r--r-- | lib/md5.c | 262 | ||||
| -rw-r--r-- | lib/md5.h | 46 | ||||
| -rw-r--r-- | lib/misc.c | 730 | ||||
| -rw-r--r-- | lib/misc.h | 74 | ||||
| -rw-r--r-- | lib/oauth.c | 456 | ||||
| -rw-r--r-- | lib/oauth.h | 94 | ||||
| -rw-r--r-- | lib/proxy.c | 586 | ||||
| -rw-r--r-- | lib/proxy.h | 53 | ||||
| -rw-r--r-- | lib/sha1.c | 422 | ||||
| -rw-r--r-- | lib/sha1.h | 71 | ||||
| -rw-r--r-- | lib/ssl_bogus.c | 71 | ||||
| -rw-r--r-- | lib/ssl_client.h | 84 | ||||
| -rw-r--r-- | lib/ssl_gnutls.c | 280 | ||||
| -rw-r--r-- | lib/ssl_nss.c | 240 | ||||
| -rw-r--r-- | lib/ssl_openssl.c | 305 | ||||
| -rw-r--r-- | lib/ssl_sspi.c | 278 | ||||
| -rw-r--r-- | lib/url.c | 109 | ||||
| -rw-r--r-- | lib/url.h | 44 | ||||
| -rw-r--r-- | lib/xmltree.c | 698 | ||||
| -rw-r--r-- | lib/xmltree.h | 100 | 
36 files changed, 7643 insertions, 0 deletions
| diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 00000000..3ae43935 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,46 @@ +########################### +## Makefile for BitlBee  ## +##                       ## +## Copyright 2006 Lintux ## +########################### + +### DEFINITIONS + +-include ../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)lib/ +endif + +# [SH] Program variables +objects = arc.o base64.o $(DES) $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o + +LFLAGS += -r + +# [SH] Phony targets +all: lib.o +check: all +lcov: check +gcov: +	gcov *.c + +.PHONY: all clean distclean + +clean: $(subdirs) +	rm -f *.o $(OUTFILE) core + +distclean: clean $(subdirs) +	rm -rf .depend + +### MAIN PROGRAM + +lib.o: $(objects) $(subdirs) +	@echo '*' Linking lib.o +	@$(LD) $(LFLAGS) $(objects) -o lib.o + +$(objects): ../Makefile.settings Makefile + +$(objects): %.o: $(SRCDIR)%.c +	@echo '*' Compiling $< +	@$(CC) -c $(CFLAGS) $< -o $@ + +-include .depend/*.d diff --git a/lib/arc.c b/lib/arc.c new file mode 100644 index 00000000..fd498454 --- /dev/null +++ b/lib/arc.c @@ -0,0 +1,223 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple (but secure) ArcFour implementation for safer password storage.   * +*                                                                           * +*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*  This library is free software; you can redistribute it and/or            * +*  modify it under the terms of the GNU Lesser General Public               * +*  License as published by the Free Software Foundation, version            * +*  2.1.                                                                     * +*                                                                           * +*  This library 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        * +*  Lesser General Public License for more details.                          * +*                                                                           * +*  You should have received a copy of the GNU Lesser General Public License * +*  along with this library; if not, write to the Free Software Foundation,  * +*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           * +*                                                                           * +\***************************************************************************/ + +/*  +   This file implements ArcFour-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 picked ArcFour is because it's pretty simple but effective, +   so it will work without adding several KBs or an extra library dependency. +    +   (ArcFour is an RC4-compatible cipher. See for details: +   http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt) +*/ + + +#include <glib.h> +#include <gmodule.h> +#include <stdlib.h> +#include <string.h> +#include "misc.h" +#include "arc.h" + +/* Add some seed to the password, to make sure we *never* use the same key. +   This defines how many bytes we use as a seed. */ +#define ARC_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 ARC_CYCLES 1024 + +struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles ) +{ +	struct arc_state *st; +	int i, j, tmp; +	unsigned char S2[256]; +	 +	st = g_malloc( sizeof( struct arc_state ) ); +	st->i = st->j = 0; +	if( kl <= 0 ) +		kl = strlen( (char*) key ); +	 +	for( i = 0; i < 256; i ++ ) +	{ +		st->S[i] = i; +		S2[i] = key[i%kl]; +	} +	 +	for( i = j = 0; i < 256; i ++ ) +	{ +		j = ( j + st->S[i] + S2[i] ) & 0xff; +		tmp = st->S[i]; +		st->S[i] = st->S[j]; +		st->S[j] = tmp; +	} +	 +	memset( S2, 0, 256 ); +	i = j = 0; +	 +	for( i = 0; i < cycles; i ++ ) +		arc_getbyte( st ); +	 +	return st; +} + +/* +   For those who don't know, ArcFour 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 byte generator, the next function can +   be used to get bytes from the generator (and shuffle things a bit). +*/ + +unsigned char arc_getbyte( struct arc_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; +	tmp = (st->S[st->i] + st->S[st->j]) & 0xff; +	 +	return st->S[tmp]; +} + +/* +   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 state +   structures. These 6 bytes are also saved in the results, because of +   course we'll need them in arc_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. +    +   Note that if you use the pad_to argument, you will need zero-termi- +   nation to find back the original string length after decryption. So +   it shouldn't be used if your string contains \0s by itself! +*/ + +int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to ) +{ +	struct arc_state *st; +	unsigned char *key; +	char *padded = NULL; +	int key_len, i, padded_len; +	 +	key_len = strlen( password ) + ARC_IV_LEN; +	if( clear_len <= 0 ) +		clear_len = strlen( clear ); +	 +	/* Pad the string to the closest multiple of pad_to. This makes it +	   impossible to see the exact length of the password. */ +	if( pad_to > 0 && ( clear_len % pad_to ) > 0 ) +	{ +		padded_len = clear_len + pad_to - ( clear_len % pad_to ); +		padded = g_malloc( padded_len ); +		memcpy( padded, clear, clear_len ); +		 +		/* First a \0 and then random data, so we don't have to do +		   anything special when decrypting. */ +		padded[clear_len] = 0; +		random_bytes( (unsigned char*) padded + clear_len + 1, padded_len - clear_len - 1 ); +		 +		clear = padded; +		clear_len = padded_len; +	} +	 +	/* Prepare buffers and the key + IV */ +	*crypt = g_malloc( clear_len + ARC_IV_LEN ); +	key = g_malloc( key_len ); +	strcpy( (char*) key, password ); +	 +	/* Add the salt. Save it for later (when decrypting) and, of course, +	   add it to the encryption key. */ +	random_bytes( crypt[0], ARC_IV_LEN ); +	memcpy( key + key_len - ARC_IV_LEN, crypt[0], ARC_IV_LEN ); +	 +	/* Generate the initial S[] from the IVed key. */ +	st = arc_keymaker( key, key_len, ARC_CYCLES ); +	g_free( key ); +	 +	for( i = 0; i < clear_len; i ++ ) +		crypt[0][i+ARC_IV_LEN] = clear[i] ^ arc_getbyte( st ); +	 +	g_free( st ); +	g_free( padded ); +	 +	return clear_len + ARC_IV_LEN; +} + +int arc_decode( unsigned char *crypt, int crypt_len, char **clear, char *password ) +{ +	struct arc_state *st; +	unsigned char *key; +	int key_len, clear_len, i; +	 +	key_len = strlen( password ) + ARC_IV_LEN; +	clear_len = crypt_len - ARC_IV_LEN; +	 +	if( clear_len < 0 ) +	{ +		*clear = g_strdup( "" ); +		return 0; +	} +	 +	/* 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 < ARC_IV_LEN; i ++ ) +		key[key_len-ARC_IV_LEN+i] = crypt[i]; +	 +	/* Generate the initial S[] from the IVed key. */ +	st = arc_keymaker( key, key_len, ARC_CYCLES ); +	g_free( key ); +	 +	for( i = 0; i < clear_len; i ++ ) +		clear[0][i] = crypt[i+ARC_IV_LEN] ^ arc_getbyte( st ); +	clear[0][i] = 0; /* Nice to have for plaintexts. */ +	 +	g_free( st ); +	 +	return clear_len; +} diff --git a/lib/arc.h b/lib/arc.h new file mode 100644 index 00000000..816fa612 --- /dev/null +++ b/lib/arc.h @@ -0,0 +1,40 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple (but secure) ArcFour implementation for safer password storage.   * +*                                                                           * +*  Copyright 2007 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.              * +*                                                                           * +\***************************************************************************/ + + +/* See arc.c for more information. */ + +struct arc_state +{ +	unsigned char S[256]; +	unsigned char i, j; +}; + +#ifndef G_GNUC_MALLOC +#define G_GNUC_MALLOC +#endif + +G_GNUC_MALLOC struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles ); +unsigned char arc_getbyte( struct arc_state *st ); +int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to ); +int arc_decode( unsigned char *crypt, int crypt_len, char **clear, char *password ); diff --git a/lib/base64.c b/lib/base64.c new file mode 100644 index 00000000..ea0db6b9 --- /dev/null +++ b/lib/base64.c @@ -0,0 +1,153 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Base64 handling functions. encode_real() is mostly based on the y64 en-  * +*  coder from libyahoo2. Moving it to a new file because it's getting big.  * +*                                                                           * +*  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.              * +*                                                                           * +\***************************************************************************/ + +#include <glib.h> +#include <string.h> +#include "base64.h" + +static const char real_b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +char *tobase64(const char *text) +{ +	return base64_encode((const unsigned char *)text, strlen(text)); +} + +char *base64_encode(const unsigned char *in, int len) +{ +	char *out; +	 +	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((unsigned char*) in, len, (unsigned char*) out, real_b64); +	 +	return out; +} + +int base64_encode_real(const unsigned char *in, int inlen, unsigned char *out, const char *b64digits) +{ +	int outlen = 0; +	 +	for (; inlen >= 3; inlen -= 3) +	{ +		out[outlen++] = b64digits[in[0] >> 2]; +		out[outlen++] = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)]; +		out[outlen++] = b64digits[((in[1]<<2) & 0x3c) | (in[2]>>6)]; +		out[outlen++] = b64digits[in[2] & 0x3f]; +		in += 3; +	} +	if (inlen > 0) +	{ +		out[outlen++] = b64digits[in[0] >> 2]; +		if (inlen > 1) +		{ +			out[outlen++] = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)]; +			out[outlen++] = b64digits[((in[1]<<2) & 0x3c)]; +		} +		else +		{ +			out[outlen++] = b64digits[((in[0]<<4) & 0x30)]; +			out[outlen++] = b64digits[64]; +		} +		out[outlen++] = b64digits[64]; +	} +	out[outlen] = 0; +	 +	return outlen; +} + +/* Just a simple wrapper, but usually not very convenient because of zero +   termination. */ +char *frombase64(const char *in) +{ +	unsigned char *out; +	 +	base64_decode(in, &out); +	 +	return (char*) out; +} + +/* FIXME: Lookup table stuff is not threadsafe! (But for now BitlBee is not threaded.) */ +int base64_decode(const char *in, unsigned char **out) +{ +	static char b64rev[256] = { 0 }; +	int len, i; +	 +	/* Create a reverse-lookup for the Base64 sequence. */ +	if( b64rev[0] == 0 ) +	{ +		memset( b64rev, 0xff, 256 ); +		for( i = 0; i <= 64; i ++ ) +			b64rev[(int)real_b64[i]] = i; +	} +	 +	len = strlen( in ); +	*out = g_malloc( ( len + 6 ) / 4 * 3 ); +	len = base64_decode_real( (unsigned char*) in, *out, b64rev ); +	*out = g_realloc( *out, len + 1 ); +	out[0][len] = 0;	/* Zero termination can't hurt. */ +	 +	return len; +} + +int base64_decode_real(const unsigned char *in, unsigned char *out, char *b64rev) +{ +	int i, outlen = 0; +	 +	for( i = 0; in[i] && in[i+1] && in[i+2] && in[i+3]; i += 4 ) +	{ +		int sx; +		 +		sx = b64rev[(int)in[i+0]]; +		if( sx >= 64 ) +			break; +		out[outlen] = ( sx << 2 ) & 0xfc; +		 +		sx = b64rev[(int)in[i+1]]; +		if( sx >= 64 ) +			break; +		out[outlen] |= ( sx >> 4 ) & 0x03; +		outlen ++; +		out[outlen] = ( sx << 4 ) & 0xf0; +		 +		sx = b64rev[(int)in[i+2]]; +		if( sx >= 64 ) +			break; +		out[outlen] |= ( sx >> 2 ) & 0x0f; +		outlen ++; +		out[outlen] = ( sx << 6 ) & 0xc0; +		 +		sx = b64rev[(int)in[i+3]]; +		if( sx >= 64 ) +			break; +		out[outlen] |= sx; +		outlen ++; +	} +	 +	/* If sx > 64 the base64 string was damaged. Should we ignore this? */ +	 +	return outlen; +} diff --git a/lib/base64.h b/lib/base64.h new file mode 100644 index 00000000..ebd74bf1 --- /dev/null +++ b/lib/base64.h @@ -0,0 +1,33 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Base64 handling functions. encode_real() is mostly based on the y64 en-  * +*  coder from libyahoo2. Moving it to a new file because it's getting big.  * +*                                                                           * +*  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.              * +*                                                                           * +\***************************************************************************/ + +#include <glib.h> +#include <gmodule.h> + +G_MODULE_EXPORT char *tobase64( const char *text ); +G_MODULE_EXPORT char *base64_encode( const unsigned char *in, int len ); +G_MODULE_EXPORT int base64_encode_real( const unsigned char *in, int inlen, unsigned char *out, const char *b64digits ); +G_MODULE_EXPORT char *frombase64( const char *in ); +G_MODULE_EXPORT int base64_decode( const char *in, unsigned char **out ); +G_MODULE_EXPORT int base64_decode_real( const unsigned char *in, unsigned char *out, char *b64reverse ); diff --git a/lib/des.c b/lib/des.c new file mode 100644 index 00000000..3b9cc8d5 --- /dev/null +++ b/lib/des.c @@ -0,0 +1,646 @@ +/* + *  FIPS-46-3 compliant 3DES implementation + * + *  Copyright (C) 2001-2003  Christophe Devine + * + *  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 + */ + +/* + * Modified for BitlBee: Added a function compatible with the existing + * function in ssl_openssl.c, fairly specialised for MSN auth (since that's + * all this is used for at least for now). + * + * Added some consts to the tables at the top, and disabled some 64-bit + * and 128-bit key code that I don't need. + * + * *Many* thanks to Christophe for this compact and easy to import code. + */ + +#include <string.h> +#include <glib.h> +#include "des.h" + +/* the eight DES S-boxes */ + +static const uint32_t SB1[64] = +{ +    0x01010400, 0x00000000, 0x00010000, 0x01010404, +    0x01010004, 0x00010404, 0x00000004, 0x00010000, +    0x00000400, 0x01010400, 0x01010404, 0x00000400, +    0x01000404, 0x01010004, 0x01000000, 0x00000004, +    0x00000404, 0x01000400, 0x01000400, 0x00010400, +    0x00010400, 0x01010000, 0x01010000, 0x01000404, +    0x00010004, 0x01000004, 0x01000004, 0x00010004, +    0x00000000, 0x00000404, 0x00010404, 0x01000000, +    0x00010000, 0x01010404, 0x00000004, 0x01010000, +    0x01010400, 0x01000000, 0x01000000, 0x00000400, +    0x01010004, 0x00010000, 0x00010400, 0x01000004, +    0x00000400, 0x00000004, 0x01000404, 0x00010404, +    0x01010404, 0x00010004, 0x01010000, 0x01000404, +    0x01000004, 0x00000404, 0x00010404, 0x01010400, +    0x00000404, 0x01000400, 0x01000400, 0x00000000, +    0x00010004, 0x00010400, 0x00000000, 0x01010004 +}; + +static const uint32_t SB2[64] = +{ +    0x80108020, 0x80008000, 0x00008000, 0x00108020, +    0x00100000, 0x00000020, 0x80100020, 0x80008020, +    0x80000020, 0x80108020, 0x80108000, 0x80000000, +    0x80008000, 0x00100000, 0x00000020, 0x80100020, +    0x00108000, 0x00100020, 0x80008020, 0x00000000, +    0x80000000, 0x00008000, 0x00108020, 0x80100000, +    0x00100020, 0x80000020, 0x00000000, 0x00108000, +    0x00008020, 0x80108000, 0x80100000, 0x00008020, +    0x00000000, 0x00108020, 0x80100020, 0x00100000, +    0x80008020, 0x80100000, 0x80108000, 0x00008000, +    0x80100000, 0x80008000, 0x00000020, 0x80108020, +    0x00108020, 0x00000020, 0x00008000, 0x80000000, +    0x00008020, 0x80108000, 0x00100000, 0x80000020, +    0x00100020, 0x80008020, 0x80000020, 0x00100020, +    0x00108000, 0x00000000, 0x80008000, 0x00008020, +    0x80000000, 0x80100020, 0x80108020, 0x00108000 +}; + +static const uint32_t SB3[64] = +{ +    0x00000208, 0x08020200, 0x00000000, 0x08020008, +    0x08000200, 0x00000000, 0x00020208, 0x08000200, +    0x00020008, 0x08000008, 0x08000008, 0x00020000, +    0x08020208, 0x00020008, 0x08020000, 0x00000208, +    0x08000000, 0x00000008, 0x08020200, 0x00000200, +    0x00020200, 0x08020000, 0x08020008, 0x00020208, +    0x08000208, 0x00020200, 0x00020000, 0x08000208, +    0x00000008, 0x08020208, 0x00000200, 0x08000000, +    0x08020200, 0x08000000, 0x00020008, 0x00000208, +    0x00020000, 0x08020200, 0x08000200, 0x00000000, +    0x00000200, 0x00020008, 0x08020208, 0x08000200, +    0x08000008, 0x00000200, 0x00000000, 0x08020008, +    0x08000208, 0x00020000, 0x08000000, 0x08020208, +    0x00000008, 0x00020208, 0x00020200, 0x08000008, +    0x08020000, 0x08000208, 0x00000208, 0x08020000, +    0x00020208, 0x00000008, 0x08020008, 0x00020200 +}; + +static const uint32_t SB4[64] = +{ +    0x00802001, 0x00002081, 0x00002081, 0x00000080, +    0x00802080, 0x00800081, 0x00800001, 0x00002001, +    0x00000000, 0x00802000, 0x00802000, 0x00802081, +    0x00000081, 0x00000000, 0x00800080, 0x00800001, +    0x00000001, 0x00002000, 0x00800000, 0x00802001, +    0x00000080, 0x00800000, 0x00002001, 0x00002080, +    0x00800081, 0x00000001, 0x00002080, 0x00800080, +    0x00002000, 0x00802080, 0x00802081, 0x00000081, +    0x00800080, 0x00800001, 0x00802000, 0x00802081, +    0x00000081, 0x00000000, 0x00000000, 0x00802000, +    0x00002080, 0x00800080, 0x00800081, 0x00000001, +    0x00802001, 0x00002081, 0x00002081, 0x00000080, +    0x00802081, 0x00000081, 0x00000001, 0x00002000, +    0x00800001, 0x00002001, 0x00802080, 0x00800081, +    0x00002001, 0x00002080, 0x00800000, 0x00802001, +    0x00000080, 0x00800000, 0x00002000, 0x00802080 +}; + +static const uint32_t SB5[64] = +{ +    0x00000100, 0x02080100, 0x02080000, 0x42000100, +    0x00080000, 0x00000100, 0x40000000, 0x02080000, +    0x40080100, 0x00080000, 0x02000100, 0x40080100, +    0x42000100, 0x42080000, 0x00080100, 0x40000000, +    0x02000000, 0x40080000, 0x40080000, 0x00000000, +    0x40000100, 0x42080100, 0x42080100, 0x02000100, +    0x42080000, 0x40000100, 0x00000000, 0x42000000, +    0x02080100, 0x02000000, 0x42000000, 0x00080100, +    0x00080000, 0x42000100, 0x00000100, 0x02000000, +    0x40000000, 0x02080000, 0x42000100, 0x40080100, +    0x02000100, 0x40000000, 0x42080000, 0x02080100, +    0x40080100, 0x00000100, 0x02000000, 0x42080000, +    0x42080100, 0x00080100, 0x42000000, 0x42080100, +    0x02080000, 0x00000000, 0x40080000, 0x42000000, +    0x00080100, 0x02000100, 0x40000100, 0x00080000, +    0x00000000, 0x40080000, 0x02080100, 0x40000100 +}; + +static const uint32_t SB6[64] = +{ +    0x20000010, 0x20400000, 0x00004000, 0x20404010, +    0x20400000, 0x00000010, 0x20404010, 0x00400000, +    0x20004000, 0x00404010, 0x00400000, 0x20000010, +    0x00400010, 0x20004000, 0x20000000, 0x00004010, +    0x00000000, 0x00400010, 0x20004010, 0x00004000, +    0x00404000, 0x20004010, 0x00000010, 0x20400010, +    0x20400010, 0x00000000, 0x00404010, 0x20404000, +    0x00004010, 0x00404000, 0x20404000, 0x20000000, +    0x20004000, 0x00000010, 0x20400010, 0x00404000, +    0x20404010, 0x00400000, 0x00004010, 0x20000010, +    0x00400000, 0x20004000, 0x20000000, 0x00004010, +    0x20000010, 0x20404010, 0x00404000, 0x20400000, +    0x00404010, 0x20404000, 0x00000000, 0x20400010, +    0x00000010, 0x00004000, 0x20400000, 0x00404010, +    0x00004000, 0x00400010, 0x20004010, 0x00000000, +    0x20404000, 0x20000000, 0x00400010, 0x20004010 +}; + +static const uint32_t SB7[64] = +{ +    0x00200000, 0x04200002, 0x04000802, 0x00000000, +    0x00000800, 0x04000802, 0x00200802, 0x04200800, +    0x04200802, 0x00200000, 0x00000000, 0x04000002, +    0x00000002, 0x04000000, 0x04200002, 0x00000802, +    0x04000800, 0x00200802, 0x00200002, 0x04000800, +    0x04000002, 0x04200000, 0x04200800, 0x00200002, +    0x04200000, 0x00000800, 0x00000802, 0x04200802, +    0x00200800, 0x00000002, 0x04000000, 0x00200800, +    0x04000000, 0x00200800, 0x00200000, 0x04000802, +    0x04000802, 0x04200002, 0x04200002, 0x00000002, +    0x00200002, 0x04000000, 0x04000800, 0x00200000, +    0x04200800, 0x00000802, 0x00200802, 0x04200800, +    0x00000802, 0x04000002, 0x04200802, 0x04200000, +    0x00200800, 0x00000000, 0x00000002, 0x04200802, +    0x00000000, 0x00200802, 0x04200000, 0x00000800, +    0x04000002, 0x04000800, 0x00000800, 0x00200002 +}; + +static const uint32_t SB8[64] = +{ +    0x10001040, 0x00001000, 0x00040000, 0x10041040, +    0x10000000, 0x10001040, 0x00000040, 0x10000000, +    0x00040040, 0x10040000, 0x10041040, 0x00041000, +    0x10041000, 0x00041040, 0x00001000, 0x00000040, +    0x10040000, 0x10000040, 0x10001000, 0x00001040, +    0x00041000, 0x00040040, 0x10040040, 0x10041000, +    0x00001040, 0x00000000, 0x00000000, 0x10040040, +    0x10000040, 0x10001000, 0x00041040, 0x00040000, +    0x00041040, 0x00040000, 0x10041000, 0x00001000, +    0x00000040, 0x10040040, 0x00001000, 0x00041040, +    0x10001000, 0x00000040, 0x10000040, 0x10040000, +    0x10040040, 0x10000000, 0x00040000, 0x10001040, +    0x00000000, 0x10041040, 0x00040040, 0x10000040, +    0x10040000, 0x10001000, 0x10001040, 0x00000000, +    0x10041040, 0x00041000, 0x00041000, 0x00001040, +    0x00001040, 0x00040040, 0x10000000, 0x10041000 +}; + +/* PC1: left and right halves bit-swap */ + +static const uint32_t LHs[16] = +{ +    0x00000000, 0x00000001, 0x00000100, 0x00000101, +    0x00010000, 0x00010001, 0x00010100, 0x00010101, +    0x01000000, 0x01000001, 0x01000100, 0x01000101, +    0x01010000, 0x01010001, 0x01010100, 0x01010101 +}; + +static const uint32_t RHs[16] = +{ +    0x00000000, 0x01000000, 0x00010000, 0x01010000, +    0x00000100, 0x01000100, 0x00010100, 0x01010100, +    0x00000001, 0x01000001, 0x00010001, 0x01010001, +    0x00000101, 0x01000101, 0x00010101, 0x01010101, +}; + +/* platform-independant 32-bit integer manipulation macros */ + +#define GET_UINT32(n,b,i)                         \ +{                                                 \ +    (n) = ( (uint32_t) (b)[(i)    ] << 24 )       \ +        | ( (uint32_t) (b)[(i) + 1] << 16 )       \ +        | ( (uint32_t) (b)[(i) + 2] <<  8 )       \ +        | ( (uint32_t) (b)[(i) + 3]       );      \ +} + +#define PUT_UINT32(n,b,i)                         \ +{                                                 \ +    (b)[(i)    ] = (uint8_t) ( (n) >> 24 );       \ +    (b)[(i) + 1] = (uint8_t) ( (n) >> 16 );       \ +    (b)[(i) + 2] = (uint8_t) ( (n) >>  8 );       \ +    (b)[(i) + 3] = (uint8_t) ( (n)       );       \ +} + +/* Initial Permutation macro */ + +#define DES_IP(X,Y)                                             \ +{                                                               \ +    T = ((X >>  4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T <<  4);   \ +    T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16);   \ +    T = ((Y >>  2) ^ X) & 0x33333333; X ^= T; Y ^= (T <<  2);   \ +    T = ((Y >>  8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T <<  8);   \ +    Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF;                    \ +    T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T;                   \ +    X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF;                    \ +} + +/* Final Permutation macro */ + +#define DES_FP(X,Y)                                             \ +{                                                               \ +    X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF;                    \ +    T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T;                   \ +    Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF;                    \ +    T = ((Y >>  8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T <<  8);   \ +    T = ((Y >>  2) ^ X) & 0x33333333; X ^= T; Y ^= (T <<  2);   \ +    T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16);   \ +    T = ((X >>  4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T <<  4);   \ +} + +/* DES round macro */ + +#define DES_ROUND(X,Y)                          \ +{                                               \ +    T = *SK++ ^ X;                              \ +    Y ^= SB8[ (T      ) & 0x3F ] ^              \ +         SB6[ (T >>  8) & 0x3F ] ^              \ +         SB4[ (T >> 16) & 0x3F ] ^              \ +         SB2[ (T >> 24) & 0x3F ];               \ +                                                \ +    T = *SK++ ^ ((X << 28) | (X >> 4));         \ +    Y ^= SB7[ (T      ) & 0x3F ] ^              \ +         SB5[ (T >>  8) & 0x3F ] ^              \ +         SB3[ (T >> 16) & 0x3F ] ^              \ +         SB1[ (T >> 24) & 0x3F ];               \ +} + +/* DES key schedule */ + +int des_main_ks( uint32_t SK[32], const uint8_t key[8] ) +{ +    int i; +    uint32_t X, Y, T; + +    GET_UINT32( X, key, 0 ); +    GET_UINT32( Y, key, 4 ); + +    /* Permuted Choice 1 */ + +    T =  ((Y >>  4) ^ X) & 0x0F0F0F0F;  X ^= T; Y ^= (T <<  4); +    T =  ((Y      ) ^ X) & 0x10101010;  X ^= T; Y ^= (T      ); + +    X =   (LHs[ (X      ) & 0xF] << 3) | (LHs[ (X >>  8) & 0xF ] << 2) +        | (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ]     ) +        | (LHs[ (X >>  5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6) +        | (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4); + +    Y =   (RHs[ (Y >>  1) & 0xF] << 3) | (RHs[ (Y >>  9) & 0xF ] << 2) +        | (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ]     ) +        | (RHs[ (Y >>  4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6) +        | (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4); + +    X &= 0x0FFFFFFF; +    Y &= 0x0FFFFFFF; + +    /* calculate subkeys */ + +    for( i = 0; i < 16; i++ ) +    { +        if( i < 2 || i == 8 || i == 15 ) +        { +            X = ((X <<  1) | (X >> 27)) & 0x0FFFFFFF; +            Y = ((Y <<  1) | (Y >> 27)) & 0x0FFFFFFF; +        } +        else +        { +            X = ((X <<  2) | (X >> 26)) & 0x0FFFFFFF; +            Y = ((Y <<  2) | (Y >> 26)) & 0x0FFFFFFF; +        } + +        *SK++ =   ((X <<  4) & 0x24000000) | ((X << 28) & 0x10000000) +                | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000) +                | ((X <<  6) & 0x01000000) | ((X <<  9) & 0x00200000) +                | ((X >>  1) & 0x00100000) | ((X << 10) & 0x00040000) +                | ((X <<  2) & 0x00020000) | ((X >> 10) & 0x00010000) +                | ((Y >> 13) & 0x00002000) | ((Y >>  4) & 0x00001000) +                | ((Y <<  6) & 0x00000800) | ((Y >>  1) & 0x00000400) +                | ((Y >> 14) & 0x00000200) | ((Y      ) & 0x00000100) +                | ((Y >>  5) & 0x00000020) | ((Y >> 10) & 0x00000010) +                | ((Y >>  3) & 0x00000008) | ((Y >> 18) & 0x00000004) +                | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001); + +        *SK++ =   ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000) +                | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000) +                | ((X >>  2) & 0x02000000) | ((X <<  1) & 0x01000000) +                | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000) +                | ((X <<  3) & 0x00080000) | ((X >>  6) & 0x00040000) +                | ((X << 15) & 0x00020000) | ((X >>  4) & 0x00010000) +                | ((Y >>  2) & 0x00002000) | ((Y <<  8) & 0x00001000) +                | ((Y >> 14) & 0x00000808) | ((Y >>  9) & 0x00000400) +                | ((Y      ) & 0x00000200) | ((Y <<  7) & 0x00000100) +                | ((Y >>  7) & 0x00000020) | ((Y >>  3) & 0x00000011) +                | ((Y <<  2) & 0x00000004) | ((Y >> 21) & 0x00000002); +    } + +    return( 0 ); +} + +#if TEST +int des_set_key( des_context *ctx, uint8_t key[8] ) +{ +    int i; + +    /* setup encryption subkeys */ + +    des_main_ks( ctx->esk, key ); + +    /* setup decryption subkeys */ + +    for( i = 0; i < 32; i += 2 ) +    { +        ctx->dsk[i    ] = ctx->esk[30 - i]; +        ctx->dsk[i + 1] = ctx->esk[31 - i]; +    } + +    return( 0 ); +} + +/* DES 64-bit block encryption/decryption */ + +void des_crypt( uint32_t SK[32], uint8_t input[8], uint8_t output[8] ) +{ +    uint32_t X, Y, T; + +    GET_UINT32( X, input, 0 ); +    GET_UINT32( Y, input, 4 ); + +    DES_IP( X, Y ); + +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); + +    DES_FP( Y, X ); + +    PUT_UINT32( Y, output, 0 ); +    PUT_UINT32( X, output, 4 ); +} + +void des_encrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] ) +{ +    des_crypt( ctx->esk, input, output ); +} + +void des_decrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] ) +{ +    des_crypt( ctx->dsk, input, output ); +} + +/* Triple-DES key schedule */ + +int des3_set_2keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8] ) +{ +    int i; + +    des_main_ks( ctx->esk     , key1 ); +    des_main_ks( ctx->dsk + 32, key2 ); + +    for( i = 0; i < 32; i += 2 ) +    { +        ctx->dsk[i     ] = ctx->esk[30 - i]; +        ctx->dsk[i +  1] = ctx->esk[31 - i]; + +        ctx->esk[i + 32] = ctx->dsk[62 - i]; +        ctx->esk[i + 33] = ctx->dsk[63 - i]; + +        ctx->esk[i + 64] = ctx->esk[     i]; +        ctx->esk[i + 65] = ctx->esk[ 1 + i]; + +        ctx->dsk[i + 64] = ctx->dsk[     i]; +        ctx->dsk[i + 65] = ctx->dsk[ 1 + i]; +    } + +    return( 0 ); +} +#endif + +int des3_set_3keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8], +                                       const uint8_t key3[8] ) +{ +    int i; + +    des_main_ks( ctx->esk     , key1 ); +    des_main_ks( ctx->dsk + 32, key2 ); +    des_main_ks( ctx->esk + 64, key3 ); + +    for( i = 0; i < 32; i += 2 ) +    { +        ctx->dsk[i     ] = ctx->esk[94 - i]; +        ctx->dsk[i +  1] = ctx->esk[95 - i]; + +        ctx->esk[i + 32] = ctx->dsk[62 - i]; +        ctx->esk[i + 33] = ctx->dsk[63 - i]; + +        ctx->dsk[i + 64] = ctx->esk[30 - i]; +        ctx->dsk[i + 65] = ctx->esk[31 - i]; +    } + +    return( 0 ); +} + +/* Triple-DES 64-bit block encryption/decryption */ + +void des3_crypt( uint32_t SK[96], uint8_t input[8], uint8_t output[8] ) +{ +    uint32_t X, Y, T; + +    GET_UINT32( X, input, 0 ); +    GET_UINT32( Y, input, 4 ); + +    DES_IP( X, Y ); + +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); + +    DES_ROUND( X, Y );  DES_ROUND( Y, X ); +    DES_ROUND( X, Y );  DES_ROUND( Y, X ); +    DES_ROUND( X, Y );  DES_ROUND( Y, X ); +    DES_ROUND( X, Y );  DES_ROUND( Y, X ); +    DES_ROUND( X, Y );  DES_ROUND( Y, X ); +    DES_ROUND( X, Y );  DES_ROUND( Y, X ); +    DES_ROUND( X, Y );  DES_ROUND( Y, X ); +    DES_ROUND( X, Y );  DES_ROUND( Y, X ); + +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); +    DES_ROUND( Y, X );  DES_ROUND( X, Y ); + +    DES_FP( Y, X ); + +    PUT_UINT32( Y, output, 0 ); +    PUT_UINT32( X, output, 4 ); +} + +void des3_encrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] ) +{ +    des3_crypt( ctx->esk, input, output ); +} + +void des3_decrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] ) +{ +    des3_crypt( ctx->dsk, input, output ); +} + +size_t ssl_des3_encrypt( const unsigned char *key, size_t key_len, const unsigned char *input, +                         size_t input_len, const unsigned char *iv, unsigned char **res ) +{ +	des3_context ctx3; +	size_t off; +	uint8_t buf[8]; +		 +	/* Keep it simple, for as long as this is just used for MSN auth anyway. */ +	if( key_len != 24 || ( input_len % 8 ) != 0 ) +		return 0; +	 +	*res = g_malloc( input_len ); +	des3_set_3keys( &ctx3, key, key + 8, key + 16 ); +	 +	/* This loop does CBC 3DES. */ +	memcpy( buf, iv, 8 ); +	for( off = 0; off < input_len; off += 8 ) +	{ +		int i; +		 +		for( i = 0; i < 8; i ++ ) +			buf[i] ^= input[off+i]; +		des3_encrypt( &ctx3, buf, buf ); +		memcpy( *res + off, buf, 8 ); +	} +	 +	return input_len; +} + +#ifdef TEST + +#include <string.h> +#include <stdio.h> + +/* + * Triple-DES Monte Carlo Test: ECB mode + * source: NIST - tripledes-vectors.zip + */ + +static const unsigned char DES3_keys[3][8] = +{ +    { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }, +    { 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01 }, +    { 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23 } +}; + +static const unsigned char DES3_init[8] = +{ +    0x4E, 0x6F, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74 +}; + +static const unsigned char DES3_enc_test[3][8] = +{ +    { 0x6A, 0x2A, 0x19, 0xF4, 0x1E, 0xCA, 0x85, 0x4B }, +    { 0x03, 0xE6, 0x9F, 0x5B, 0xFA, 0x58, 0xEB, 0x42 }, +    { 0xDD, 0x17, 0xE8, 0xB8, 0xB4, 0x37, 0xD2, 0x32 } +}; +    +static const unsigned char DES3_dec_test[3][8] = +{ +    { 0xCD, 0xD6, 0x4F, 0x2F, 0x94, 0x27, 0xC1, 0x5D }, +    { 0x69, 0x96, 0xC8, 0xFA, 0x47, 0xA2, 0xAB, 0xEB }, +    { 0x83, 0x25, 0x39, 0x76, 0x44, 0x09, 0x1A, 0x0A } +}; + +int main( void ) +{ +    int m, n, i; +    des_context ctx; +    des3_context ctx3; +    unsigned char buf[8]; + +    for( m = 0; m < 2; m++ ) +    { +        printf( "\n Triple-DES Monte Carlo Test (ECB mode) - " ); + +        if( m == 0 ) printf( "encryption\n\n" ); +        if( m == 1 ) printf( "decryption\n\n" ); + +        for( n = 0; n < 3; n++ ) +        { +            printf( " Test %d, key size = %3d bits: ", +                    n + 1, 64 + n * 64 ); + +            fflush( stdout ); + +            memcpy( buf, DES3_init, 8 ); + +            switch( n ) +            { +                case 0: +                    des_set_key( &ctx, DES3_keys[0] ); +                    break; + +                case 1: +                    des3_set_2keys( &ctx3, DES3_keys[0], +                                           DES3_keys[1] ); +                    break; + +                case 2: +                    des3_set_3keys( &ctx3, DES3_keys[0], +                                           DES3_keys[1], +                                           DES3_keys[2] ); +                    break; +            } + +            for( i = 0; i < 10000; i++ ) +            { +                if( n == 0 ) +                { +                    if( m == 0 ) des_encrypt( &ctx, buf, buf ); +                    if( m == 1 ) des_decrypt( &ctx, buf, buf ); +                } +                else +                { +                    if( m == 0 ) des3_encrypt( &ctx3, buf, buf ); +                    if( m == 1 ) des3_decrypt( &ctx3, buf, buf ); +                } +            } + +            if( ( m == 0 && memcmp( buf, DES3_enc_test[n], 8 ) ) || +                ( m == 1 && memcmp( buf, DES3_dec_test[n], 8 ) ) ) +            { +                printf( "failed!\n" ); +                return( 1 ); +            } + +            printf( "passed.\n" ); +        } +    } + +    printf( "\n" ); + +    return( 0 ); +} + +#endif diff --git a/lib/des.h b/lib/des.h new file mode 100644 index 00000000..92fbfd22 --- /dev/null +++ b/lib/des.h @@ -0,0 +1,51 @@ +/* + *  FIPS-46-3 compliant 3DES implementation + * + *  Copyright (C) 2001-2003  Christophe Devine + * + *  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 + */ + +#ifndef _DES_H +#define _DES_H + +#include <stdint.h> + +typedef struct +{ +    uint32_t esk[32];     /* DES encryption subkeys */ +    uint32_t dsk[32];     /* DES decryption subkeys */ +} +des_context; + +typedef struct +{ +    uint32_t esk[96];     /* Triple-DES encryption subkeys */ +    uint32_t dsk[96];     /* Triple-DES decryption subkeys */ +} +des3_context; + +int  des_set_key( des_context *ctx, uint8_t key[8] ); +void des_encrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] ); +void des_decrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] ); + +int  des3_set_2keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8] ); +int  des3_set_3keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8], +                                        const uint8_t key3[8] ); + +void des3_encrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] ); +void des3_decrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] ); + +#endif /* des.h */ diff --git a/lib/events.h b/lib/events.h new file mode 100644 index 00000000..66c4c6b4 --- /dev/null +++ b/lib/events.h @@ -0,0 +1,87 @@ +/* + * 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 + * + */ + +/* This stuff used to be in proxy.c too, but I split it off so BitlBee can +   use other libraries (like libevent) to handle events. proxy.c is one very +   nice piece of work from Gaim. It connects to a TCP server in the back- +   ground and calls a callback function once the connection is ready to use. +   This function (proxy_connect()) can be found in proxy.c. (It also +   transparently handles HTTP/SOCKS proxies, when necessary.) +    +   This file offers some extra event handling toys, which will be handled +   by GLib or libevent. The advantage of using libevent is that it can use +   more advanced I/O polling functions like epoll() in recent Linux +   kernels. This should improve BitlBee's scalability. */ + + +#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> + +/* The conditions you can pass to b_input_add()/that will be passed to +   the given callback function. */ +typedef enum { +	B_EV_IO_READ = 1 << 0, +	B_EV_IO_WRITE = 1 << 1, +	B_EV_FLAG_FORCE_ONCE = 1 << 16, +	B_EV_FLAG_FORCE_REPEAT = 1 << 17, +} b_input_condition; +typedef gboolean (*b_event_handler)(gpointer data, gint fd, b_input_condition cond); + +/* For internal use. */ +#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... ) + +/* Call this once when the program starts. It'll initialize the event handler +   library (if necessary) and then return immediately. */ +G_MODULE_EXPORT void b_main_init(); + +/* This one enters the event loop. It shouldn't return until one of the event +   handlers calls b_main_quit(). */ +G_MODULE_EXPORT void b_main_run(); +G_MODULE_EXPORT void b_main_quit(); + + +/* Add event handlers (for I/O or a timeout). The event handler will be called +   every time the event "happens", until your event handler returns FALSE (or +   until you remove it using b_event_remove(). As usual, the data argument +   can be used to pass your own data to the event handler. */ +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); + +/* With libevent, this one also cleans up event handlers if that wasn't already +   done (the caller is expected to do so but may miss it sometimes). */ +G_MODULE_EXPORT void closesocket(int fd); + +#endif /* _EVENTS_H_ */ diff --git a/lib/events_glib.c b/lib/events_glib.c new file mode 100644 index 00000000..3fafc872 --- /dev/null +++ b/lib/events_glib.c @@ -0,0 +1,153 @@ +  /********************************************************************\ +  * 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; +	gpointer data; +	guint flags; +} GaimIOClosure; + +static GMainLoop *loop = NULL; + +void b_main_init() +{ +	if( loop == NULL ) +		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 |= B_EV_IO_READ; +	if (condition & GAIM_WRITE_COND) +		gaim_cond |= B_EV_IO_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" ); +	 +	if (closure->flags & B_EV_FLAG_FORCE_ONCE) +		return FALSE; +	else if (closure->flags & B_EV_FLAG_FORCE_REPEAT) +		return TRUE; +	else +		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; +	int st; +	 +	closure->function = function; +	closure->data = data; +	closure->flags = condition; +	 +	if (condition & B_EV_IO_READ) +		cond |= GAIM_READ_COND; +	if (condition & B_EV_IO_WRITE) +		cond |= GAIM_WRITE_COND; +	 +	channel = g_io_channel_unix_new(source); +	st = 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 (%p)\n", source, condition, function, data, st, closure ); +	 +	g_io_channel_unref(channel); +	return st; +} + +gint b_timeout_add(gint timeout, b_event_handler func, gpointer data) +{ +	/* GSourceFunc and the BitlBee event handler function aren't +	   really the same, but they're "compatible". ;-) It will do +	   for now, BitlBee only looks at the "data" argument. */ +	gint st = g_timeout_add(timeout, (GSourceFunc) 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); +} + +void closesocket( int fd ) +{ +	close( fd ); +} diff --git a/lib/events_libevent.c b/lib/events_libevent.c new file mode 100644 index 00000000..43d770ea --- /dev/null +++ b/lib/events_libevent.c @@ -0,0 +1,294 @@ +  /********************************************************************\ +  * 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 <sys/time.h> +#include <event.h> +#include "proxy.h" + +static void b_main_restart(); +static guint id_next = 1; /* Next ID to be allocated to an event handler. */ +static guint id_cur = 0; /* Event ID that we're currently handling. */ +static guint id_dead; /* Set to 1 if b_event_remove removes id_cur. */ +static GHashTable *id_hash; +static int quitting = 0; /* Prepare to quit, stop handling events. */ + +/* 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 event_base *leh; +struct event_base *old_leh; + +struct b_event_data +{ +	guint id; +	struct event evinfo; +	gint timeout; +	b_event_handler function; +	void *data; +	guint flags; +}; + +void b_main_init() +{ +	if( leh != NULL ) +	{ +		/* Clean up the hash tables? */ +		 +		b_main_restart(); +		old_leh = leh; +	} +	 +	leh = event_init(); +	 +	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() +{ +	/* This while loop is necessary to exit the event loop and start a +	   different one (necessary for ForkDaemon mode). */ +	while( event_base_dispatch( leh ) == 0 && !quitting ) +	{ +		if( old_leh != NULL ) +		{ +			/* For some reason this just isn't allowed... +			   Possibly a bug in older versions, will see later. +			event_base_free( old_leh ); */ +			old_leh = NULL; +		} +		 +		event_debug( "New event loop.\n" ); +	} +} + +static void b_main_restart() +{ +	struct timeval tv; +	 +	memset( &tv, 0, sizeof( struct timeval ) ); +	event_base_loopexit( leh, &tv ); +	 +	event_debug( "b_main_restart()\n" ); +} + +void b_main_quit() +{ +	/* Tell b_main_run() that it shouldn't restart the loop. Also, +	   libevent sometimes generates events before really quitting, +	   we want to stop them. */ +	quitting = 1; +	 +	b_main_restart(); +} + +static void b_event_passthrough( int fd, short event, void *data ) +{ +	struct b_event_data *b_ev = data; +	b_input_condition cond = 0; +	gboolean st; +	 +	if( fd >= 0 ) +	{ +		if( event & EV_READ ) +			cond |= B_EV_IO_READ; +		if( event & EV_WRITE ) +			cond |= B_EV_IO_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_cur = b_ev->id; +	id_dead = 0; +	 +	if( quitting ) +	{ +		b_event_remove( id_cur ); +		return; +	} +	 +	st = b_ev->function( b_ev->data, fd, cond ); +	if( id_dead ) +	{ +		/* This event was killed already, don't touch it! */ +		return; +	} +	else if( !st && !( b_ev->flags & B_EV_FLAG_FORCE_REPEAT ) ) +	{ +		event_debug( "Handler returned FALSE: " ); +		b_event_remove( id_cur ); +	} +	else if( fd == -1 ) +	{ +		/* fd == -1 means it was a timer. These can't be auto-repeated +		   so it has to be recreated every time. */ +		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 & B_EV_IO_READ  && ( b_ev = g_hash_table_lookup( read_hash,  &fd ) ) ) || +	    ( condition & B_EV_IO_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 & B_EV_IO_READ ) +			out_cond |= EV_READ; +		if( condition & B_EV_IO_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 ); +	} +	 +	b_ev->flags = condition; +	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 ) +	{ +		if( id == id_cur ) +			id_dead = TRUE; +		 +		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/ftutil.c b/lib/ftutil.c new file mode 100644 index 00000000..71c09b50 --- /dev/null +++ b/lib/ftutil.c @@ -0,0 +1,144 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Utility functions for file transfer                                      * +*                                                                           * +*  Copyright 2008 Uli Meis <a.sporto+bee@gmail.com>                         * +*                                                                           * +*  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.              * +*                                                                           * +\***************************************************************************/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include <poll.h> +#include <netinet/tcp.h> +#include "lib/ftutil.h" + +#define ASSERTSOCKOP(op, msg) \ +	if( (op) == -1 ) {\ +		g_snprintf( errmsg, sizeof( errmsg ), msg ": %s", strerror( errno ) ); \ +		return -1; } + +/* + * Creates a listening socket and returns it in saddr_ptr. + */ +int ft_listen( struct sockaddr_storage *saddr_ptr, char *host, char *port, int copy_fd, int for_bitlbee_client, char **errptr ) +{ +	int fd, gret, saddrlen; +	struct addrinfo hints, *rp; +	socklen_t ssize = sizeof( struct sockaddr_storage ); +	struct sockaddr_storage saddrs, *saddr = &saddrs; +	static char errmsg[1024]; +	char *ftlisten = global.conf->ft_listen; + +	if( errptr ) +		*errptr = errmsg; + +	strcpy( port, "0" ); + +	/* Format is <IP-A>[:<Port-A>];<IP-B>[:<Port-B>] where +	 * A is for connections with the bitlbee client (DCC) +	 * and B is for connections with IM peers. +	 */ +	if( ftlisten ) +	{ +		char *scolon = strchr( ftlisten, ';' ); +		char *colon; + +		if( scolon ) +		{ +			if( for_bitlbee_client ) +			{ +				*scolon = '\0'; +				strncpy( host, ftlisten, HOST_NAME_MAX ); +				*scolon = ';'; +			} +			else +			{ +				strncpy( host, scolon + 1, HOST_NAME_MAX ); +			} +		} +		else +		{ +			strncpy( host, ftlisten, HOST_NAME_MAX ); +		} + +		if( ( colon = strchr( host, ':' ) ) ) +		{ +			*colon = '\0'; +			strncpy( port, colon + 1, 5 ); +		} +	} +	else if( copy_fd >= 0 && getsockname( copy_fd, (struct sockaddr*) &saddrs, &ssize ) == 0 && +	         ( saddrs.ss_family == AF_INET || saddrs.ss_family == AF_INET6 ) && +	         getnameinfo( (struct sockaddr*) &saddrs, ssize, host, HOST_NAME_MAX, +	                      NULL, 0, NI_NUMERICHOST ) == 0 ) +	{ +		/* We just took our local address on copy_fd, which is likely to be a +		   sensible address from which we can do a file transfer now - the +		   most sensible we can get easily. */ +	} +	else +	{ +		ASSERTSOCKOP( gethostname( host, HOST_NAME_MAX + 1 ), "gethostname()" ); +	} + +	memset( &hints, 0, sizeof( struct addrinfo ) ); +	hints.ai_socktype = SOCK_STREAM; +	hints.ai_flags = AI_NUMERICSERV; + +	if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) != 0 ) +	{ +		sprintf( errmsg, "getaddrinfo() failed: %s", gai_strerror( gret ) ); +		return -1; +	} + +	saddrlen = rp->ai_addrlen; + +	memcpy( saddr, rp->ai_addr, saddrlen ); + +	freeaddrinfo( rp ); + +	ASSERTSOCKOP( fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); +	ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, saddrlen ), "Binding socket" ); +	ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); + +	if ( !inet_ntop( saddr->ss_family, saddr->ss_family == AF_INET ? +			( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr : +			( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr, +			host, HOST_NAME_MAX ) ) +	{ +		strcpy( errmsg, "inet_ntop failed on listening socket" ); +		return -1; +	} + +	ssize = sizeof( struct sockaddr_storage ); +	ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); + +	if( saddr->ss_family == AF_INET ) +		g_snprintf( port, 6, "%d", ntohs( ( (struct sockaddr_in *) saddr )->sin_port ) ); +	else +		g_snprintf( port, 6, "%d", ntohs( ( (struct sockaddr_in6 *) saddr )->sin6_port ) ); + +	if( saddr_ptr ) +		memcpy( saddr_ptr, saddr, saddrlen ); + +	/* I hate static-length strings.. */ +	host[HOST_NAME_MAX] = '\0'; +	port[5] = '\0'; +	 +	return fd; +} diff --git a/lib/ftutil.h b/lib/ftutil.h new file mode 100644 index 00000000..09c1104e --- /dev/null +++ b/lib/ftutil.h @@ -0,0 +1,40 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Utility functions for file transfer                                      * +*                                                                           * +*  Copyright 2008 Uli Meis <a.sporto+bee@gmail.com>                         * +*                                                                           * +*  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.              * +*                                                                           * +\***************************************************************************/ + +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0x0400   /* Don't use name resolution.  */ +#endif + +/* Some ifdefs for ulibc and apparently also BSD (Thanks to Whoopie) */ +#ifndef HOST_NAME_MAX +#include <sys/param.h> +#ifdef MAXHOSTNAMELEN +#define HOST_NAME_MAX MAXHOSTNAMELEN +#else +#define HOST_NAME_MAX 255 +#endif +#endif + +/* This function should be used with care. host should be AT LEAST a +   char[HOST_NAME_MAX+1] and port AT LEAST a char[6]. */ +int ft_listen( struct sockaddr_storage *saddr_ptr, char *host, char *port, int copy_fd, int for_bitlbee_client, char **errptr ); diff --git a/lib/http_client.c b/lib/http_client.c new file mode 100644 index 00000000..69f06ec5 --- /dev/null +++ b/lib/http_client.c @@ -0,0 +1,456 @@ +  /********************************************************************\ +  * 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 ); +static void http_free( struct http_request *req ); + + +struct http_request *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 ) +	{ +		http_free( req ); +		return NULL; +	} +	 +	req->func = func; +	req->data = data; +	req->request = g_strdup( request ); +	req->request_length = strlen( request ); +	req->redir_ttl = 3; +	 +	return( req ); +} + +struct http_request *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 ) : B_EV_IO_WRITE, +	        	                 http_connected, req ); +	else +		req->inpa = b_input_add( source, B_EV_IO_READ, http_incoming_data, req ); +	 +	return FALSE; +	 +error: +	req->status_string = g_strdup( "Error while writing HTTP request" ); +	 +	req->func( req ); +	http_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 ) : B_EV_IO_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 ) && req->redir_ttl-- > 0 ) +	{ +		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 ); +	http_free( req ); +	return FALSE; +} + +static void http_free( struct http_request *req ) +{ +	g_free( req->request ); +	g_free( req->reply_headers ); +	g_free( req->status_string ); +	g_free( req ); +} + diff --git a/lib/http_client.h b/lib/http_client.h new file mode 100644 index 00000000..27c484ff --- /dev/null +++ b/lib/http_client.h @@ -0,0 +1,84 @@ +  /********************************************************************\ +  * 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 +*/ + +/* http_client allows you to talk (asynchronously, again) to HTTP servers. +   In the "background" it will send the whole query and wait for a complete +   response to come back. Right now it's only used by the MSN Passport +   authentication code, but it might be useful for other things too (for +   example the AIM usericon patch uses this so icons can be stored on +   webservers instead of the local filesystem). +    +   Didn't test this too much, but it seems to work well. Just don't look +   at the code that handles HTTP 30x redirects. ;-) The function is +   probably not very useful for downloading lots of data since it keeps  +   everything in a memory buffer until the download is completed (and +   can't pass any data or whatever before then). It's very useful for +   doing quick requests without blocking the whole program, though. */ + +#include <glib.h> +#include "ssl_client.h" + +struct http_request; + +/* Your callback function should look like this: */ +typedef void (*http_input_function)( struct http_request * ); + +/* This structure will be filled in by the http_dorequest* functions, and +   it will be passed to the callback function. Use the data field to add +   your own data. */ +struct http_request +{ +	char *request;          /* The request to send to the server. */ +	int request_length;     /* Its size. */ +	int status_code;        /* The numeric HTTP status code. (Or -1 +	                           if something really went wrong) */ +	char *status_string;    /* The error text. */ +	char *reply_headers; +	char *reply_body; +	int body_size;          /* The number of bytes in reply_body. */ +	int finished;           /* Set to non-0 if the request was completed +	                           successfully. */ +	int redir_ttl;          /* You can set it to 0 if you don't want +	                           http_client to follow them. */ +	 +	http_input_function func; +	gpointer data; +	 +	/* Please don't touch the things down here, you shouldn't need them. */ +	 +	void *ssl; +	int fd; +	 +	int inpa; +	int bytes_written; +	int bytes_read; +}; + +/* The _url variant is probably more useful than the raw version. The raw +   version is probably only useful if you want to do POST requests or if +   you want to add some extra headers. As you can see, HTTPS connections +   are also supported (using ssl_client). */ +struct http_request *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data ); +struct http_request *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..aa291bb2 --- /dev/null +++ b/lib/ini.c @@ -0,0 +1,145 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2008 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 ) +{ +	int fd; +	ini_t *ini = NULL; +	struct stat fi; +	 +	if( ( fd = open( file, O_RDONLY ) ) != -1 && +	    fstat( fd, &fi ) == 0 && +	    fi.st_size <= 16384 && +	    ( ini = g_malloc( sizeof( ini_t ) + fi.st_size + 1 ) ) && +	    read( fd, ini->file, fi.st_size ) == fi.st_size ) +	{ +		memset( ini, 0, sizeof( ini_t ) ); +		ini->size = fi.st_size; +		ini->file[ini->size] = 0; +		ini->cur = ini->file; +		ini->c_section = ""; +		 +		close( fd ); +		 +		return ini; +	} + +	if( fd >= 0 ) +		close( fd ); +	 +	ini_close( ini ); + +	return NULL; +} + +/* Strips leading and trailing whitespace and returns a pointer to the first +   non-ws character of the given string. */ +static char *ini_strip_whitespace( char *in ) +{ +	char *e; + +	while( isspace( *in ) ) +		in++; + +	e = in + strlen( in ) - 1; +	while( e > in && isspace( *e ) ) +		e--; +	e[1] = 0; +	 +	return in; +} + +int ini_read( ini_t *file ) +{ +	char *s; +	 +	while( file->cur && file->cur < file->file + file->size ) +	{ +		char *e, *next; +		 +		file->line++; + +		/* Find the end of line */ +		if( ( e = strchr( file->cur, '\n' ) ) != NULL ) +		{ +			*e = 0; +			next = e + 1; +		} +		else +		{ +			/* No more lines. */ +			e = file->cur + strlen( file->cur ); +			next = NULL; +		} +		 +		/* Comment? */ +		if( ( s = strchr( file->cur, '#' ) ) != NULL ) +			*s = 0; +		 +		file->cur = ini_strip_whitespace( file->cur ); +		 +		if( *file->cur == '[' ) +		{ +			file->cur++; +			if( ( s = strchr( file->cur, ']' ) ) != NULL ) +			{ +				*s = 0; +				file->c_section = file->cur; +			} +		} +		else if( ( s = strchr( file->cur, '=' ) ) != NULL ) +		{ +			*s = 0; +			file->key = ini_strip_whitespace( file->cur ); +			file->value = ini_strip_whitespace( s + 1 ); +			 +			if( ( s = strchr( file->key, '.' ) ) != NULL ) +			{ +				*s = 0; +				file->section = file->key; +				file->key = s + 1; +			} +			else +			{ +				file->section = file->c_section; +			} +			 +			file->cur = next; +			return 1; +		} +		/* else: noise/comment/etc, let's just ignore it. */ + +		file->cur = next; +	} +	 +	return 0; +} + +void ini_close( ini_t *file ) +{ +	g_free( file ); +} diff --git a/lib/ini.h b/lib/ini.h new file mode 100644 index 00000000..6ae0bde5 --- /dev/null +++ b/lib/ini.h @@ -0,0 +1,45 @@ +  /********************************************************************\ +  * 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 +{ +	int line; +	char *c_section; +	char *section; +	char *key; +	char *value; +	int size; +	char *cur, *tok; +	char file[]; +} 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..3c39eccd --- /dev/null +++ b/lib/md5.c @@ -0,0 +1,262 @@ +/* + * MD5 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/> + * + * Adapted to be API-compatible with the previous (GPL-incompatible) code. + */ + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest.  This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include <sys/types.h> +#include <string.h>		/* for memcpy() */ +#include "md5.h" + +static void md5_transform(uint32_t buf[4], uint32_t const in[16]); + +/* + * Wrapper function for all-in-one MD5 + * + * Bernardo Reino, aka Lepton. + * 20021120 + */ + +/* Turns out MD5 was designed for little-endian machines. If we're running +   on a big-endian machines, we have to swap some bytes. Since detecting +   endianness at compile time reliably seems pretty hard, let's do it at +   run-time. It's not like we're going to checksum megabytes of data... */ +static uint32_t cvt32(uint32_t val) +{ +	static int little_endian = -1; +	 +	if (little_endian == -1) +	{ +		little_endian = 1; +		little_endian = *((char*) &little_endian); +	} +	 +	if (little_endian) +		return val; +	else +		return (val >> 24) | +		       ((val >> 8) & 0xff00) | +		       ((val << 8) & 0xff0000) | +		       (val << 24); +} + +void md5_init(struct MD5Context *ctx) +{ +	ctx->buf[0] = 0x67452301; +	ctx->buf[1] = 0xefcdab89; +	ctx->buf[2] = 0x98badcfe; +	ctx->buf[3] = 0x10325476; + +	ctx->bits[0] = 0; +	ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void md5_append(struct MD5Context *ctx, const md5_byte_t *buf, +		unsigned int len) +{ +	uint32_t t; + +	/* Update bitcount */ + +	t = ctx->bits[0]; +	if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) +		ctx->bits[1]++;	/* Carry from low to high */ +	ctx->bits[1] += len >> 29; + +	t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */ + +	/* Handle any leading odd-sized chunks */ + +	if (t) { +		unsigned char *p = (unsigned char *) ctx->in + t; + +		t = 64 - t; +		if (len < t) { +			memcpy(p, buf, len); +			return; +		} +		memcpy(p, buf, t); +		md5_transform(ctx->buf, (uint32_t *) ctx->in); +		buf += t; +		len -= t; +	} +	/* Process data in 64-byte chunks */ + +	while (len >= 64) { +		memcpy(ctx->in, buf, 64); +		md5_transform(ctx->buf, (uint32_t *) ctx->in); +		buf += 64; +		len -= 64; +	} + +	/* Handle any remaining bytes of data. */ + +	memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern  + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void md5_finish(struct MD5Context *ctx, md5_byte_t digest[16]) +{ +	unsigned count; +	unsigned char *p; + +	/* Compute number of bytes mod 64 */ +	count = (ctx->bits[0] >> 3) & 0x3F; + +	/* Set the first char of padding to 0x80.  This is safe since there is +	   always at least one byte free */ +	p = ctx->in + count; +	*p++ = 0x80; + +	/* Bytes of padding needed to make 64 bytes */ +	count = 64 - 1 - count; + +	/* Pad out to 56 mod 64 */ +	if (count < 8) { +		/* Two lots of padding:  Pad the first block to 64 bytes */ +		memset(p, 0, count); +		md5_transform(ctx->buf, (uint32_t *) ctx->in); + +		/* Now fill the next block with 56 bytes */ +		memset(ctx->in, 0, 56); +	} else { +		/* Pad block to 56 bytes */ +		memset(p, 0, count - 8); +	} + +	/* Append length in bits and transform */ +	((uint32_t *) ctx->in)[14] = cvt32(ctx->bits[0]); +	((uint32_t *) ctx->in)[15] = cvt32(ctx->bits[1]); + +	md5_transform(ctx->buf, (uint32_t *) ctx->in); +	ctx->buf[0] = cvt32(ctx->buf[0]); +	ctx->buf[1] = cvt32(ctx->buf[1]); +	ctx->buf[2] = cvt32(ctx->buf[2]); +	ctx->buf[3] = cvt32(ctx->buf[3]); +	memcpy(digest, ctx->buf, 16); +	memset(ctx, 0, sizeof(ctx));	/* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ +	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data.  MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void md5_transform(uint32_t buf[4], uint32_t const in[16]) +{ +	register uint32_t a, b, c, d; + +	a = buf[0]; +	b = buf[1]; +	c = buf[2]; +	d = buf[3]; + +	MD5STEP(F1, a, b, c, d, cvt32(in[0]) + 0xd76aa478, 7); +	MD5STEP(F1, d, a, b, c, cvt32(in[1]) + 0xe8c7b756, 12); +	MD5STEP(F1, c, d, a, b, cvt32(in[2]) + 0x242070db, 17); +	MD5STEP(F1, b, c, d, a, cvt32(in[3]) + 0xc1bdceee, 22); +	MD5STEP(F1, a, b, c, d, cvt32(in[4]) + 0xf57c0faf, 7); +	MD5STEP(F1, d, a, b, c, cvt32(in[5]) + 0x4787c62a, 12); +	MD5STEP(F1, c, d, a, b, cvt32(in[6]) + 0xa8304613, 17); +	MD5STEP(F1, b, c, d, a, cvt32(in[7]) + 0xfd469501, 22); +	MD5STEP(F1, a, b, c, d, cvt32(in[8]) + 0x698098d8, 7); +	MD5STEP(F1, d, a, b, c, cvt32(in[9]) + 0x8b44f7af, 12); +	MD5STEP(F1, c, d, a, b, cvt32(in[10]) + 0xffff5bb1, 17); +	MD5STEP(F1, b, c, d, a, cvt32(in[11]) + 0x895cd7be, 22); +	MD5STEP(F1, a, b, c, d, cvt32(in[12]) + 0x6b901122, 7); +	MD5STEP(F1, d, a, b, c, cvt32(in[13]) + 0xfd987193, 12); +	MD5STEP(F1, c, d, a, b, cvt32(in[14]) + 0xa679438e, 17); +	MD5STEP(F1, b, c, d, a, cvt32(in[15]) + 0x49b40821, 22); + +	MD5STEP(F2, a, b, c, d, cvt32(in[1]) + 0xf61e2562, 5); +	MD5STEP(F2, d, a, b, c, cvt32(in[6]) + 0xc040b340, 9); +	MD5STEP(F2, c, d, a, b, cvt32(in[11]) + 0x265e5a51, 14); +	MD5STEP(F2, b, c, d, a, cvt32(in[0]) + 0xe9b6c7aa, 20); +	MD5STEP(F2, a, b, c, d, cvt32(in[5]) + 0xd62f105d, 5); +	MD5STEP(F2, d, a, b, c, cvt32(in[10]) + 0x02441453, 9); +	MD5STEP(F2, c, d, a, b, cvt32(in[15]) + 0xd8a1e681, 14); +	MD5STEP(F2, b, c, d, a, cvt32(in[4]) + 0xe7d3fbc8, 20); +	MD5STEP(F2, a, b, c, d, cvt32(in[9]) + 0x21e1cde6, 5); +	MD5STEP(F2, d, a, b, c, cvt32(in[14]) + 0xc33707d6, 9); +	MD5STEP(F2, c, d, a, b, cvt32(in[3]) + 0xf4d50d87, 14); +	MD5STEP(F2, b, c, d, a, cvt32(in[8]) + 0x455a14ed, 20); +	MD5STEP(F2, a, b, c, d, cvt32(in[13]) + 0xa9e3e905, 5); +	MD5STEP(F2, d, a, b, c, cvt32(in[2]) + 0xfcefa3f8, 9); +	MD5STEP(F2, c, d, a, b, cvt32(in[7]) + 0x676f02d9, 14); +	MD5STEP(F2, b, c, d, a, cvt32(in[12]) + 0x8d2a4c8a, 20); + +	MD5STEP(F3, a, b, c, d, cvt32(in[5]) + 0xfffa3942, 4); +	MD5STEP(F3, d, a, b, c, cvt32(in[8]) + 0x8771f681, 11); +	MD5STEP(F3, c, d, a, b, cvt32(in[11]) + 0x6d9d6122, 16); +	MD5STEP(F3, b, c, d, a, cvt32(in[14]) + 0xfde5380c, 23); +	MD5STEP(F3, a, b, c, d, cvt32(in[1]) + 0xa4beea44, 4); +	MD5STEP(F3, d, a, b, c, cvt32(in[4]) + 0x4bdecfa9, 11); +	MD5STEP(F3, c, d, a, b, cvt32(in[7]) + 0xf6bb4b60, 16); +	MD5STEP(F3, b, c, d, a, cvt32(in[10]) + 0xbebfbc70, 23); +	MD5STEP(F3, a, b, c, d, cvt32(in[13]) + 0x289b7ec6, 4); +	MD5STEP(F3, d, a, b, c, cvt32(in[0]) + 0xeaa127fa, 11); +	MD5STEP(F3, c, d, a, b, cvt32(in[3]) + 0xd4ef3085, 16); +	MD5STEP(F3, b, c, d, a, cvt32(in[6]) + 0x04881d05, 23); +	MD5STEP(F3, a, b, c, d, cvt32(in[9]) + 0xd9d4d039, 4); +	MD5STEP(F3, d, a, b, c, cvt32(in[12]) + 0xe6db99e5, 11); +	MD5STEP(F3, c, d, a, b, cvt32(in[15]) + 0x1fa27cf8, 16); +	MD5STEP(F3, b, c, d, a, cvt32(in[2]) + 0xc4ac5665, 23); + +	MD5STEP(F4, a, b, c, d, cvt32(in[0]) + 0xf4292244, 6); +	MD5STEP(F4, d, a, b, c, cvt32(in[7]) + 0x432aff97, 10); +	MD5STEP(F4, c, d, a, b, cvt32(in[14]) + 0xab9423a7, 15); +	MD5STEP(F4, b, c, d, a, cvt32(in[5]) + 0xfc93a039, 21); +	MD5STEP(F4, a, b, c, d, cvt32(in[12]) + 0x655b59c3, 6); +	MD5STEP(F4, d, a, b, c, cvt32(in[3]) + 0x8f0ccc92, 10); +	MD5STEP(F4, c, d, a, b, cvt32(in[10]) + 0xffeff47d, 15); +	MD5STEP(F4, b, c, d, a, cvt32(in[1]) + 0x85845dd1, 21); +	MD5STEP(F4, a, b, c, d, cvt32(in[8]) + 0x6fa87e4f, 6); +	MD5STEP(F4, d, a, b, c, cvt32(in[15]) + 0xfe2ce6e0, 10); +	MD5STEP(F4, c, d, a, b, cvt32(in[6]) + 0xa3014314, 15); +	MD5STEP(F4, b, c, d, a, cvt32(in[13]) + 0x4e0811a1, 21); +	MD5STEP(F4, a, b, c, d, cvt32(in[4]) + 0xf7537e82, 6); +	MD5STEP(F4, d, a, b, c, cvt32(in[11]) + 0xbd3af235, 10); +	MD5STEP(F4, c, d, a, b, cvt32(in[2]) + 0x2ad7d2bb, 15); +	MD5STEP(F4, b, c, d, a, cvt32(in[9]) + 0xeb86d391, 21); + +	buf[0] += a; +	buf[1] += b; +	buf[2] += c; +	buf[3] += d; +} diff --git a/lib/md5.h b/lib/md5.h new file mode 100644 index 00000000..3ba28586 --- /dev/null +++ b/lib/md5.h @@ -0,0 +1,46 @@ +/* + * MD5 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/> + * + * Adapted to be API-compatible with the previous (GPL-incompatible) code. + */ + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest.  This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#ifndef _MD5_H +#define _MD5_H + +#include <sys/types.h> +#include <gmodule.h> +#if(__sun) +#include <inttypes.h> +#else +#include <stdint.h> +#endif + +typedef uint8_t md5_byte_t; +typedef struct MD5Context { +	uint32_t buf[4]; +	uint32_t bits[2]; +	unsigned char in[64]; +} md5_state_t; + +G_MODULE_EXPORT void md5_init(struct MD5Context *context); +G_MODULE_EXPORT void md5_append(struct MD5Context *context, const md5_byte_t *buf, unsigned int len); +G_MODULE_EXPORT void md5_finish(struct MD5Context *context, md5_byte_t digest[16]); + +#endif diff --git a/lib/misc.c b/lib/misc.c new file mode 100644 index 00000000..05192d9c --- /dev/null +++ b/lib/misc.c @@ -0,0 +1,730 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2006 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-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 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 "base64.h" +#include "md5.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <glib.h> +#include <time.h> + +#ifdef HAVE_RESOLV_A +#include <arpa/nameser.h> +#include <resolv.h> +#endif + +#include "md5.h" +#include "ssl_client.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); +} + +time_t get_time(int year, int month, int day, int hour, int min, int sec) +{ +	struct tm tm; + +	memset(&tm, 0, sizeof(struct 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); +} + +time_t mktime_utc( struct tm *tp ) +{ +	struct tm utc; +	time_t res, tres; +	 +	tp->tm_isdst = -1; +	res = mktime( tp ); +	/* Problem is, mktime() just gave us the GMT timestamp for the +	   given local time... While the given time WAS NOT local. So +	   we should fix this now. +	    +	   Now I could choose between messing with environment variables +	   (kludgy) or using timegm() (not portable)... Or doing the +	   following, which I actually prefer... +	    +	   tzset() may also work but in other places I actually want to +	   use local time. +	    +	   FFFFFFFFFFFFFFFFFFFFFUUUUUUUUUUUUUUUUUUUU!! */ +	gmtime_r( &res, &utc ); +	utc.tm_isdst = -1; +	if( utc.tm_hour == tp->tm_hour && utc.tm_min == tp->tm_min ) +		/* Sweet! We're in UTC right now... */ +		return res; +	 +	tres = mktime( &utc ); +	res += res - tres; +	 +	/* Yes, this is a hack. And it will go wrong around DST changes. +	   BUT this is more likely to be threadsafe than messing with +	   environment variables, and possibly more portable... */ +	 +	return res; +} + +typedef struct htmlentity +{ +	char code[7]; +	char is[3]; +} htmlentity_t; + +static const htmlentity_t ent[] = +{ +	{ "lt",     "<" }, +	{ "gt",     ">" }, +	{ "amp",    "&" }, +	{ "apos",   "'" }, +	{ "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[strlen(in)+1]; +	char *s = out, *cs; +	int i, matched; +	int taglen; +	 +	memset( out, 0, sizeof( out ) ); +	 +	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 ++; +			 +			taglen = in - cs - 1;   /* not <0 because the above loop runs at least once */ +			if( *in ) +			{ +				if( g_strncasecmp( cs+1, "b", taglen) == 0 ) +					*(s++) = '\x02'; +				else if( g_strncasecmp( cs+1, "/b", taglen) == 0 ) +					*(s++) = '\x02'; +				else if( g_strncasecmp( cs+1, "i", taglen) == 0 ) +					*(s++) = '\x1f'; +				else if( g_strncasecmp( cs+1, "/i", taglen) == 0 ) +					*(s++) = '\x1f'; +				else if( g_strncasecmp( cs+1, "br", taglen) == 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 ); +} + +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 ); +} + +/* 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[strlen(s)+1]; +	int i, j; +	 +	strcpy( t, s ); +	for( i = j = 0; t[i]; i ++, j ++ ) +	{ +		/* Warning: isalnum() is locale-aware, so don't use it here! */ +		if( ( t[i] >= 'A' && t[i] <= 'Z' ) || +		    ( t[i] >= 'a' && t[i] <= 'z' ) || +		    ( t[i] >= '0' && t[i] <= '9' ) || +		    strchr( "._-~", t[i] ) ) +		{ +			s[j] = t[i]; +		} +		else +		{ +			sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] ); +			j += 2; +		} +	} +	s[j] = 0; +} + +/* 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; +} + +/* 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 ); +} + +/* 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 ); +} + +/* A pretty reliable random number generator. Tries to use the /dev/random +   devices first, and falls back to the random number generator from libc +   when it fails. Opens randomizer devices with O_NONBLOCK to make sure a +   lack of entropy won't halt BitlBee. */ +void random_bytes( unsigned char *buf, int count ) +{ +#ifndef _WIN32 +	static int use_dev = -1; +	 +	/* Actually this probing code isn't really necessary, is it? */ +	if( use_dev == -1 ) +	{ +		if( access( "/dev/random", R_OK ) == 0 || access( "/dev/urandom", R_OK ) == 0 ) +			use_dev = 1; +		else +		{ +			use_dev = 0; +			srand( ( getpid() << 16 ) ^ time( NULL ) ); +		} +	} +	 +	if( use_dev ) +	{ +		int fd; +		 +		/* At least on Linux, /dev/random can block if there's not +		   enough entropy. We really don't want that, so if it can't +		   give anything, use /dev/urandom instead. */ +		if( ( fd = open( "/dev/random", O_RDONLY | O_NONBLOCK ) ) >= 0 ) +			if( read( fd, buf, count ) == count ) +			{ +				close( fd ); +				return; +			} +		close( fd ); +		 +		/* urandom isn't supposed to block at all, but just to be +		   sure. If it blocks, we'll disable use_dev and use the libc +		   randomizer instead. */ +		if( ( fd = open( "/dev/urandom", O_RDONLY | O_NONBLOCK ) ) >= 0 ) +			if( read( fd, buf, count ) == count ) +			{ +				close( fd ); +				return; +			} +		close( fd ); +		 +		/* If /dev/random blocks once, we'll still try to use it +		   again next time. If /dev/urandom also fails for some +		   reason, stick with libc during this session. */ +		 +		use_dev = 0; +		srand( ( getpid() << 16 ) ^ time( NULL ) ); +	} +	 +	if( !use_dev ) +#endif +	{ +		int i; +		 +		/* Possibly the LSB of rand() isn't very random on some +		   platforms. Seems okay on at least Linux and OSX though. */ +		for( i = 0; i < count; i ++ ) +			buf[i] = rand() & 0xff; +	} +} + +int is_bool( char *value ) +{ +	if( *value == 0 ) +		return 0; +	 +	if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) ) +		return 1; +	if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) ) +		return 1; +	 +	while( *value ) +		if( !isdigit( *value ) ) +			return 0; +		else +			value ++; +	 +	return 1; +} + +int bool2int( char *value ) +{ +	int i; +	 +	if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) ) +		return 1; +	if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) ) +		return 0; +	 +	if( sscanf( value, "%d", &i ) == 1 ) +		return i; +	 +	return 0; +} + +struct ns_srv_reply **srv_lookup( char *service, char *protocol, char *domain ) +{	 +	struct ns_srv_reply **replies = NULL; +#ifdef HAVE_RESOLV_A +	struct ns_srv_reply *reply = NULL; +	char name[1024]; +	unsigned char querybuf[1024]; +	const unsigned char *buf; +	ns_msg nsh; +	ns_rr rr; +	int i, n, len, size; +	 +	g_snprintf( name, sizeof( name ), "_%s._%s.%s", service, protocol, domain ); +	 +	if( ( size = res_query( name, ns_c_in, ns_t_srv, querybuf, sizeof( querybuf ) ) ) <= 0 ) +		return NULL; +	 +	if( ns_initparse( querybuf, size, &nsh ) != 0 ) +		return NULL; +	 +	n = 0; +	while( ns_parserr( &nsh, ns_s_an, n, &rr ) == 0 ) +	{ +		size = ns_rr_rdlen( rr ); +		buf = ns_rr_rdata( rr ); +		 +		len = 0; +		for( i = 6; i < size && buf[i]; i += buf[i] + 1 ) +			len += buf[i] + 1; +		 +		if( i > size ) +			break; +		 +		reply = g_malloc( sizeof( struct ns_srv_reply ) + len ); +		memcpy( reply->name, buf + 7, len ); +		 +		for( i = buf[6]; i < len && buf[7+i]; i += buf[7+i] + 1 ) +			reply->name[i] = '.'; +		 +		if( i > len ) +		{ +			g_free( reply ); +			break; +		} +		 +		reply->prio = ( buf[0] << 8 ) | buf[1]; +		reply->weight = ( buf[2] << 8 ) | buf[3]; +		reply->port = ( buf[4] << 8 ) | buf[5]; +		 +		n ++; +		replies = g_renew( struct ns_srv_reply *, replies, n + 1 ); +		replies[n-1] = reply; +	} +	if( replies ) +		replies[n] = NULL; +#endif +	 +	return replies; +} + +void srv_free( struct ns_srv_reply **srv ) +{ +	int i; +	 +	if( srv == NULL ) +		return; +	 +	for( i = 0; srv[i]; i ++ ) +		g_free( srv[i] ); +	g_free( srv ); +} + +/* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */ +char *word_wrap( const char *msg, int line_len ) +{ +	GString *ret = g_string_sized_new( strlen( msg ) + 16 ); +	 +	while( strlen( msg ) > line_len ) +	{ +		int i; +		 +		/* First try to find out if there's a newline already. Don't +		   want to add more splits than necessary. */ +		for( i = line_len; i > 0 && msg[i] != '\n'; i -- ); +		if( msg[i] == '\n' ) +		{ +			g_string_append_len( ret, msg, i + 1 ); +			msg += i + 1; +			continue; +		} +		 +		for( i = line_len; i > 0; i -- ) +		{ +			if( msg[i] == '-' ) +			{ +				g_string_append_len( ret, msg, i + 1 ); +				g_string_append_c( ret, '\n' ); +				msg += i + 1; +				break; +			} +			else if( msg[i] == ' ' ) +			{ +				g_string_append_len( ret, msg, i ); +				g_string_append_c( ret, '\n' ); +				msg += i + 1; +				break; +			} +		} +		if( i == 0 ) +		{ +			g_string_append_len( ret, msg, line_len ); +			g_string_append_c( ret, '\n' ); +			msg += line_len; +		} +	} +	g_string_append( ret, msg ); +	 +	return g_string_free( ret, FALSE ); +} + +gboolean ssl_sockerr_again( void *ssl ) +{ +	if( ssl ) +		return ssl_errno == SSL_AGAIN; +	else +		return sockerr_again(); +} + +/* Returns values: -1 == Failure (base64-decoded to something unexpected) +                    0 == Okay +                    1 == Password doesn't match the hash. */ +int md5_verify_password( char *password, char *hash ) +{ +	md5_byte_t *pass_dec = NULL; +	md5_byte_t pass_md5[16]; +	md5_state_t md5_state; +	int ret = -1, i; +	 +	if( base64_decode( hash, &pass_dec ) == 21 ) +	{ +		md5_init( &md5_state ); +		md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) ); +		md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */ +		md5_finish( &md5_state, pass_md5 ); +		 +		for( i = 0; i < 16; i ++ ) +		{ +			if( pass_dec[i] != pass_md5[i] ) +			{ +				ret = 1; +				break; +			} +		} +		 +		/* If we reached the end of the loop, it was a match! */ +		if( i == 16 ) +			ret = 0; +	} +	 +	g_free( pass_dec ); + +	return ret; +} + +/* Split commands (root-style, *not* IRC-style). Handles "quoting of" +   white\ space in 'various ways'. Returns a NULL-terminated static +   char** so watch out with nested use! Definitely not thread-safe. */ +char **split_command_parts( char *command ) +{ +	static char *cmd[IRC_MAX_ARGS+1]; +	char *s, q = 0; +	int k; +	 +	memset( cmd, 0, sizeof( cmd ) ); +	cmd[0] = command; +	k = 1; +	for( s = command; *s && k < IRC_MAX_ARGS; s ++ ) +		if( *s == ' ' && !q ) +		{ +			*s = 0; +			while( *++s == ' ' ); +			if( *s == '"' || *s == '\'' ) +			{ +				q = *s; +				s ++; +			} +			if( *s ) +			{ +				cmd[k++] = s; +				s --; +			} +			else +			{ +				break; +			} +		} +		else if( *s == '\\' && ( ( !q && s[1] ) || ( q && q == s[1] ) ) ) +		{ +			char *cpy; +			 +			for( cpy = s; *cpy; cpy ++ ) +				cpy[0] = cpy[1]; +		} +		else if( *s == q ) +		{ +			q = *s = 0; +		} +	 +	/* Full zero-padding for easier argc checking. */ +	while( k <= IRC_MAX_ARGS ) +		cmd[k++] = NULL; +	 +	return cmd; +} diff --git a/lib/misc.h b/lib/misc.h new file mode 100644 index 00000000..83ba9e67 --- /dev/null +++ b/lib/misc.h @@ -0,0 +1,74 @@ +  /********************************************************************\ +  * 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 _MISC_H +#define _MISC_H + +#include <gmodule.h> +#include <time.h> + +struct ns_srv_reply +{ +	int prio; +	int weight; +	int port; +	char name[]; +}; + +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 time_t get_time( int year, int month, int day, int hour, int min, int sec ); +G_MODULE_EXPORT time_t mktime_utc( struct tm *tp ); +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 ); + +G_MODULE_EXPORT void random_bytes( unsigned char *buf, int count ); + +G_MODULE_EXPORT int is_bool( char *value ); +G_MODULE_EXPORT int bool2int( char *value ); + +G_MODULE_EXPORT struct ns_srv_reply **srv_lookup( char *service, char *protocol, char *domain ); +G_MODULE_EXPORT void srv_free( struct ns_srv_reply **srv ); + +G_MODULE_EXPORT char *word_wrap( const char *msg, int line_len ); + +G_MODULE_EXPORT gboolean ssl_sockerr_again( void *ssl ); + +G_MODULE_EXPORT int md5_verify_password( char *password, char *hash ); + +G_MODULE_EXPORT char **split_command_parts( char *command ); + +#endif diff --git a/lib/oauth.c b/lib/oauth.c new file mode 100644 index 00000000..9c67363a --- /dev/null +++ b/lib/oauth.c @@ -0,0 +1,456 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple OAuth client (consumer) implementation.                           * +*                                                                           * +*  Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*  This library is free software; you can redistribute it and/or            * +*  modify it under the terms of the GNU Lesser General Public               * +*  License as published by the Free Software Foundation, version            * +*  2.1.                                                                     * +*                                                                           * +*  This library 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        * +*  Lesser General Public License for more details.                          * +*                                                                           * +*  You should have received a copy of the GNU Lesser General Public License * +*  along with this library; if not, write to the Free Software Foundation,  * +*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           * +*                                                                           * +\***************************************************************************/ + +#include <glib.h> +#include <gmodule.h> +#include <stdlib.h> +#include <string.h> +#include "http_client.h" +#include "base64.h" +#include "misc.h" +#include "sha1.h" +#include "url.h" +#include "oauth.h" + +#define HMAC_BLOCK_SIZE 64 + +static char *oauth_sign( const char *method, const char *url, +                         const char *params, struct oauth_info *oi ) +{ +	sha1_state_t sha1; +	uint8_t hash[sha1_hash_size]; +	uint8_t key[HMAC_BLOCK_SIZE+1]; +	char *s; +	int i; +	 +	/* Create K. If our current key is >64 chars we have to hash it, +	   otherwise just pad. */ +	memset( key, 0, HMAC_BLOCK_SIZE ); +	i = strlen( oi->sp->consumer_secret ) + 1 + ( oi->token_secret ? strlen( oi->token_secret ) : 0 ); +	if( i > HMAC_BLOCK_SIZE ) +	{ +		sha1_init( &sha1 ); +		sha1_append( &sha1, (uint8_t*) oi->sp->consumer_secret, strlen( oi->sp->consumer_secret ) ); +		sha1_append( &sha1, (uint8_t*) "&", 1 ); +		if( oi->token_secret ) +			sha1_append( &sha1, (uint8_t*) oi->token_secret, strlen( oi->token_secret ) ); +		sha1_finish( &sha1, key ); +	} +	else +	{ +		g_snprintf( (gchar*) key, HMAC_BLOCK_SIZE + 1, "%s&%s", +		            oi->sp->consumer_secret, oi->token_secret ? oi->token_secret : "" ); +	} +	 +	/* Inner part: H(K XOR 0x36, text) */ +	sha1_init( &sha1 ); +	 +	for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) +		key[i] ^= 0x36; +	sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); +	 +	/* OAuth: text = method&url¶ms, all http_encoded. */ +	sha1_append( &sha1, (const uint8_t*) method, strlen( method ) ); +	sha1_append( &sha1, (const uint8_t*) "&", 1 ); +	 +	s = g_new0( char, strlen( url ) * 3 + 1 ); +	strcpy( s, url ); +	http_encode( s ); +	sha1_append( &sha1, (const uint8_t*) s, strlen( s ) ); +	sha1_append( &sha1, (const uint8_t*) "&", 1 ); +	g_free( s ); +	 +	s = g_new0( char, strlen( params ) * 3 + 1 ); +	strcpy( s, params ); +	http_encode( s ); +	sha1_append( &sha1, (const uint8_t*) s, strlen( s ) ); +	g_free( s ); +	 +	sha1_finish( &sha1, hash ); +	 +	/* Final result: H(K XOR 0x5C, inner stuff) */ +	sha1_init( &sha1 ); +	for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) +		key[i] ^= 0x36 ^ 0x5c; +	sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); +	sha1_append( &sha1, hash, sha1_hash_size ); +	sha1_finish( &sha1, hash ); +	 +	/* base64_encode + HTTP escape it (both consumers  +	   need it that away) and we're done. */ +	s = base64_encode( hash, sha1_hash_size ); +	s = g_realloc( s, strlen( s ) * 3 + 1 ); +	http_encode( s ); +	 +	return s; +} + +static char *oauth_nonce() +{ +	unsigned char bytes[21]; +	char *ret = g_new0( char, sizeof( bytes) / 3 * 4 + 1 ); +	 +	random_bytes( bytes, sizeof( bytes ) ); +	base64_encode_real( bytes, sizeof( bytes), (unsigned char*) ret, "0123456789" +	                    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0A" ); +	 +	return ret; +} + +void oauth_params_add( GSList **params, const char *key, const char *value ) +{ +	char *item; +	 +	item = g_strdup_printf( "%s=%s", key, value ); +	*params = g_slist_insert_sorted( *params, item, (GCompareFunc) strcmp ); +} + +void oauth_params_del( GSList **params, const char *key ) +{ +	int key_len = strlen( key ); +	GSList *l, *n; +	 +	for( l = *params; l; l = n ) +	{ +		n = l->next; +		 +		if( strncmp( (char*) l->data, key, key_len ) == 0 && +		    ((char*)l->data)[key_len] == '=' ) +		{ +			g_free( l->data ); +			*params = g_slist_remove( *params, l->data ); +		} +	} +} + +void oauth_params_set( GSList **params, const char *key, const char *value ) +{ +	oauth_params_del( params, key ); +	oauth_params_add( params, key, value ); +} + +const char *oauth_params_get( GSList **params, const char *key ) +{ +	int key_len = strlen( key ); +	GSList *l; +	 +	for( l = *params; l; l = l->next ) +	{ +		if( strncmp( (char*) l->data, key, key_len ) == 0 && +		    ((char*)l->data)[key_len] == '=' ) +			return (const char*) l->data + key_len + 1; +	} +	 +	return NULL; +} + +static void oauth_params_parse( GSList **params, char *in ) +{ +	char *amp, *eq, *s; +	 +	while( in && *in ) +	{ +		eq = strchr( in, '=' ); +		if( !eq ) +			break; +		 +		*eq = '\0'; +		if( ( amp = strchr( eq + 1, '&' ) ) ) +			*amp = '\0'; +		 +		s = g_strdup( eq + 1 ); +		http_decode( s ); +		oauth_params_add( params, in, s ); +		g_free( s ); +		 +		*eq = '='; +		if( amp == NULL ) +			break; +		 +		*amp = '&'; +		in = amp + 1; +	} +} + +void oauth_params_free( GSList **params ) +{ +	while( params && *params ) +	{ +		g_free( (*params)->data ); +		*params = g_slist_remove( *params, (*params)->data ); +	} +} + +char *oauth_params_string( GSList *params ) +{ +	GSList *l; +	GString *str = g_string_new( "" ); +	 +	for( l = params; l; l = l->next ) +	{ +		char *s, *eq; +		 +		s = g_malloc( strlen( l->data ) * 3 + 1 ); +		strcpy( s, l->data ); +		if( ( eq = strchr( s, '=' ) ) ) +			http_encode( eq + 1 ); +		g_string_append( str, s ); +		g_free( s ); +		 +		if( l->next ) +			g_string_append_c( str, '&' ); +	} +	 +	return g_string_free( str, FALSE ); +} + +void oauth_info_free( struct oauth_info *info ) +{ +	if( info ) +	{ +		g_free( info->auth_url ); +		g_free( info->request_token ); +		g_free( info->token ); +		g_free( info->token_secret ); +		oauth_params_free( &info->params ); +		g_free( info ); +	} +} + +static void oauth_add_default_params( GSList **params, const struct oauth_service *sp ) +{ +	char *s; +	 +	oauth_params_set( params, "oauth_consumer_key", sp->consumer_key ); +	oauth_params_set( params, "oauth_signature_method", "HMAC-SHA1" ); +	 +	s = g_strdup_printf( "%d", (int) time( NULL ) ); +	oauth_params_set( params, "oauth_timestamp", s ); +	g_free( s ); +	 +	s = oauth_nonce(); +	oauth_params_set( params, "oauth_nonce", s ); +	g_free( s ); +	 +	oauth_params_set( params, "oauth_version", "1.0" ); +} + +static void *oauth_post_request( const char *url, GSList **params_, http_input_function func, struct oauth_info *oi ) +{ +	GSList *params = NULL; +	char *s, *params_s, *post; +	void *req; +	url_t url_p; +	 +	if( !url_set( &url_p, url ) ) +	{ +		oauth_params_free( params_ ); +		return NULL; +	} +	 +	if( params_ ) +		params = *params_; +	 +	oauth_add_default_params( ¶ms, oi->sp ); +	 +	params_s = oauth_params_string( params ); +	oauth_params_free( ¶ms ); +	 +	s = oauth_sign( "POST", url, params_s, oi ); +	post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s ); +	g_free( params_s ); +	g_free( s ); +	 +	s = g_strdup_printf( "POST %s HTTP/1.0\r\n" +	                     "Host: %s\r\n" +	                     "Content-Type: application/x-www-form-urlencoded\r\n" +	                     "Content-Length: %zd\r\n" +	                     "\r\n" +	                     "%s", url_p.file, url_p.host, strlen( post ), post ); +	g_free( post ); +	 +	req = http_dorequest( url_p.host, url_p.port, url_p.proto == PROTO_HTTPS, +	                      s, func, oi ); +	g_free( s ); +	 +	return req; +} + +static void oauth_request_token_done( struct http_request *req ); + +struct oauth_info *oauth_request_token( const struct oauth_service *sp, oauth_cb func, void *data ) +{ +	struct oauth_info *st = g_new0( struct oauth_info, 1 ); +	GSList *params = NULL; +	 +	st->func = func; +	st->data = data; +	st->sp = sp; +	 +	oauth_params_add( ¶ms, "oauth_callback", "oob" ); +	 +	if( !oauth_post_request( sp->url_request_token, ¶ms, oauth_request_token_done, st ) ) +	{ +		oauth_info_free( st ); +		return NULL; +	} +	 +	return st; +} + +static void oauth_request_token_done( struct http_request *req ) +{ +	struct oauth_info *st = req->data; +	 +	st->http = req; +	 +	if( req->status_code == 200 ) +	{ +		GSList *params = NULL; +		 +		st->auth_url = g_strdup_printf( "%s?%s", st->sp->url_authorize, req->reply_body ); +		oauth_params_parse( ¶ms, req->reply_body ); +		st->request_token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); +		oauth_params_free( ¶ms ); +	} +	 +	st->stage = OAUTH_REQUEST_TOKEN; +	st->func( st ); +} + +static void oauth_access_token_done( struct http_request *req ); + +gboolean oauth_access_token( const char *pin, struct oauth_info *st ) +{ +	GSList *params = NULL; +	 +	oauth_params_add( ¶ms, "oauth_token", st->request_token ); +	oauth_params_add( ¶ms, "oauth_verifier", pin ); +	 +	return oauth_post_request( st->sp->url_access_token, ¶ms, oauth_access_token_done, st ) != NULL; +} + +static void oauth_access_token_done( struct http_request *req ) +{ +	struct oauth_info *st = req->data; +	 +	st->http = req; +	 +	if( req->status_code == 200 ) +	{ +		oauth_params_parse( &st->params, req->reply_body ); +		st->token = g_strdup( oauth_params_get( &st->params, "oauth_token" ) ); +		st->token_secret = g_strdup( oauth_params_get( &st->params, "oauth_token_secret" ) ); +	} +	 +	st->stage = OAUTH_ACCESS_TOKEN; +	if( st->func( st ) ) +	{ +		/* Don't need these anymore, but keep the rest. */ +		g_free( st->auth_url ); +		st->auth_url = NULL; +		g_free( st->request_token ); +		st->request_token = NULL; +		oauth_params_free( &st->params ); +	} +} + +char *oauth_http_header( struct oauth_info *oi, const char *method, const char *url, char *args ) +{ +	GSList *params = NULL, *l; +	char *sig = NULL, *params_s, *s; +	GString *ret = NULL; +	 +	oauth_params_add( ¶ms, "oauth_token", oi->token ); +	oauth_add_default_params( ¶ms, oi->sp ); +	 +	/* Start building the OAuth header. 'key="value", '... */ +	ret = g_string_new( "OAuth " ); +	for( l = params; l; l = l->next ) +	{ +		char *kv = l->data; +		char *eq = strchr( kv, '=' ); +		char esc[strlen(kv)*3+1]; +		 +		if( eq == NULL ) +			break; /* WTF */ +		 +		strcpy( esc, eq + 1 ); +		http_encode( esc ); +		 +		g_string_append_len( ret, kv, eq - kv + 1 ); +		g_string_append_c( ret, '"' ); +		g_string_append( ret, esc ); +		g_string_append( ret, "\", " ); +	} +	 +	/* Now, before generating the signature, add GET/POST arguments to params +	   since they should be included in the base signature string (but not in +	   the HTTP header). */ +	if( args ) +		oauth_params_parse( ¶ms, args ); +	if( ( s = strchr( url, '?' ) ) ) +	{ +		s = g_strdup( s + 1 ); +		oauth_params_parse( ¶ms, s ); +		g_free( s ); +	} +	 +	/* Append the signature and we're done! */ +	params_s = oauth_params_string( params ); +	sig = oauth_sign( method, url, params_s, oi ); +	g_string_append_printf( ret, "oauth_signature=\"%s\"", sig ); +	g_free( params_s ); +	 +	oauth_params_free( ¶ms ); +	g_free( sig ); +	 +	return ret ? g_string_free( ret, FALSE ) : NULL; +} + +char *oauth_to_string( struct oauth_info *oi ) +{ +	GSList *params = NULL; +	char *ret; +	 +	oauth_params_add( ¶ms, "oauth_token", oi->token ); +	oauth_params_add( ¶ms, "oauth_token_secret", oi->token_secret ); +	ret = oauth_params_string( params ); +	oauth_params_free( ¶ms ); +	 +	return ret; +} + +struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp ) +{ +	struct oauth_info *oi = g_new0( struct oauth_info, 1 ); +	GSList *params = NULL; +	 +	oauth_params_parse( ¶ms, in ); +	oi->token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); +	oi->token_secret = g_strdup( oauth_params_get( ¶ms, "oauth_token_secret" ) ); +	oauth_params_free( ¶ms ); +	oi->sp = sp; +	 +	return oi; +} diff --git a/lib/oauth.h b/lib/oauth.h new file mode 100644 index 00000000..8270a545 --- /dev/null +++ b/lib/oauth.h @@ -0,0 +1,94 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple OAuth client (consumer) implementation.                           * +*                                                                           * +*  Copyright 2010 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.              * +*                                                                           * +\***************************************************************************/ + +/* http://oauth.net/core/1.0a/ */ + +struct oauth_info; + +/* Callback function called twice during the access token request process. +   Return FALSE if something broke and the process must be aborted. */ +typedef gboolean (*oauth_cb)( struct oauth_info * ); + +typedef enum +{ +	OAUTH_INIT, +	OAUTH_REQUEST_TOKEN, +	OAUTH_ACCESS_TOKEN, +} oauth_stage_t; + +struct oauth_info +{ +	oauth_stage_t stage; +	const struct oauth_service *sp; +	 +	oauth_cb func; +	void *data; +	 +	struct http_request *http; +	 +	char *auth_url; +	char *request_token; +	 +	char *token; +	char *token_secret; +	GSList *params; +}; + +struct oauth_service +{ +	char *url_request_token; +	char *url_access_token; +	char *url_authorize; +	 +	char *consumer_key; +	char *consumer_secret; +}; + +/* http://oauth.net/core/1.0a/#auth_step1 (section 6.1)  +   Request an initial anonymous token which can be used to construct an +   authorization URL for the user. This is passed to the callback function +   in a struct oauth_info. */ +struct oauth_info *oauth_request_token( const struct oauth_service *sp, oauth_cb func, void *data ); + +/* http://oauth.net/core/1.0a/#auth_step3 (section 6.3) +   The user gets a PIN or so which we now exchange for the final access +   token. This is passed to the callback function in the same +   struct oauth_info. */ +gboolean oauth_access_token( const char *pin, struct oauth_info *st ); + +/* http://oauth.net/core/1.0a/#anchor12 (section 7) +   Generate an OAuth Authorization: HTTP header. access_token should be +   saved/fetched using the functions above. args can be a string with +   whatever's going to be in the POST body of the request. GET args will +   automatically be grabbed from url. */ +char *oauth_http_header( struct oauth_info *oi, const char *method, const char *url, char *args ); + +/* Shouldn't normally be required unless the process is aborted by the user. */ +void oauth_info_free( struct oauth_info *info ); + +/* Convert to and back from strings, for easier saving. */ +char *oauth_to_string( struct oauth_info *oi ); +struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp ); + +/* For reading misc. data. */ +const char *oauth_params_get( GSList **params, const char *key ); diff --git a/lib/proxy.c b/lib/proxy.c new file mode 100644 index 00000000..b79afea4 --- /dev/null +++ b/lib/proxy.c @@ -0,0 +1,586 @@ +/* + * 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" +#include "base64.h" + +char proxyhost[128] = ""; +int proxyport = 0; +int proxytype = PROXY_NONE; +char proxyuser[128] = ""; +char proxypass[128] = ""; + +/* Some systems don't know this one. It's not essential, so set it to 0 then. */ +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0 +#endif +#ifndef AI_ADDRCONFIG +#define AI_ADDRCONFIG 0 +#endif + +struct PHB { +	b_event_handler func, proxy_func; +	gpointer data, proxy_data; +	char *host; +	int port; +	int fd; +	gint inpa; +	struct addrinfo *gai, *gai_cur; +}; + +static int proxy_connect_none(const char *host, unsigned short port_, struct PHB *phb); + +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 || error) { +		if ((phb->gai_cur = phb->gai_cur->ai_next)) { +			int new_fd; +			b_event_remove(phb->inpa); +			if ((new_fd = proxy_connect_none(NULL, 0, phb))) { +				b_event_remove(phb->inpa); +				closesocket(source); +				dup2(new_fd, source); +				phb->inpa = b_input_add(source, B_EV_IO_WRITE, gaim_io_connected, phb); +				return FALSE; +			} +		} +		freeaddrinfo(phb->gai); +		closesocket(source); +		b_event_remove(phb->inpa); +		if( phb->proxy_func ) +			phb->proxy_func(phb->proxy_data, -1, B_EV_IO_READ); +		else { +			phb->func(phb->data, -1, B_EV_IO_READ); +			g_free(phb); +		} +		return FALSE; +	} +#endif +	freeaddrinfo(phb->gai); +	sock_make_blocking(source); +	b_event_remove(phb->inpa); +	if( phb->proxy_func ) +		phb->proxy_func(phb->proxy_data, source, B_EV_IO_READ); +	else { +		phb->func(phb->data, source, B_EV_IO_READ); +		g_free(phb); +	} +	 +	return FALSE; +} + +static int proxy_connect_none(const char *host, unsigned short port_, struct PHB *phb) +{ +	struct sockaddr_in me; +	int fd = -1; +	 +	if (phb->gai_cur == NULL) +	{ +		int ret; +		char port[6]; +		struct addrinfo hints; +	 +		g_snprintf(port, sizeof(port), "%d", port_); +	 +		memset(&hints, 0, sizeof(struct addrinfo)); +		hints.ai_family = AF_UNSPEC; +		hints.ai_socktype = SOCK_STREAM; +		hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; +	 +		if (!(ret = getaddrinfo(host, port, &hints, &phb->gai))) +			phb->gai_cur = phb->gai; +		else +			event_debug("gai(): %s\n", gai_strerror(ret)); +	} +	 +	for (; phb->gai_cur; phb->gai_cur = phb->gai_cur->ai_next) +	{ +		if ((fd = socket(phb->gai_cur->ai_family, phb->gai_cur->ai_socktype, phb->gai_cur->ai_protocol)) < 0) { +			event_debug( "socket failed: %d\n", errno); +			continue; +		} + +		sock_make_nonblocking(fd); + +		if (global.conf->iface_out) +		{ +			me.sin_family = AF_INET; +			me.sin_port = 0; +			me.sin_addr.s_addr = inet_addr( global.conf->iface_out ); +				 +			if (bind(fd, (struct sockaddr *) &me, sizeof(me)) != 0) +				event_debug("bind( %d, \"%s\" ) failure\n", fd, global.conf->iface_out); +		} + +		event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd); +	 +		if (connect(fd, phb->gai_cur->ai_addr, phb->gai_cur->ai_addrlen) < 0 && !sockerr_again()) { +			event_debug( "connect failed: %s\n", strerror(errno)); +			closesocket(fd); +			fd = -1; +			continue; +		} else { +			phb->inpa = b_input_add(fd, B_EV_IO_WRITE, gaim_io_connected, phb); +			phb->fd = fd; +			 +			break; +		} +	} +	 +	if(fd < 0 && host) +		g_free(phb); + +	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, B_EV_IO_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	close(source); +	phb->func(phb->data, -1, B_EV_IO_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, B_EV_IO_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, B_EV_IO_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	if (strlen(proxyuser) > 0) { +		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, B_EV_IO_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, B_EV_IO_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	phb->inpa = b_input_add(source, B_EV_IO_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, B_EV_IO_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	close(source); +	phb->func(phb->data, -1, B_EV_IO_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, B_EV_IO_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, B_EV_IO_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, B_EV_IO_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	phb->inpa = b_input_add(source, B_EV_IO_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, B_EV_IO_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} +	if ((buf[0] != 0x05) || (buf[1] != 0x00)) { +		close(source); +		phb->func(phb->data, -1, B_EV_IO_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	phb->func(phb->data, source, B_EV_IO_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, B_EV_IO_READ); +		g_free(phb->host); +		g_free(phb); +		return; +	} + +	phb->inpa = b_input_add(source, B_EV_IO_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, B_EV_IO_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	if ((buf[0] != 0x01) || (buf[1] != 0x00)) { +		close(source); +		phb->func(phb->data, -1, B_EV_IO_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, B_EV_IO_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	if ((buf[0] != 0x05) || (buf[1] == 0xff)) { +		close(source); +		phb->func(phb->data, -1, B_EV_IO_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, B_EV_IO_READ); +			g_free(phb->host); +			g_free(phb); +			return FALSE; +		} + +		phb->inpa = b_input_add(source, B_EV_IO_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, B_EV_IO_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, B_EV_IO_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	phb->inpa = b_input_add(source, B_EV_IO_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 <= 0 || !func || strlen(host) > 128) { +		return -1; +	} +	 +	phb = g_new0(struct PHB, 1); +	phb->func = func; +	phb->data = data; +	 +	if (proxytype == PROXY_NONE || !proxyhost[0] || proxyport <= 0) +		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); +	 +	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/sha1.c b/lib/sha1.c new file mode 100644 index 00000000..7ee90640 --- /dev/null +++ b/lib/sha1.c @@ -0,0 +1,422 @@ +/* + * SHA1 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/> + * + * Adapted to be API-compatible with the previous (GPL-incompatible) code. + */ + +/* + *  sha1.c + * + *  Description: + *      This file implements the Secure Hashing Algorithm 1 as + *      defined in FIPS PUB 180-1 published April 17, 1995. + * + *      The SHA-1, produces a 160-bit message digest for a given + *      data stream.  It should take about 2**n steps to find a + *      message with the same digest as a given message and + *      2**(n/2) to find any two messages with the same digest, + *      when n is the digest size in bits.  Therefore, this + *      algorithm can serve as a means of providing a + *      "fingerprint" for a message. + * + *  Portability Issues: + *      SHA-1 is defined in terms of 32-bit "words".  This code + *      uses <stdint.h> (included via "sha1.h" to define 32 and 8 + *      bit unsigned integer types.  If your C compiler does not + *      support 32 bit unsigned integers, this code is not + *      appropriate. + * + *  Caveats: + *      SHA-1 is designed to work with messages less than 2^64 bits + *      long.  Although SHA-1 allows a message digest to be generated + *      for messages of any number of bits less than 2^64, this + *      implementation only works with messages with a length that is + *      a multiple of the size of an 8-bit character. + * + */ + +#include <string.h> +#include "sha1.h" + +/* + *  Define the SHA1 circular left shift macro + */ +#define SHA1CircularShift(bits,word) \ +       (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* Local Function Prototyptes */ +static void sha1_pad(sha1_state_t *); +static void sha1_process_block(sha1_state_t *); + +/* + *  sha1_init + * + *  Description: + *      This function will initialize the sha1_state_t in preparation + *      for computing a new SHA1 message digest. + * + *  Parameters: + *      context: [in/out] + *          The context to reset. + * + *  Returns: + *      sha Error Code. + * + */ +int sha1_init(sha1_state_t * context) +{ +	context->Length_Low = 0; +	context->Length_High = 0; +	context->Message_Block_Index = 0; + +	context->Intermediate_Hash[0] = 0x67452301; +	context->Intermediate_Hash[1] = 0xEFCDAB89; +	context->Intermediate_Hash[2] = 0x98BADCFE; +	context->Intermediate_Hash[3] = 0x10325476; +	context->Intermediate_Hash[4] = 0xC3D2E1F0; + +	context->Computed = 0; +	context->Corrupted = 0; +	 +	return shaSuccess; +} + +/* + *  sha1_finish + * + *  Description: + *      This function will return the 160-bit message digest into the + *      Message_Digest array  provided by the caller. + *      NOTE: The first octet of hash is stored in the 0th element, + *            the last octet of hash in the 19th element. + * + *  Parameters: + *      context: [in/out] + *          The context to use to calculate the SHA-1 hash. + *      Message_Digest: [out] + *          Where the digest is returned. + * + *  Returns: + *      sha Error Code. + * + */ +int sha1_finish(sha1_state_t * context, uint8_t Message_Digest[sha1_hash_size]) +{ +	int i; + +	if (!context || !Message_Digest) { +		return shaNull; +	} + +	if (context->Corrupted) { +		return context->Corrupted; +	} + +	if (!context->Computed) { +		sha1_pad(context); +		for (i = 0; i < 64; ++i) { +			/* message may be sensitive, clear it out */ +			context->Message_Block[i] = 0; +		} +		context->Length_Low = 0;	/* and clear length */ +		context->Length_High = 0; +		context->Computed = 1; + +	} + +	for (i = 0; i < sha1_hash_size; ++i) { +		Message_Digest[i] = context->Intermediate_Hash[i >> 2] +		    >> 8 * (3 - (i & 0x03)); +	} + +	return shaSuccess; +} + +/* + *  sha1_append + * + *  Description: + *      This function accepts an array of octets as the next portion + *      of the message. + * + *  Parameters: + *      context: [in/out] + *          The SHA context to update + *      message_array: [in] + *          An array of characters representing the next portion of + *          the message. + *      length: [in] + *          The length of the message in message_array + * + *  Returns: + *      sha Error Code. + * + */ +int +sha1_append(sha1_state_t * context, +	  const uint8_t * message_array, unsigned length) +{ +	if (!length) { +		return shaSuccess; +	} + +	if (!context || !message_array) { +		return shaNull; +	} + +	if (context->Computed) { +		context->Corrupted = shaStateError; + +		return shaStateError; +	} + +	if (context->Corrupted) { +		return context->Corrupted; +	} +	while (length-- && !context->Corrupted) { +		context->Message_Block[context->Message_Block_Index++] = +		    (*message_array & 0xFF); + +		context->Length_Low += 8; +		if (context->Length_Low == 0) { +			context->Length_High++; +			if (context->Length_High == 0) { +				/* Message is too long */ +				context->Corrupted = 1; +			} +		} + +		if (context->Message_Block_Index == 64) { +			sha1_process_block(context); +		} + +		message_array++; +	} + +	return shaSuccess; +} + +/* + *  sha1_process_block + * + *  Description: + *      This function will process the next 512 bits of the message + *      stored in the Message_Block array. + * + *  Parameters: + *      None. + * + *  Returns: + *      Nothing. + * + *  Comments: + *      Many of the variable names in this code, especially the + *      single character names, were used because those were the + *      names used in the publication. + * + * + */ +static void sha1_process_block(sha1_state_t * context) +{ +	const uint32_t K[] = {	/* Constants defined in SHA-1   */ +		0x5A827999, +		0x6ED9EBA1, +		0x8F1BBCDC, +		0xCA62C1D6 +	}; +	int t;			/* Loop counter                */ +	uint32_t temp;		/* Temporary word value        */ +	uint32_t W[80];		/* Word sequence               */ +	uint32_t A, B, C, D, E;	/* Word buffers                */ + +	/* +	 *  Initialize the first 16 words in the array W +	 */ +	for (t = 0; t < 16; t++) { +		W[t] = context->Message_Block[t * 4] << 24; +		W[t] |= context->Message_Block[t * 4 + 1] << 16; +		W[t] |= context->Message_Block[t * 4 + 2] << 8; +		W[t] |= context->Message_Block[t * 4 + 3]; +	} + +	for (t = 16; t < 80; t++) { +		W[t] = +		    SHA1CircularShift(1, +				      W[t - 3] ^ W[t - 8] ^ W[t - +							      14] ^ W[t - +								      16]); +	} + +	A = context->Intermediate_Hash[0]; +	B = context->Intermediate_Hash[1]; +	C = context->Intermediate_Hash[2]; +	D = context->Intermediate_Hash[3]; +	E = context->Intermediate_Hash[4]; + +	for (t = 0; t < 20; t++) { +		temp = SHA1CircularShift(5, A) + +		    ((B & C) | ((~B) & D)) + E + W[t] + K[0]; +		E = D; +		D = C; +		C = SHA1CircularShift(30, B); + +		B = A; +		A = temp; +	} + +	for (t = 20; t < 40; t++) { +		temp = +		    SHA1CircularShift(5, +				      A) + (B ^ C ^ D) + E + W[t] + K[1]; +		E = D; +		D = C; +		C = SHA1CircularShift(30, B); +		B = A; +		A = temp; +	} + +	for (t = 40; t < 60; t++) { +		temp = SHA1CircularShift(5, A) + +		    ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; +		E = D; +		D = C; +		C = SHA1CircularShift(30, B); +		B = A; +		A = temp; +	} + +	for (t = 60; t < 80; t++) { +		temp = +		    SHA1CircularShift(5, +				      A) + (B ^ C ^ D) + E + W[t] + K[3]; +		E = D; +		D = C; +		C = SHA1CircularShift(30, B); +		B = A; +		A = temp; +	} + +	context->Intermediate_Hash[0] += A; +	context->Intermediate_Hash[1] += B; +	context->Intermediate_Hash[2] += C; +	context->Intermediate_Hash[3] += D; +	context->Intermediate_Hash[4] += E; + +	context->Message_Block_Index = 0; +} + +/* + *  sha1_pad + * + *  Description: + *      According to the standard, the message must be padded to an even + *      512 bits.  The first padding bit must be a '1'.  The last 64 + *      bits represent the length of the original message.  All bits in + *      between should be 0.  This function will pad the message + *      according to those rules by filling the Message_Block array + *      accordingly.  It will also call the ProcessMessageBlock function + *      provided appropriately.  When it returns, it can be assumed that + *      the message digest has been computed. + * + *  Parameters: + *      context: [in/out] + *          The context to pad + *      ProcessMessageBlock: [in] + *          The appropriate SHA*ProcessMessageBlock function + *  Returns: + *      Nothing. + * + */ + +static void sha1_pad(sha1_state_t * context) +{ +	/* +	 *  Check to see if the current message block is too small to hold +	 *  the initial padding bits and length.  If so, we will pad the +	 *  block, process it, and then continue padding into a second +	 *  block. +	 */ +	if (context->Message_Block_Index > 55) { +		context->Message_Block[context->Message_Block_Index++] = +		    0x80; +		while (context->Message_Block_Index < 64) { +			context->Message_Block[context-> +					       Message_Block_Index++] = 0; +		} + +		sha1_process_block(context); + +		while (context->Message_Block_Index < 56) { +			context->Message_Block[context-> +					       Message_Block_Index++] = 0; +		} +	} else { +		context->Message_Block[context->Message_Block_Index++] = +		    0x80; +		while (context->Message_Block_Index < 56) { + +			context->Message_Block[context-> +					       Message_Block_Index++] = 0; +		} +	} + +	/* +	 *  Store the message length as the last 8 octets +	 */ +	context->Message_Block[56] = context->Length_High >> 24; +	context->Message_Block[57] = context->Length_High >> 16; +	context->Message_Block[58] = context->Length_High >> 8; +	context->Message_Block[59] = context->Length_High; +	context->Message_Block[60] = context->Length_Low >> 24; +	context->Message_Block[61] = context->Length_Low >> 16; +	context->Message_Block[62] = context->Length_Low >> 8; +	context->Message_Block[63] = context->Length_Low; + +	sha1_process_block(context); +} + +#define HMAC_BLOCK_SIZE 64 + +/* BitlBee addition: */ +void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, uint8_t Message_Digest[sha1_hash_size]) +{ +	sha1_state_t sha1; +	uint8_t hash[sha1_hash_size]; +	uint8_t key[HMAC_BLOCK_SIZE+1]; +	int i; +	 +	if( key_len == 0 ) +		key_len = strlen( key_ ); +	if( payload_len == 0 ) +		payload_len = strlen( payload ); +	 +	/* Create K. If our current key is >64 chars we have to hash it, +	   otherwise just pad. */ +	memset( key, 0, HMAC_BLOCK_SIZE + 1 ); +	if( key_len > HMAC_BLOCK_SIZE ) +	{ +		sha1_init( &sha1 ); +		sha1_append( &sha1, (uint8_t*) key_, key_len ); +		sha1_finish( &sha1, key ); +	} +	else +	{ +		memcpy( key, key_, key_len ); +	} +	 +	/* Inner part: H(K XOR 0x36, text) */ +	sha1_init( &sha1 ); +	for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) +		key[i] ^= 0x36; +	sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); +	sha1_append( &sha1, (const uint8_t*) payload, payload_len ); +	sha1_finish( &sha1, hash ); +	 +	/* Final result: H(K XOR 0x5C, inner stuff) */ +	sha1_init( &sha1 ); +	for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) +		key[i] ^= 0x36 ^ 0x5c; +	sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); +	sha1_append( &sha1, hash, sha1_hash_size ); +	sha1_finish( &sha1, Message_Digest ); +} diff --git a/lib/sha1.h b/lib/sha1.h new file mode 100644 index 00000000..a87410eb --- /dev/null +++ b/lib/sha1.h @@ -0,0 +1,71 @@ +/* + * SHA1 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/> + * + * Adapted to be API-compatible with the previous (GPL-incompatible) code. + */ + +/* + *  sha1.h + * + *  Description: + *      This is the header file for code which implements the Secure + *      Hashing Algorithm 1 as defined in FIPS PUB 180-1 published + *      April 17, 1995. + * + *      Many of the variable names in this code, especially the + *      single character names, were used because those were the names + *      used in the publication. + * + *      Please read the file sha1.c for more information. + * + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +#if(__sun) +#include <inttypes.h> +#else +#include <stdint.h> +#endif +#include <gmodule.h> + +#ifndef _SHA_enum_ +#define _SHA_enum_ +enum { +	shaSuccess = 0, +	shaNull,		/* Null pointer parameter */ +	shaInputTooLong,	/* input data too long */ +	shaStateError		/* called Input after Result */ +}; +#endif +#define sha1_hash_size 20 + +/* + *  This structure will hold context information for the SHA-1 + *  hashing operation + */ +typedef struct SHA1Context { +	uint32_t Intermediate_Hash[sha1_hash_size/4];	/* Message Digest   */ + +	uint32_t Length_Low;            /* Message length in bits           */ +	uint32_t Length_High;           /* Message length in bits           */ + +	/* Index into message block array   */ +	int_least16_t Message_Block_Index; +	uint8_t Message_Block[64];	/* 512-bit message blocks           */ + +	int Computed;                   /* Is the digest computed?          */ +	int Corrupted;                  /* Is the message digest corrupted? */ +} sha1_state_t; + +/* + *  Function Prototypes + */ + +G_MODULE_EXPORT int sha1_init(sha1_state_t *); +G_MODULE_EXPORT int sha1_append(sha1_state_t *, const uint8_t *, unsigned int); +G_MODULE_EXPORT int sha1_finish(sha1_state_t *, uint8_t Message_Digest[sha1_hash_size]); +G_MODULE_EXPORT void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, uint8_t Message_Digest[sha1_hash_size]); + +#endif diff --git a/lib/ssl_bogus.c b/lib/ssl_bogus.c new file mode 100644 index 00000000..f4ce5d4d --- /dev/null +++ b/lib/ssl_bogus.c @@ -0,0 +1,71 @@ +  /********************************************************************\ +  * 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_init( void ) +{ +} + +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 ); +} + +void *ssl_starttls( int fd, ssl_input_function func, gpointer data )  +{ +	return NULL; +} + +b_input_condition ssl_getdirection( void *conn ) +{ +	return B_EV_IO_READ; +} + +int ssl_pending( void *conn ) +{ +	return 0; +} diff --git a/lib/ssl_client.h b/lib/ssl_client.h new file mode 100644 index 00000000..d0340840 --- /dev/null +++ b/lib/ssl_client.h @@ -0,0 +1,84 @@ +  /********************************************************************\ +  * 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 +*/ + +/* ssl_client makes it easier to open SSL connections to servers. (It +   doesn't offer SSL server functionality yet, but it could be useful +   to add it later.) Different ssl_client modules are available, and +   ssl_client tries to make them all behave the same. It's very simple +   and basic, it just imitates the proxy_connect() function from the +   Gaim libs and passes the socket to the program once the handshake +   is completed. */ + +#include <glib.h> +#include "proxy.h" + +/* Some generic error codes. Especially SSL_AGAIN is important if you +   want to do asynchronous I/O. */ +#define SSL_OK            0 +#define SSL_NOHANDSHAKE   1 +#define SSL_AGAIN         2 + +extern int ssl_errno; + +/* This is what your callback function should look like. */ +typedef gboolean (*ssl_input_function)(gpointer, void*, b_input_condition); + + +/* Perform any global initialization the SSL library might need. */ +G_MODULE_EXPORT void ssl_init( void ); + +/* Connect to host:port, call the given function when the connection is +   ready to be used for SSL traffic. This is all done asynchronously, no +   blocking I/O! (Except for the DNS lookups, for now...) */ +G_MODULE_EXPORT void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ); + +/* Start an SSL session on an existing fd. Useful for STARTTLS functionality, +   for example in Jabber. */ +G_MODULE_EXPORT void *ssl_starttls( int fd, ssl_input_function func, gpointer data ); + +/* Obviously you need special read/write functions to read 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 ); + +/* See ssl_openssl.c for an explanation. */ +G_MODULE_EXPORT int ssl_pending( void *conn ); + +/* Abort the SSL connection and disconnect the socket. Do not use close() +   directly, both the SSL library and the peer will be unhappy! */ +G_MODULE_EXPORT void ssl_disconnect( void *conn_ ); + +/* Get the fd for this connection, you will usually need it for event +   handling. */ +G_MODULE_EXPORT int ssl_getfd( void *conn ); + +/* This function returns B_EV_IO_READ/WRITE. With SSL connections it's +   possible that something has to be read while actually were trying to +   write something (think about key exchange/refresh/etc). So when an +   SSL operation returned SSL_AGAIN, *always* use this function when +   adding an event handler to the queue. (And it should perform exactly +   the same action as the handler that just received the SSL_AGAIN.) */ +G_MODULE_EXPORT b_input_condition ssl_getdirection( void *conn ); + +G_MODULE_EXPORT size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len, const unsigned char *iv, unsigned char **res); diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c new file mode 100644 index 00000000..cdc7c498 --- /dev/null +++ b/lib/ssl_gnutls.c @@ -0,0 +1,280 @@ +  /********************************************************************\ +  * 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 <gcrypt.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; + +#include <limits.h> + +#if defined(ULONG_MAX) && ULONG_MAX > 4294967295UL +#define GNUTLS_STUPID_CAST (long) +#else +#define GNUTLS_STUPID_CAST (int) +#endif + +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 ); +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ); +static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); + + +void ssl_init( void ) +{ +	if( initialized ) +		return; +	 +	gnutls_global_init(); +	initialized = TRUE; +	atexit( gnutls_global_deinit ); +} + +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; +	} +	 +	return conn; +} + +void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +{ +	struct scd *conn = g_new0( struct scd, 1 ); +	 +	conn->fd = fd; +	conn->func = func; +	conn->data = data; +	conn->inpa = -1; +	 +	/* This function should be called via a (short) timeout instead of +	   directly from here, because these SSL calls are *supposed* to be +	   *completely* asynchronous and not ready yet when this function +	   (or *_connect, for examle) returns. Also, errors are reported via +	   the callback function, not via this function's return value. +	    +	   In short, doing things like this makes the rest of the code a lot +	   simpler. */ +	 +	b_timeout_add( 1, ssl_starttls_real, conn ); +	 +	return conn; +} + +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; +	 +	return ssl_connected( conn, conn->fd, B_EV_IO_WRITE ); +} + +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 ); +		g_free( conn ); +		return FALSE; +	} +	 +	ssl_init(); +	 +	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 ); +	 +	sock_make_nonblocking( conn->fd ); +	gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) GNUTLS_STUPID_CAST 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; +	 +	if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 1, buf, st ); +	 +	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; +	 +	if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 1, buf, st ); +	 +	return st; +} + +/* See ssl_openssl.c for an explanation. */ +int ssl_pending( void *conn ) +{ +	return 0; +} + +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 ); +	 +	if( conn->session ) +		gnutls_deinit( conn->session ); +	if( conn->xcred ) +		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 ) ? +	        B_EV_IO_WRITE : B_EV_IO_READ ); +} + +size_t ssl_des3_encrypt( const unsigned char *key, size_t key_len, const unsigned char *input, +                         size_t input_len, const unsigned char *iv, unsigned char **res ) +{ +	gcry_cipher_hd_t gcr; +	gcry_error_t st; +	 +	ssl_init(); +	 +	*res = g_malloc( input_len  ); +	st = gcry_cipher_open( &gcr, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0 ) || +	     gcry_cipher_setkey( gcr, key, key_len ) || +	     gcry_cipher_setiv( gcr, iv, 8 ) || +	     gcry_cipher_encrypt( gcr, *res, input_len, input, input_len ); +	 +	gcry_cipher_close( gcr ); +	 +	if( st == 0 ) +		return input_len; +	 +	g_free( *res ); +	return 0; +} diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c new file mode 100644 index 00000000..512c7655 --- /dev/null +++ b/lib/ssl_nss.c @@ -0,0 +1,240 @@ +  /********************************************************************\ +  * 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 <pk11pub.h> +#include <private/pprio.h> +#include <ssl.h> +#include <seccomon.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 gboolean ssl_starttls_real( 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_init( void ) +{ +	PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); +	NSS_NoDB_Init(NULL); +	NSS_SetDomesticPolicy(); +	initialized = TRUE; +} + +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 ) +	{ +		ssl_init(); +	} + +	 +	return( conn ); +} + +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; + +	return ssl_connected( conn, conn->fd, B_EV_IO_WRITE ); +} + +void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +{ +	struct scd *conn = g_new0( struct scd, 1 ); + +	conn->fd = fd; +	conn->func = func; +	conn->data = data; + +	/* This function should be called via a (short) timeout instead of +	   directly from here, because these SSL calls are *supposed* to be +	   *completely* asynchronous and not ready yet when this function +	   (or *_connect, for examle) returns. Also, errors are reported via +	   the callback function, not via this function's return value. + +	   In short, doing things like this makes the rest of the code a lot +	   simpler. */ + +	b_timeout_add( 1, ssl_starttls_real, conn ); + +	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 ) ); +} + +/* See ssl_openssl.c for an explanation. */ +int ssl_pending( void *conn ) +{ +	struct scd *c = (struct scd *) conn; + +	if( c == NULL ) { +		return 0; +	} + +	return ( c->established && SSL_DataPending( c->prfd ) > 0 ); +} + +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 B_EV_IO_READ; +} diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c new file mode 100644 index 00000000..64bc9257 --- /dev/null +++ b/lib/ssl_openssl.c @@ -0,0 +1,305 @@ +  /********************************************************************\ +  * 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 ); +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ); +static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); + + +void ssl_init( void ) +{ +	initialized = TRUE; +	SSL_library_init(); +	// SSLeay_add_ssl_algorithms(); +} + +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 ); +	if( conn->fd < 0 ) +	{ +		g_free( conn ); +		return NULL; +	} +	 +	conn->func = func; +	conn->data = data; +	conn->inpa = -1; +	 +	return conn; +} + +void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +{ +	struct scd *conn = g_new0( struct scd, 1 ); +	 +	conn->fd = fd; +	conn->func = func; +	conn->data = data; +	conn->inpa = -1; +	 +	/* This function should be called via a (short) timeout instead of +	   directly from here, because these SSL calls are *supposed* to be +	   *completely* asynchronous and not ready yet when this function +	   (or *_connect, for examle) returns. Also, errors are reported via +	   the callback function, not via this function's return value. +	    +	   In short, doing things like this makes the rest of the code a lot +	   simpler. */ +	 +	b_timeout_add( 1, ssl_starttls_real, conn ); +	 +	return conn; +} + +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; +	 +	return ssl_connected( conn, conn->fd, B_EV_IO_WRITE ); +} + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; +	SSL_METHOD *meth; +	 +	if( source == -1 ) +		goto ssl_connected_failure; +	 +	if( !initialized ) +	{ +		ssl_init(); +	} +	 +	meth = TLSv1_client_method(); +	conn->ssl_ctx = SSL_CTX_new( meth ); +	if( conn->ssl_ctx == NULL ) +		goto ssl_connected_failure; +	 +	conn->ssl = SSL_new( conn->ssl_ctx ); +	if( conn->ssl == NULL ) +		goto ssl_connected_failure; +	 +	/* 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 ); + +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; + +}	 + +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 ) +		{ +			conn->func( conn->data, NULL, cond ); +			 +			SSL_shutdown( conn->ssl ); +			SSL_free( conn->ssl ); +			SSL_CTX_free( conn->ssl_ctx ); +			 +			if( source >= 0 ) closesocket( source ); +			g_free( conn ); +			 +			return FALSE; +		} +		 +		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; +} + +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; +	} +	 +	if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st ); +	 +	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 ); +	 +	if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st ); +	 +	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; +} + +/* Only OpenSSL *really* needs this (and well, maybe NSS). See for more info: +   http://www.gnu.org/software/gnutls/manual/gnutls.html#index-gnutls_005frecord_005fcheck_005fpending-209 +   http://www.openssl.org/docs/ssl/SSL_pending.html +    +   Required because OpenSSL empties the TCP buffer completely but doesn't +   necessarily give us all the unencrypted data. +    +   Returns 0 if there's nothing left or if we don't have to care (GnuTLS), +   1 if there's more data. */ +int ssl_pending( void *conn ) +{ +	return ( ((struct scd*)conn) && ((struct scd*)conn)->established ) ? +	       SSL_pending( ((struct scd*)conn)->ssl ) > 0 : 0; +} + +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 ? B_EV_IO_WRITE : B_EV_IO_READ ); +} + +size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len, const unsigned char *iv, unsigned char **res) +{ +	int output_length = 0;     +	EVP_CIPHER_CTX ctx; +	 +	*res = g_new0(unsigned char, 72); +	 +	/* Don't set key or IV because we will modify the parameters */ +	EVP_CIPHER_CTX_init(&ctx); +	EVP_CipherInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, NULL, NULL, 1); +	EVP_CIPHER_CTX_set_key_length(&ctx, key_len); +	EVP_CIPHER_CTX_set_padding(&ctx, 0); +	/* We finished modifying parameters so now we can set key and IV */ +	EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, 1); +	EVP_CipherUpdate(&ctx, *res, &output_length, input, input_len); +	EVP_CipherFinal_ex(&ctx, *res, &output_length); +	EVP_CIPHER_CTX_cleanup(&ctx);    +	//EVP_cleanup(); +	 +	return output_length; +} diff --git a/lib/ssl_sspi.c b/lib/ssl_sspi.c new file mode 100644 index 00000000..e14c451e --- /dev/null +++ b/lib/ssl_sspi.c @@ -0,0 +1,278 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* SSL module - SSPI backend */ + +/* Copyright (C) 2005 Jelmer Vernooij <jelmer@samba.org> */ + +/* +  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" +#include <windows.h> +#define SECURITY_WIN32 +#include <security.h> +#include <sspi.h> +#include <schannel.h> +#include "sock.h" + +static gboolean initialized = FALSE; +int ssl_errno; + +struct scd +{ +	int fd; +	ssl_input_function func; +	gpointer data; +	gboolean established; +  	CredHandle cred;		/* SSL credentials */ +	CtxtHandle context;		/* SSL context */ +	SecPkgContext_StreamSizes sizes; + +	char *host; + +	char *pending_raw_data; +	gsize pending_raw_data_len; +	char *pending_data; +	gsize pending_data_len; +}; + +static void ssl_connected(gpointer, gint, GaimInputCondition); + +void sspi_global_init(void) +{ +	/* FIXME */ +} + +void sspi_global_deinit(void) +{ +	/* FIXME */ +} + +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); +	sock_make_nonblocking(conn->fd); +	conn->func = func; +	conn->data = data; +	conn->host = g_strdup(host); +	 +	if (conn->fd < 0) +	{ +		g_free(conn); +		return NULL; +	} +	 +	if (!initialized) +	{ +		sspi_global_init(); +		initialized = TRUE; +		atexit(sspi_global_deinit); +	} + +	return conn; +} + +static void ssl_connected(gpointer _conn, gint fd, GaimInputCondition cond) +{ +	struct scd *conn = _conn; +	SCHANNEL_CRED ssl_cred; +	TimeStamp timestamp; +	SecBuffer ibuf[2],obuf[1]; +	SecBufferDesc ibufs,obufs; +	ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | +    	ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY | +      	ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR | +		ISC_REQ_MANUAL_CRED_VALIDATION; +	ULONG a; +	gsize size = 0; +	gchar *data = NULL; + +	memset(&ssl_cred, 0, sizeof(SCHANNEL_CRED)); +	ssl_cred.dwVersion = SCHANNEL_CRED_VERSION; +	ssl_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT; + +	SECURITY_STATUS st = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &ssl_cred, NULL, NULL, &conn->cred, ×tamp); + +	if (st != SEC_E_OK) { +		conn->func(conn->data, NULL, cond); +		return; +	} +	 +	do { +		/* initialize buffers */ +	    ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = data; +	    ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NULL; +	    obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NULL; +    	ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN; +	    ibuf[1].BufferType = SECBUFFER_EMPTY; + +		/* initialize buffer descriptors */ +	    ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION; +	    ibufs.cBuffers = 2; obufs.cBuffers = 1; +	    ibufs.pBuffers = ibuf; obufs.pBuffers = obuf; + +		st = InitializeSecurityContext(&conn->cred, size?&conn->context:NULL, conn->host, req, 0, SECURITY_NETWORK_DREP, size?&ibufs:NULL, 0, &conn->context, &obufs, &a, ×tamp);   +    	if (obuf[0].pvBuffer && obuf[0].cbBuffer) { +			/* FIXME: Check return value */ +			send(conn->fd, obuf[0].pvBuffer, obuf[0].cbBuffer, 0); +		} + +		switch (st) { +		case SEC_I_INCOMPLETE_CREDENTIALS: +			break; +		case SEC_I_CONTINUE_NEEDED: +			break; +		case SEC_E_INCOMPLETE_MESSAGE: +			break; +		case SEC_E_OK: +			break; +		} +	 +		QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->sizes); +	} while (1); + +	conn->func(conn->data, conn, cond); +} + +int ssl_read(void *conn, char *retdata, int len) +{ +	struct scd *scd = conn; +	SecBufferDesc msg; +	SecBuffer buf[4]; +	int ret = -1, i; +	char *data = g_malloc(scd->sizes.cbHeader + scd->sizes.cbMaximumMessage + scd->sizes.cbTrailer); + +	/* FIXME: Try to read some data */ + +  	msg.ulVersion = SECBUFFER_VERSION; +	msg.cBuffers = 4; +	msg.pBuffers = buf; +	 +	buf[0].BufferType = SECBUFFER_DATA; +	buf[0].cbBuffer = len; +	buf[0].pvBuffer = data; + +	buf[1].BufferType = SECBUFFER_EMPTY; +	buf[2].BufferType = SECBUFFER_EMPTY; +	buf[3].BufferType = SECBUFFER_EMPTY; + +	SECURITY_STATUS st = DecryptMessage(&scd->context, &msg, 0, NULL); + +	if (st != SEC_E_OK) { +		/* FIXME */ +		return -1; +	} + +	for (i = 0; i < 4; i++) { +		if (buf[i].BufferType == SECBUFFER_DATA) { +			memcpy(retdata, buf[i].pvBuffer, len); +			ret = len; +		}	 +	} + +	g_free(data); +	return -1; +} + +int ssl_write(void *conn, const char *userdata, int len) +{ +	struct scd *scd = conn; +	SecBuffer buf[4]; +	SecBufferDesc msg; +	char *data; +	int ret; + +	msg.ulVersion = SECBUFFER_VERSION; +	msg.cBuffers = 4; +	msg.pBuffers = buf; + +	data = g_malloc(scd->sizes.cbHeader + scd->sizes.cbMaximumMessage + scd->sizes.cbTrailer); +	memcpy(data + scd->sizes.cbHeader, userdata, len); + +	buf[0].BufferType = SECBUFFER_STREAM_HEADER; +	buf[0].cbBuffer = scd->sizes.cbHeader; +	buf[0].pvBuffer = data; + +	buf[1].BufferType = SECBUFFER_DATA; +	buf[1].cbBuffer = len; +	buf[1].pvBuffer = data + scd->sizes.cbHeader; + +	buf[2].BufferType = SECBUFFER_STREAM_TRAILER; +	buf[2].cbBuffer = scd->sizes.cbTrailer; +	buf[2].pvBuffer = data + scd->sizes.cbHeader + len; +	buf[3].BufferType = SECBUFFER_EMPTY; + +	SECURITY_STATUS st = EncryptMessage(&scd->context, 0, &msg, 0); + +	ret = send(scd->fd, data,  +				buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer, 0); + +	g_free(data); + +	return ret; +} + +void ssl_disconnect(void *conn) +{ +	struct scd *scd = conn; + +	SecBufferDesc msg; +	SecBuffer buf; +	DWORD dw; + +	dw = SCHANNEL_SHUTDOWN; +	buf.cbBuffer = sizeof(dw); +	buf.BufferType = SECBUFFER_TOKEN; +	buf.pvBuffer = &dw; +	 +	msg.ulVersion = SECBUFFER_VERSION; +	msg.cBuffers = 1; +	msg.pBuffers = &buf; + +	SECURITY_STATUS st = ApplyControlToken(&scd->context, &msg); + +	if (st != SEC_E_OK) { +		/* FIXME */ +	} +	 +	/* FIXME: call InitializeSecurityContext(Schannel), passing  +	 * in empty buffers*/ + +	DeleteSecurityContext(&scd->context); + +	FreeCredentialsHandle(&scd->cred); + +	closesocket(scd->fd); +	g_free(scd->host); +	g_free(scd); +} + +int ssl_getfd(void *conn) +{ +	return ((struct scd*)conn)->fd; +} + +GaimInputCondition ssl_getdirection( void *conn ) +{ +	return B_EV_IO_WRITE; /* FIXME: or B_EV_IO_READ */ +} diff --git a/lib/url.c b/lib/url.c new file mode 100644 index 00000000..9e330f8c --- /dev/null +++ b/lib/url.c @@ -0,0 +1,109 @@ +  /********************************************************************\ +  * 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, const char *set_url ) +{ +	char s[MAX_STRING+1]; +	char *i; +	 +	memset( url, 0, sizeof( url_t ) ); +	memset( s, 0, sizeof( s ) ); +	 +	/* 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_SOCKS5 ) +			url->port = 1080; +	} +	 +	return( url->port > 0 ); +} diff --git a/lib/url.h b/lib/url.h new file mode 100644 index 00000000..55107ad2 --- /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+1]; +	char file[MAX_STRING+1]; +	char user[MAX_STRING+1]; +	char pass[MAX_STRING+1]; +} url_t; + +int url_set( url_t *url, const char *set_url ); diff --git a/lib/xmltree.c b/lib/xmltree.c new file mode 100644 index 00000000..54a7dd13 --- /dev/null +++ b/lib/xmltree.c @@ -0,0 +1,698 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       * +*                                                                           * +*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*  This library is free software; you can redistribute it and/or            * +*  modify it under the terms of the GNU Lesser General Public               * +*  License as published by the Free Software Foundation, version            * +*  2.1.                                                                     * +*                                                                           * +*  This library 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        * +*  Lesser General Public License for more details.                          * +*                                                                           * +*  You should have received a copy of the GNU Lesser General Public License * +*  along with this library; if not, write to the Free Software Foundation,  * +*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           * +*                                                                           * +****************************************************************************/ + +#include <glib.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <stdio.h> + +#include "xmltree.h" + +static void xt_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error ) +{ +	struct xt_parser *xt = data; +	struct xt_node *node = g_new0( struct xt_node, 1 ), *nt; +	int i; +	 +	node->parent = xt->cur; +	node->name = g_strdup( element_name ); +	 +	/* First count the number of attributes */ +	for( i = 0; attr_names[i]; i ++ ); +	 +	/* Then allocate a NULL-terminated array. */ +	node->attr = g_new0( struct xt_attr, i + 1 ); +	 +	/* And fill it, saving one variable by starting at the end. */ +	for( i --; i >= 0; i -- ) +	{ +		node->attr[i].key = g_strdup( attr_names[i] ); +		node->attr[i].value = g_strdup( attr_values[i] ); +	} +	 +	/* Add it to the linked list of children nodes, if we have a current +	   node yet. */ +	if( xt->cur ) +	{ +		if( xt->cur->children ) +		{ +			for( nt = xt->cur->children; nt->next; nt = nt->next ); +			nt->next = node; +		} +		else +		{ +			xt->cur->children = node; +		} +	} +	else if( xt->root ) +	{ +		/* ERROR situation: A second root-element??? */ +	} +	 +	/* Now this node will be the new current node. */ +	xt->cur = node; +	/* And maybe this is the root? */ +	if( xt->root == NULL ) +		xt->root = node; +} + +static void xt_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error ) +{ +	struct xt_parser *xt = data; +	struct xt_node *node = xt->cur; +	 +	if( node == NULL ) +		return; +	 +	/* FIXME: Does g_renew also OFFICIALLY accept NULL arguments? */ +	node->text = g_renew( char, node->text, node->text_len + text_len + 1 ); +	memcpy( node->text + node->text_len, text, text_len ); +	node->text_len += text_len; +	/* Zero termination is always nice to have. */ +	node->text[node->text_len] = 0; +} + +static void xt_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error ) +{ +	struct xt_parser *xt = data; +	 +	xt->cur->flags |= XT_COMPLETE; +	xt->cur = xt->cur->parent; +} + +GMarkupParser xt_parser_funcs = +{ +	xt_start_element, +	xt_end_element, +	xt_text, +	NULL, +	NULL +}; + +struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data ) +{ +	struct xt_parser *xt = g_new0( struct xt_parser, 1 ); +	 +	xt->data = data; +	xt->handlers = handlers; +	xt_reset( xt ); +	 +	return xt; +} + +/* Reset the parser, flush everything we have so far. For example, we need +   this for XMPP when doing TLS/SASL to restart the stream. */ +void xt_reset( struct xt_parser *xt ) +{ +	if( xt->parser ) +		g_markup_parse_context_free( xt->parser ); +	 +	xt->parser = g_markup_parse_context_new( &xt_parser_funcs, 0, xt, NULL ); +	 +	if( xt->root ) +	{ +		xt_free_node( xt->root ); +		xt->root = NULL; +		xt->cur = NULL; +	} +} + +/* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on +   end-of-stream and 1 otherwise. */ +int xt_feed( struct xt_parser *xt, const char *text, int text_len ) +{ +	if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) ) +	{ +		return -1; +	} +	 +	return !( xt->root && xt->root->flags & XT_COMPLETE ); +} + +/* Find completed nodes and see if a handler has to be called. Passing +   a node isn't necessary if you want to start at the root, just pass +   NULL. This second argument is needed for recursive calls. */ +int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth ) +{ +	struct xt_node *c; +	xt_status st; +	int i; +	 +	/* Just in case someone likes infinite loops... */ +	if( xt->root == NULL ) +		return 0; +	 +	if( node == NULL ) +		return xt_handle( xt, xt->root, depth ); +	 +	if( depth != 0 ) +		for( c = node->children; c; c = c->next ) +			if( !xt_handle( xt, c, depth > 0 ? depth - 1 : depth ) ) +				return 0; +	 +	if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) ) +	{ +		if( xt->handlers ) for( i = 0; xt->handlers[i].func; i ++ ) +		{ +			/* This one is fun! \o/ */ +			 +			    /* If handler.name == NULL it means it should always match. */ +			if( ( xt->handlers[i].name == NULL ||  +			      /* If it's not, compare. There should always be a name. */ +			      g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) && +			    /* If handler.parent == NULL, it's a match. */ +			    ( xt->handlers[i].parent == NULL || +			      /* If there's a parent node, see if the name matches. */ +			      ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 :  +			      /* If there's no parent, the handler should mention <root> as a parent. */ +			                       strcmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) ) +			{ +				st = xt->handlers[i].func( node, xt->data ); +				 +				if( st == XT_ABORT ) +					return 0; +				else if( st != XT_NEXT ) +					break; +			} +		} +		 +		node->flags |= XT_SEEN; +	} +	 +	return 1; +} + +/* Garbage collection: Cleans up all nodes that are handled. Useful for +   streams because there's no reason to keep a complete packet history +   in memory. */ +void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth ) +{ +	struct xt_node *c, *prev; +	 +	if( !xt || !xt->root ) +		return; +	 +	if( node == NULL ) +	{ +		xt_cleanup( xt, xt->root, depth ); +		return; +	} +	 +	if( node->flags & XT_SEEN && node == xt->root ) +	{ +		xt_free_node( xt->root ); +		xt->root = xt->cur = NULL; +		/* xt->cur should be NULL already, BTW... */ +		 +		return; +	} +	 +	/* c contains the current node, prev the previous node (or NULL). +	   I admit, this one's pretty horrible. */ +	for( c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children ) +	{ +		if( c->flags & XT_SEEN ) +		{ +			/* Remove the node from the linked list. */ +			if( prev ) +				prev->next = c->next; +			else +				node->children = c->next; +			 +			xt_free_node( c ); +			 +			/* Since the for loop wants to get c->next, make sure +			   c points at something that exists (and that c->next +			   will actually be the next item we should check). c +			   can be NULL now, if we just removed the first item. +			   That explains the ? thing in for(). */ +			c = prev; +		} +		else +		{ +			/* This node can't be cleaned up yet, but maybe a +			   subnode can. */ +			if( depth != 0 ) +				xt_cleanup( xt, c, depth > 0 ? depth - 1 : depth ); +		} +	} +} + +struct xt_node *xt_from_string( const char *in ) +{ +	struct xt_parser *parser; +	struct xt_node *ret; +	 +	parser = xt_new( NULL, NULL ); +	xt_feed( parser, in, strlen( in ) ); +	ret = parser->root; +	parser->root = NULL; +	xt_free( parser ); +	 +	return ret; +} + +static void xt_to_string_real( struct xt_node *node, GString *str ) +{ +	char *buf; +	struct xt_node *c; +	int i; +	 +	g_string_append_printf( str, "<%s", node->name ); +	 +	for( i = 0; node->attr[i].key; i ++ ) +	{ +		buf = g_markup_printf_escaped( " %s=\"%s\"", node->attr[i].key, node->attr[i].value ); +		g_string_append( str, buf ); +		g_free( buf ); +	} +	 +	if( node->text == NULL && node->children == NULL ) +	{ +		g_string_append( str, "/>" ); +		return; +	} +	 +	g_string_append( str, ">" ); +	if( node->text_len > 0 ) +	{ +		buf = g_markup_escape_text( node->text, node->text_len ); +		g_string_append( str, buf ); +		g_free( buf ); +	} +	 +	for( c = node->children; c; c = c->next ) +		xt_to_string_real( c, str ); +	 +	g_string_append_printf( str, "</%s>", node->name ); +} + +char *xt_to_string( struct xt_node *node ) +{ +	GString *ret; +	char *real; +	 +	ret = g_string_new( "" ); +	xt_to_string_real( node, ret ); +	 +	real = ret->str; +	g_string_free( ret, FALSE ); +	 +	return real; +} + +#ifdef DEBUG +void xt_print( struct xt_node *node ) +{ +	int i; +	struct xt_node *c; +	 +	/* Indentation */ +	for( c = node; c->parent; c = c->parent ) +		printf( "    " ); +	 +	/* Start the tag */ +	printf( "<%s", node->name ); +	 +	/* Print the attributes */ +	for( i = 0; node->attr[i].key; i ++ ) +	{ +		char *v = g_markup_escape_text( node->attr[i].value, -1 ); +		printf( " %s=\"%s\"", node->attr[i].key, v ); +		g_free( v ); +	} +	 +	/* /> in case there's really *nothing* inside this tag, otherwise +	   just >. */ +	/* If this tag doesn't have any content at all... */ +	if( node->text == NULL && node->children == NULL ) +	{ +		printf( "/>\n" ); +		return; +		/* Then we're finished! */ +	} +	 +	/* Otherwise... */ +	printf( ">" ); +	 +	/* Only print the text if it contains more than whitespace (TEST). */ +	if( node->text_len > 0 ) +	{ +		for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ ); +		if( node->text[i] ) +		{ +			char *v = g_markup_escape_text( node->text, -1 ); +			printf( "%s", v ); +			g_free( v ); +		} +	} +	 +	if( node->children ) +		printf( "\n" ); +	 +	for( c = node->children; c; c = c->next ) +		xt_print( c ); +	 +	if( node->children ) +		for( c = node; c->parent; c = c->parent ) +			printf( "    " ); +	 +	/* Non-empty tag is now finished. */ +	printf( "</%s>\n", node->name ); +} +#endif + +struct xt_node *xt_dup( struct xt_node *node ) +{ +	struct xt_node *dup = g_new0( struct xt_node, 1 ); +	struct xt_node *c, *dc = NULL; +	int i; +	 +	/* Let's NOT copy the parent element here BTW! Only do it for children. */ +	 +	dup->name = g_strdup( node->name ); +	dup->flags = node->flags; +	if( node->text ) +	{ +		dup->text = g_memdup( node->text, node->text_len + 1 ); +		dup->text_len = node->text_len; +	} +	 +	/* Count the number of attributes and allocate the new array. */ +	for( i = 0; node->attr[i].key; i ++ ); +	dup->attr = g_new0( struct xt_attr, i + 1 ); +	 +	/* Copy them all! */ +	for( i --; i >= 0; i -- ) +	{ +		dup->attr[i].key = g_strdup( node->attr[i].key ); +		dup->attr[i].value = g_strdup( node->attr[i].value ); +	} +	 +	/* This nice mysterious loop takes care of the children. */ +	for( c = node->children; c; c = c->next ) +	{ +		if( dc == NULL ) +			dc = dup->children = xt_dup( c ); +		else +			dc = ( dc->next = xt_dup( c ) ); +		 +		dc->parent = dup; +	} +	 +	return dup; +} + +/* Frees a node. This doesn't clean up references to itself from parents! */ +void xt_free_node( struct xt_node *node ) +{ +	int i; +	 +	if( !node ) +		return; +	 +	g_free( node->name ); +	g_free( node->text ); +	 +	for( i = 0; node->attr[i].key; i ++ ) +	{ +		g_free( node->attr[i].key ); +		g_free( node->attr[i].value ); +	} +	g_free( node->attr ); +	 +	while( node->children ) +	{ +		struct xt_node *next = node->children->next; +		 +		xt_free_node( node->children ); +		node->children = next; +	} +	 +	g_free( node ); +} + +void xt_free( struct xt_parser *xt ) +{ +	if( !xt ) +		return; +	 +	if( xt->root ) +		xt_free_node( xt->root ); +	 +	g_markup_parse_context_free( xt->parser ); +	 +	g_free( xt ); +} + +/* To find a node's child with a specific name, pass the node's children +   list, not the node itself! The reason you have to do this by hand: So +   that you can also use this function as a find-next. */ +struct xt_node *xt_find_node( struct xt_node *node, const char *name ) +{ +	while( node ) +	{ +		char *colon; +		 +		if( g_strcasecmp( node->name, name ) == 0 || +		    ( ( colon = strchr( node->name, ':' ) ) && +		      g_strcasecmp( colon + 1, name ) == 0 ) ) +			break; +		 +		node = node->next; +	} +	 +	return node; +} + +/* More advanced than the one above, understands something like +   ../foo/bar to find a subnode bar of a node foo which is a child +   of node's parent. Pass the node directly, not its list of children. */ +struct xt_node *xt_find_path( struct xt_node *node, const char *name ) +{ +	while( name && *name && node ) +	{ +		char *colon, *slash; +		int n; +		 +		if( ( slash = strchr( name, '/' ) ) ) +			n = slash - name; +		else +			n = strlen( name ); +		 +		if( strncmp( name, "..", n ) == 0 ) +		{ +			node = node->parent; +		} +		else +		{ +			node = node->children; +			 +			while( node ) +			{ +				if( g_strncasecmp( node->name, name, n ) == 0 || +				    ( ( colon = strchr( node->name, ':' ) ) && +				      g_strncasecmp( colon + 1, name, n ) == 0 ) ) +					break; +				 +				node = node->next; +			} +		} +		 +		name = slash ? slash + 1 : NULL; +	} +	 +	return node; +} + +char *xt_find_attr( struct xt_node *node, const char *key ) +{ +	int i; +	char *colon; +	 +	if( !node ) +		return NULL; +	 +	for( i = 0; node->attr[i].key; i ++ ) +		if( g_strcasecmp( node->attr[i].key, key ) == 0 ) +			break; +	 +	/* This is an awful hack that only takes care of namespace prefixes +	   inside a tag. Since IMHO excessive namespace usage in XMPP is +	   massive overkill anyway (this code exists for almost four years +	   now and never really missed it): Meh. */ +	if( !node->attr[i].key && strcmp( key, "xmlns" ) == 0 && +	    ( colon = strchr( node->name, ':' ) ) ) +	{ +		*colon = '\0'; +		for( i = 0; node->attr[i].key; i ++ ) +			if( strncmp( node->attr[i].key, "xmlns:", 6 ) == 0 && +			    strcmp( node->attr[i].key + 6, node->name ) == 0 ) +				break; +		*colon = ':'; +	} +	 +	return node->attr[i].value; +} + +struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children ) +{ +	struct xt_node *node, *c; +	 +	node = g_new0( struct xt_node, 1 ); +	node->name = g_strdup( name ); +	node->children = children; +	node->attr = g_new0( struct xt_attr, 1 ); +	 +	if( text ) +	{ +		node->text_len = strlen( text ); +		node->text = g_memdup( text, node->text_len + 1 ); +	} +	 +	for( c = children; c; c = c->next ) +	{ +		if( c->parent != NULL ) +		{ +			/* ERROR CONDITION: They seem to have a parent already??? */ +		} +		 +		c->parent = node; +	} +	 +	return node; +} + +void xt_add_child( struct xt_node *parent, struct xt_node *child ) +{ +	struct xt_node *node; +	 +	/* This function can actually be used to add more than one child, so +	   do handle this properly. */ +	for( node = child; node; node = node->next ) +	{ +		if( node->parent != NULL ) +		{ +			/* ERROR CONDITION: They seem to have a parent already??? */ +		} +		 +		node->parent = parent; +	} +	 +	if( parent->children == NULL ) +	{ +		parent->children = child; +	} +	else +	{ +		for( node = parent->children; node->next; node = node->next ); +		node->next = child; +	} +} + +/* Same, but at the beginning. */ +void xt_insert_child( struct xt_node *parent, struct xt_node *child ) +{ +	struct xt_node *node, *last = NULL; +	 +	if( child == NULL ) +		return; /* BUG */ +	 +	for( node = child; node; node = node->next ) +	{ +		if( node->parent != NULL ) +		{ +			/* ERROR CONDITION: They seem to have a parent already??? */ +		} +		 +		node->parent = parent; +		last = node; +	} +	 +	last->next = parent->children; +	parent->children = child; +} + +void xt_add_attr( struct xt_node *node, const char *key, const char *value ) +{ +	int i; +	 +	/* Now actually it'd be nice if we can also change existing attributes +	   (which actually means this function doesn't have the right name). +	   So let's find out if we have this attribute already... */ +	for( i = 0; node->attr[i].key; i ++ ) +		if( strcmp( node->attr[i].key, key ) == 0 ) +			break; +	 +	if( node->attr[i].key == NULL ) +	{ +		/* If not, allocate space for a new attribute. */ +		node->attr = g_renew( struct xt_attr, node->attr, i + 2 ); +		node->attr[i].key = g_strdup( key ); +		node->attr[i+1].key = NULL; +	} +	else +	{ +		/* Otherwise, free the old value before setting the new one. */ +		g_free( node->attr[i].value ); +	} +	 +	node->attr[i].value = g_strdup( value ); +} + +int xt_remove_attr( struct xt_node *node, const char *key ) +{ +	int i, last; +	 +	for( i = 0; node->attr[i].key; i ++ ) +		if( strcmp( node->attr[i].key, key ) == 0 ) +			break; +	 +	/* If we didn't find the attribute... */ +	if( node->attr[i].key == NULL ) +		return 0; +	 +	g_free( node->attr[i].key ); +	g_free( node->attr[i].value ); +	 +	/* If it's the last, this is easy: */ +	if( node->attr[i+1].key == NULL ) +	{ +		node->attr[i].key = node->attr[i].value = NULL; +	} +	else /* It's also pretty easy, actually. */ +	{ +		/* Find the last item. */ +		for( last = i + 1; node->attr[last+1].key; last ++ ); +		 +		node->attr[i] = node->attr[last]; +		node->attr[last].key = NULL; +		node->attr[last].value = NULL; +	} +	 +	/* Let's not bother with reallocating memory here. It takes time and +	   most packets don't stay in memory for long anyway. */ +	 +	return 1; +} diff --git a/lib/xmltree.h b/lib/xmltree.h new file mode 100644 index 00000000..5a0dbc8e --- /dev/null +++ b/lib/xmltree.h @@ -0,0 +1,100 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       * +*                                                                           * +*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*  This library is free software; you can redistribute it and/or            * +*  modify it under the terms of the GNU Lesser General Public               * +*  License as published by the Free Software Foundation, version            * +*  2.1.                                                                     * +*                                                                           * +*  This library 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        * +*  Lesser General Public License for more details.                          * +*                                                                           * +*  You should have received a copy of the GNU Lesser General Public License * +*  along with this library; if not, write to the Free Software Foundation,  * +*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           * +*                                                                           * +****************************************************************************/ + +#ifndef _XMLTREE_H +#define _XMLTREE_H + +typedef enum +{ +	XT_COMPLETE	= 1,	/* </tag> reached */ +	XT_SEEN		= 2,	/* Handler called (or not defined) */ +} xt_flags; + +typedef enum +{ +	XT_ABORT,		/* Abort, don't handle the rest anymore */ +	XT_HANDLED,		/* Handled this tag properly, go to the next one */ +	XT_NEXT			/* Try if there's another matching handler */ +} xt_status; + +struct xt_attr +{ +	char *key, *value; +}; + +struct xt_node +{ +	struct xt_node *parent; +	struct xt_node *children; +	 +	char *name; +	struct xt_attr *attr; +	char *text; +	int text_len; +	 +	struct xt_node *next; +	xt_flags flags; +}; + +typedef xt_status (*xt_handler_func) ( struct xt_node *node, gpointer data ); + +struct xt_handler_entry +{ +	char *name, *parent; +	xt_handler_func func; +}; + +struct xt_parser +{ +	GMarkupParseContext *parser; +	struct xt_node *root; +	struct xt_node *cur; +	 +	const struct xt_handler_entry *handlers; +	gpointer data; +	 +	GError *gerr; +}; + +struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data ); +void xt_reset( struct xt_parser *xt ); +int xt_feed( struct xt_parser *xt, const char *text, int text_len ); +int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth ); +void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth ); +struct xt_node *xt_from_string( const char *in ); +char *xt_to_string( struct xt_node *node ); +void xt_print( struct xt_node *node ); +struct xt_node *xt_dup( struct xt_node *node ); +void xt_free_node( struct xt_node *node ); +void xt_free( struct xt_parser *xt ); +struct xt_node *xt_find_node( struct xt_node *node, const char *name ); +struct xt_node *xt_find_path( struct xt_node *node, const char *name ); +char *xt_find_attr( struct xt_node *node, const char *key ); + +struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children ); +void xt_add_child( struct xt_node *parent, struct xt_node *child ); +void xt_insert_child( struct xt_node *parent, struct xt_node *child ); +void xt_add_attr( struct xt_node *node, const char *key, const char *value ); +int xt_remove_attr( struct xt_node *node, const char *key ); + +#endif | 
