From e6b41b1db86aded510fa413d3ab1c34e624c9c27 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 20 Oct 2011 23:59:14 -0400 Subject: Strip illegal characters in generated XML streams so Jabber servers won't disconnect people who don't understand how to disable stupid IRC client features. Based very loosely on a patch and discussion submitted by Artem Savkov on bug #552. --- lib/xmltree.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/xmltree.c b/lib/xmltree.c index 54a7dd13..c4f34cd9 100644 --- a/lib/xmltree.c +++ b/lib/xmltree.c @@ -556,6 +556,28 @@ char *xt_find_attr( struct xt_node *node, const char *key ) return node->attr[i].value; } +/* Strip a few non-printable characters that aren't allowed in XML streams + (and upset some XMPP servers for example). */ +void xt_strip_text( char *in ) +{ + char *out = in; + static const char nonprint[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0..7 */ + 0, 1, 1, 0, 0, 1, 0, 0, /* 9 (tab), 10 (\n), 13 (\r) */ + }; + + if( !in ) + return; + + while( *in ) + { + if( (unsigned int) *in >= ' ' || nonprint[(unsigned int) *in] ) + *out ++ = *in; + in ++; + } + *out = *in; +} + struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children ) { struct xt_node *node, *c; @@ -567,8 +589,9 @@ struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *child if( text ) { - node->text_len = strlen( text ); - node->text = g_memdup( text, node->text_len + 1 ); + node->text = g_strdup( text ); + xt_strip_text( node->text ); + node->text_len = strlen( node->text ); } for( c = children; c; c = c->next ) -- cgit v1.2.3 From dff732d93799db858a5728abbf31d2e6274b8425 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 12 Nov 2011 23:49:52 +0800 Subject: Undoing old workaround for MSN troubles and added more proper fix. The http_client module needs some refactoring though. Will do that later.. This should hopefully fix bug #850. --- lib/http_client.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/http_client.c b/lib/http_client.c index 8b045414..f535f13f 100644 --- a/lib/http_client.c +++ b/lib/http_client.c @@ -313,7 +313,7 @@ got_reply: req->status_code = -1; } - if( ( req->status_code == 301 || req->status_code == 302 ) && req->redir_ttl-- > 0 ) + if( ( req->status_code >= 301 && req->status_code <= 303 ) && req->redir_ttl-- > 0 ) { char *loc, *new_request, *new_host; int error = 0, new_port, new_proto; @@ -374,10 +374,13 @@ got_reply: /* 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 = 0; + sprintf( new_request, "%s %s HTTP/1.0\r\nHost: %s", + req->status_code == 303 || req->request[0] == 'G' ? "GET" : "POST", url->file, url->host ); + *s = ' '; - s = strstr( req->request, "\r\n" ); - if( s == NULL ) + if( !( ( s = strstr( req->request, "\r\nHost: " ) ) && + ( s = strstr( s + strlen( "\r\nHost: " ), "\r\n" ) ) ) ) { req->status_string = g_strdup( "Error while rebuilding request string" ); g_free( new_request ); -- cgit v1.2.3 From 03a8f8ec82719512f059fdd2c0dca3bb919b059c Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 13 Nov 2011 09:41:42 +0800 Subject: Cleanup of http_client fix. Use g_strdup_printf and completely avoid strcat, truncate request if we switched from POST to GET. --- lib/http_client.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/http_client.c b/lib/http_client.c index f535f13f..84914388 100644 --- a/lib/http_client.c +++ b/lib/http_client.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2005 Wilmer van der Gaast and others * + * Copyright 2002-2011 Wilmer van der Gaast and others * \********************************************************************/ /* HTTP(S) module */ @@ -353,6 +353,7 @@ got_reply: /* A whole URL */ url_t *url; char *s; + const char *new_method; s = strstr( loc, "\r\n" ); if( s == NULL ) @@ -368,31 +369,37 @@ got_reply: 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. :-) */ - - *s = 0; - sprintf( new_request, "%s %s HTTP/1.0\r\nHost: %s", - req->status_code == 303 || req->request[0] == 'G' ? "GET" : "POST", url->file, url->host ); - *s = ' '; - + /* Find all headers and, if necessary, the POST request contents. + Skip the old Host: header though. This crappy code here means + anything using this http_client MUST put the Host: header at + the top. */ if( !( ( s = strstr( req->request, "\r\nHost: " ) ) && ( s = strstr( s + strlen( "\r\nHost: " ), "\r\n" ) ) ) ) { req->status_string = g_strdup( "Error while rebuilding request string" ); - g_free( new_request ); g_free( url ); goto cleanup; } - strcat( new_request, s ); + /* More or less HTTP/1.0 compliant, from my reading of RFC 2616. + Always perform a GET request unless we received a 301. 303 was + meant for this but it's HTTP/1.1-only and we're specifically + speaking HTTP/1.0. */ + new_method = req->status_code != 301 || req->request[0] == 'G' ? "GET" : "POST"; + + /* Okay, this isn't fun! We have to rebuild the request... :-( */ + new_request = g_strdup_printf( "%s %s HTTP/1.0\r\nHost: %s%s", + new_method, url->file, url->host, s ); + new_host = g_strdup( url->host ); new_port = url->port; new_proto = url->proto; + /* If we went from POST to GET, truncate the request content. */ + if( new_request[0] != req->request[0] && new_request[0] == 'G' && + ( s = strstr( new_request, "\r\n\r\n" ) ) ) + s[4] = '\0'; + g_free( url ); } -- cgit v1.2.3 From 80acb6db8133ebd16c3257d33721994ed164ba9f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 14 Nov 2011 18:43:03 +0800 Subject: GnuTLS now also needs ssl_pending() implemented. Bug #860. --- lib/http_client.c | 5 ++++- lib/ssl_client.h | 10 +++++++++- lib/ssl_gnutls.c | 20 +++++++++++++++----- lib/ssl_nss.c | 1 - lib/ssl_openssl.c | 9 --------- 5 files changed, 28 insertions(+), 17 deletions(-) (limited to 'lib') diff --git a/lib/http_client.c b/lib/http_client.c index 84914388..8bd543af 100644 --- a/lib/http_client.c +++ b/lib/http_client.c @@ -239,7 +239,10 @@ static gboolean http_incoming_data( gpointer data, int source, b_input_condition req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_READ, http_incoming_data, req ); - return FALSE; + if( ssl_pending( req->ssl ) ) + return http_incoming_data( data, source, cond ); + else + return FALSE; got_reply: /* Maybe if the webserver is overloaded, or when there's bad SSL diff --git a/lib/ssl_client.h b/lib/ssl_client.h index d0340840..091335c5 100644 --- a/lib/ssl_client.h +++ b/lib/ssl_client.h @@ -62,7 +62,15 @@ G_MODULE_EXPORT void *ssl_starttls( int fd, ssl_input_function func, gpointer da 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. */ +/* Now needed by most SSL libs. 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. Or maybe you didn't ask + for all of it because your buffer is too small. + + Returns 0 if there's nothing left, 1 if there's more data. */ G_MODULE_EXPORT int ssl_pending( void *conn ); /* Abort the SSL connection and disconnect the socket. Do not use close() diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c index 72517e72..d72b17f5 100644 --- a/lib/ssl_gnutls.c +++ b/lib/ssl_gnutls.c @@ -134,7 +134,9 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con gnutls_certificate_allocate_credentials( &conn->xcred ); gnutls_init( &conn->session, GNUTLS_CLIENT ); - gnutls_transport_set_lowat( conn->session, 1 ); +#if GNUTLS_VERSION_NUMBER < 0x020c00 + gnutls_transport_set_lowat( conn->session, 0 ); +#endif gnutls_set_default_priority( conn->session ); gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred ); @@ -186,7 +188,7 @@ int ssl_read( void *conn, char *buf, int len ) if( !((struct scd*)conn)->established ) { ssl_errno = SSL_NOHANDSHAKE; - return( -1 ); + return -1; } st = gnutls_record_recv( ((struct scd*)conn)->session, buf, len ); @@ -207,7 +209,7 @@ int ssl_write( void *conn, const char *buf, int len ) if( !((struct scd*)conn)->established ) { ssl_errno = SSL_NOHANDSHAKE; - return( -1 ); + return -1; } st = gnutls_record_send( ((struct scd*)conn)->session, buf, len ); @@ -221,10 +223,18 @@ int ssl_write( void *conn, const char *buf, int len ) return st; } -/* See ssl_openssl.c for an explanation. */ int ssl_pending( void *conn ) { - return 0; + if( conn == NULL ) + return 0; + + if( !((struct scd*)conn)->established ) + { + ssl_errno = SSL_NOHANDSHAKE; + return 0; + } + + return gnutls_record_check_pending( ((struct scd*)conn)->session ) != 0; } void ssl_disconnect( void *conn_ ) diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c index 512c7655..ec524ca6 100644 --- a/lib/ssl_nss.c +++ b/lib/ssl_nss.c @@ -206,7 +206,6 @@ int ssl_write( void *conn, const char *buf, int len ) 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; diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c index 64bc9257..5f64042d 100644 --- a/lib/ssl_openssl.c +++ b/lib/ssl_openssl.c @@ -240,15 +240,6 @@ int ssl_write( void *conn, const char *buf, int len ) 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 ) ? -- cgit v1.2.3 From bd3166176fdb7ed96211eb539316f47843e0faa7 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 2 Dec 2011 10:32:54 +0000 Subject: Somewhat improve debug logging of HTTP/SOAP stuff. --- lib/http_client.c | 10 ++++++++++ lib/xmltree.c | 2 -- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/http_client.c b/lib/http_client.c index 8bd543af..56a6f376 100644 --- a/lib/http_client.c +++ b/lib/http_client.c @@ -278,6 +278,9 @@ got_reply: *end1 = 0; + if( getenv( "BITLBEE_DEBUG" ) ) + printf( "HTTP response headers:\n%s", req->reply_headers ); + if( evil_server ) req->reply_body = end1 + 1; else @@ -414,6 +417,9 @@ got_reply: req->fd = -1; req->ssl = NULL; + if( getenv( "BITLBEE_DEBUG" ) ) + printf( "New headers for redirected HTTP request:\n%s", new_request ); + if( new_proto == PROTO_HTTPS ) { req->ssl = ssl_connect( new_host, new_port, http_ssl_connected, req ); @@ -455,6 +461,10 @@ cleanup: else closesocket( req->fd ); + if( getenv( "BITLBEE_DEBUG" ) && req ) + printf( "Finishing HTTP request with status: %s", + req->status_string ? req->status_string : "NULL" ); + req->func( req ); http_free( req ); return FALSE; diff --git a/lib/xmltree.c b/lib/xmltree.c index c4f34cd9..7dd7b6a3 100644 --- a/lib/xmltree.c +++ b/lib/xmltree.c @@ -322,7 +322,6 @@ char *xt_to_string( struct xt_node *node ) return real; } -#ifdef DEBUG void xt_print( struct xt_node *node ) { int i; @@ -381,7 +380,6 @@ void xt_print( struct xt_node *node ) /* Non-empty tag is now finished. */ printf( "\n", node->name ); } -#endif struct xt_node *xt_dup( struct xt_node *node ) { -- cgit v1.2.3 From ca974d7f6171a70c5484420bd6c59919383105a6 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 4 Dec 2011 19:14:29 +0000 Subject: Debug output tweaks: Try to send everything to stderr, and add ifdef to enable printing of all SSL traffic. --- lib/ssl_gnutls.c | 6 ++++-- lib/xmltree.c | 18 +++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c index d72b17f5..ccab8aca 100644 --- a/lib/ssl_gnutls.c +++ b/lib/ssl_gnutls.c @@ -44,6 +44,8 @@ static gboolean initialized = FALSE; #define GNUTLS_STUPID_CAST (int) #endif +#define SSLDEBUG 0 + struct scd { ssl_input_function func; @@ -197,7 +199,7 @@ int ssl_read( void *conn, char *buf, int len ) 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 ); + if( SSLDEBUG && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 2, buf, st ); return st; } @@ -218,7 +220,7 @@ int ssl_write( void *conn, const char *buf, int len ) 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 ); + if( SSLDEBUG && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 2, buf, st ); return st; } diff --git a/lib/xmltree.c b/lib/xmltree.c index 7dd7b6a3..e2654579 100644 --- a/lib/xmltree.c +++ b/lib/xmltree.c @@ -329,16 +329,16 @@ void xt_print( struct xt_node *node ) /* Indentation */ for( c = node; c->parent; c = c->parent ) - printf( " " ); + fprintf( stderr, " " ); /* Start the tag */ - printf( "<%s", node->name ); + fprintf( stderr, "<%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 ); + fprintf( stderr, " %s=\"%s\"", node->attr[i].key, v ); g_free( v ); } @@ -347,13 +347,13 @@ void xt_print( struct xt_node *node ) /* If this tag doesn't have any content at all... */ if( node->text == NULL && node->children == NULL ) { - printf( "/>\n" ); + fprintf( stderr, "/>\n" ); return; /* Then we're finished! */ } /* Otherwise... */ - printf( ">" ); + fprintf( stderr, ">" ); /* Only print the text if it contains more than whitespace (TEST). */ if( node->text_len > 0 ) @@ -362,23 +362,23 @@ void xt_print( struct xt_node *node ) if( node->text[i] ) { char *v = g_markup_escape_text( node->text, -1 ); - printf( "%s", v ); + fprintf( stderr, "%s", v ); g_free( v ); } } if( node->children ) - printf( "\n" ); + fprintf( stderr, "\n" ); for( c = node->children; c; c = c->next ) xt_print( c ); if( node->children ) for( c = node; c->parent; c = c->parent ) - printf( " " ); + fprintf( stderr, " " ); /* Non-empty tag is now finished. */ - printf( "\n", node->name ); + fprintf( stderr, "\n", node->name ); } struct xt_node *xt_dup( struct xt_node *node ) -- cgit v1.2.3 From 3f808ca58e45f2a305e01471cc514957039a865d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Dec 2011 16:38:02 +0000 Subject: Support HTTP/1.1 redirect status codes and use HTTPS for OAuth setup. This is required for identi.ca and really should be done for Twitter as well. Twitter OAuth is still broken though, it seems to disagree about signatures. --- lib/http_client.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/http_client.c b/lib/http_client.c index 56a6f376..9d986412 100644 --- a/lib/http_client.c +++ b/lib/http_client.c @@ -69,6 +69,9 @@ struct http_request *http_dorequest( char *host, int port, int ssl, char *reques req->request_length = strlen( request ); req->redir_ttl = 3; + if( getenv( "BITLBEE_DEBUG" ) ) + printf( "About to send HTTP request:\n%s\n", req->request ); + return( req ); } @@ -279,7 +282,7 @@ got_reply: *end1 = 0; if( getenv( "BITLBEE_DEBUG" ) ) - printf( "HTTP response headers:\n%s", req->reply_headers ); + printf( "HTTP response headers:\n%s\n", req->reply_headers ); if( evil_server ) req->reply_body = end1 + 1; @@ -319,7 +322,8 @@ got_reply: req->status_code = -1; } - if( ( req->status_code >= 301 && req->status_code <= 303 ) && req->redir_ttl-- > 0 ) + if( ( ( req->status_code >= 301 && req->status_code <= 303 ) || + req->status_code == 307 ) && req->redir_ttl-- > 0 ) { char *loc, *new_request, *new_host; int error = 0, new_port, new_proto; @@ -390,8 +394,20 @@ got_reply: /* More or less HTTP/1.0 compliant, from my reading of RFC 2616. Always perform a GET request unless we received a 301. 303 was meant for this but it's HTTP/1.1-only and we're specifically - speaking HTTP/1.0. */ - new_method = req->status_code != 301 || req->request[0] == 'G' ? "GET" : "POST"; + speaking HTTP/1.0. ... + + Well except someone at identi.ca's didn't bother reading any + RFCs and just return HTTP/1.1-specific status codes to HTTP/1.0 + requests. Fuckers. So here we are, handle 301..303,307. */ + if( strncmp( req->request, "GET", 3 ) == 0 ) + /* GETs never become POSTs. */ + new_method = "GET"; + else if( req->status_code == 302 || req->status_code == 303 ) + /* 302 de-facto becomes GET, 303 as specified by RFC 2616#10.3.3 */ + new_method = "GET"; + else + /* 301 de-facto should stay POST, 307 specifally RFC 2616#10.3.8 */ + new_method = "POST"; /* Okay, this isn't fun! We have to rebuild the request... :-( */ new_request = g_strdup_printf( "%s %s HTTP/1.0\r\nHost: %s%s", @@ -418,7 +434,7 @@ got_reply: req->ssl = NULL; if( getenv( "BITLBEE_DEBUG" ) ) - printf( "New headers for redirected HTTP request:\n%s", new_request ); + printf( "New headers for redirected HTTP request:\n%s\n", new_request ); if( new_proto == PROTO_HTTPS ) { @@ -462,7 +478,7 @@ cleanup: closesocket( req->fd ); if( getenv( "BITLBEE_DEBUG" ) && req ) - printf( "Finishing HTTP request with status: %s", + printf( "Finishing HTTP request with status: %s\n", req->status_string ? req->status_string : "NULL" ); req->func( req ); -- cgit v1.2.3