diff options
Diffstat (limited to 'protocols/jabber/s5bytestream.c')
-rw-r--r-- | protocols/jabber/s5bytestream.c | 1182 |
1 files changed, 596 insertions, 586 deletions
diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 6b22d95f..27fb018b 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -33,11 +33,10 @@ struct bs_transfer { jabber_streamhost_t *sh; GSList *streamhosts; - enum - { - BS_PHASE_CONNECT, - BS_PHASE_CONNECTED, - BS_PHASE_REQUEST, + enum { + BS_PHASE_CONNECT, + BS_PHASE_CONNECTED, + BS_PHASE_REQUEST, BS_PHASE_REPLY } phase; @@ -45,16 +44,14 @@ struct bs_transfer { char *pseudoadr; gint connect_timeout; - + char peek_buf[64]; int peek_buf_len; }; -struct socks5_message -{ +struct socks5_message { unsigned char ver; - union - { + union { unsigned char cmd; unsigned char rep; } cmdrep; @@ -63,7 +60,7 @@ struct socks5_message unsigned char addrlen; unsigned char address[40]; in_port_t port; -} __attribute__ ((packed)); +} __attribute__ ((packed)); char *socks5_reply_code[] = { "succeeded", @@ -75,7 +72,8 @@ char *socks5_reply_code[] = { "TTL expired", "Command not supported", "Address type not supported", - "unassigned"}; + "unassigned" +}; /* connect() timeout in seconds. */ #define JABBER_BS_CONTIMEOUT 15 @@ -84,286 +82,284 @@ char *socks5_reply_code[] = { /* very useful */ #define ASSERTSOCKOP(op, msg) \ - if( (op) == -1 ) \ - return jabber_bs_abort( bt , msg ": %s", strerror( errno ) ); - -gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... ); -void jabber_bs_canceled( file_transfer_t *ft , char *reason ); -void jabber_bs_free_transfer( file_transfer_t *ft ); -gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond ); -gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents ); -gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen ); - -void jabber_bs_recv_answer_request( struct bs_transfer *bt ); -gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ); -gboolean jabber_bs_recv_write_request( file_transfer_t *ft ); -gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition cond ); -gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ); -int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode ); - -gboolean jabber_bs_send_handshake_abort( struct bs_transfer *bt, char *error ); -gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts ); -gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition cond ); -static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); -void jabber_bs_send_activate( struct bs_transfer *bt ); + if ((op) == -1) { \ + return jabber_bs_abort(bt, msg ": %s", strerror(errno)); } + +gboolean jabber_bs_abort(struct bs_transfer *bt, char *format, ...); +void jabber_bs_canceled(file_transfer_t *ft, char *reason); +void jabber_bs_free_transfer(file_transfer_t *ft); +gboolean jabber_bs_connect_timeout(gpointer data, gint fd, b_input_condition cond); +gboolean jabber_bs_poll(struct bs_transfer *bt, int fd, short *revents); +gboolean jabber_bs_peek(struct bs_transfer *bt, void *buffer, int buflen); + +void jabber_bs_recv_answer_request(struct bs_transfer *bt); +gboolean jabber_bs_recv_read(gpointer data, gint fd, b_input_condition cond); +gboolean jabber_bs_recv_write_request(file_transfer_t *ft); +gboolean jabber_bs_recv_handshake(gpointer data, gint fd, b_input_condition cond); +gboolean jabber_bs_recv_handshake_abort(struct bs_transfer *bt, char *error); +int jabber_bs_recv_request(struct im_connection *ic, struct xt_node *node, struct xt_node *qnode); + +gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error); +gboolean jabber_bs_send_request(struct jabber_transfer *tf, GSList *streamhosts); +gboolean jabber_bs_send_handshake(gpointer data, gint fd, b_input_condition cond); +static xt_status jabber_bs_send_handle_activate(struct im_connection *ic, struct xt_node *node, struct xt_node *orig); +void jabber_bs_send_activate(struct bs_transfer *bt); /* * Frees a bs_transfer struct and calls the SI free function */ -void jabber_bs_free_transfer( file_transfer_t *ft) { +void jabber_bs_free_transfer(file_transfer_t *ft) +{ struct jabber_transfer *tf = ft->data; struct bs_transfer *bt = tf->streamhandle; jabber_streamhost_t *sh; - if ( bt->connect_timeout ) - { - b_event_remove( bt->connect_timeout ); + if (bt->connect_timeout) { + b_event_remove(bt->connect_timeout); bt->connect_timeout = 0; } - if ( tf->watch_in ) - { - b_event_remove( tf->watch_in ); + if (tf->watch_in) { + b_event_remove(tf->watch_in); tf->watch_in = 0; } - - if( tf->watch_out ) - { - b_event_remove( tf->watch_out ); + + if (tf->watch_out) { + b_event_remove(tf->watch_out); tf->watch_out = 0; } - - g_free( bt->pseudoadr ); - while( bt->streamhosts ) - { + g_free(bt->pseudoadr); + + while (bt->streamhosts) { sh = bt->streamhosts->data; - bt->streamhosts = g_slist_remove( bt->streamhosts, sh ); - g_free( sh->jid ); - g_free( sh->host ); - g_free( sh ); + bt->streamhosts = g_slist_remove(bt->streamhosts, sh); + g_free(sh->jid); + g_free(sh->host); + g_free(sh); } - - g_free( bt ); - jabber_si_free_transfer( ft ); + g_free(bt); + + jabber_si_free_transfer(ft); } /* * Checks if buflen data is available on the socket and * writes it to buffer if that's the case. */ -gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen ) +gboolean jabber_bs_peek(struct bs_transfer *bt, void *buffer, int buflen) { int ret; int fd = bt->tf->fd; - if( buflen > sizeof( bt->peek_buf ) ) - return jabber_bs_abort( bt, "BUG: %d > sizeof(peek_buf)", buflen ); + if (buflen > sizeof(bt->peek_buf)) { + return jabber_bs_abort(bt, "BUG: %d > sizeof(peek_buf)", buflen); + } + + ASSERTSOCKOP(ret = recv(fd, bt->peek_buf + bt->peek_buf_len, + buflen - bt->peek_buf_len, 0), "recv() on SOCKS5 connection"); - ASSERTSOCKOP( ret = recv( fd, bt->peek_buf + bt->peek_buf_len, - buflen - bt->peek_buf_len, 0 ), "recv() on SOCKS5 connection" ); + if (ret == 0) { + return jabber_bs_abort(bt, "Remote end closed connection"); + } - if( ret == 0 ) - return jabber_bs_abort( bt, "Remote end closed connection" ); - bt->peek_buf_len += ret; - memcpy( buffer, bt->peek_buf, bt->peek_buf_len ); - - if( bt->peek_buf_len == buflen ) - { + memcpy(buffer, bt->peek_buf, bt->peek_buf_len); + + if (bt->peek_buf_len == buflen) { /* If we have everything the caller wanted, reset the peek buffer. */ bt->peek_buf_len = 0; return buflen; - } - else + } else { return bt->peek_buf_len; + } } -/* +/* * This function is scheduled in bs_handshake via b_timeout_add after a (non-blocking) connect(). */ -gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond ) +gboolean jabber_bs_connect_timeout(gpointer data, gint fd, b_input_condition cond) { struct bs_transfer *bt = data; bt->connect_timeout = 0; - jabber_bs_abort( bt, "no connection after %d seconds", bt->tf->ft->sending ? JABBER_BS_LISTEN_TIMEOUT : JABBER_BS_CONTIMEOUT ); + jabber_bs_abort(bt, "no connection after %d seconds", + bt->tf->ft->sending ? JABBER_BS_LISTEN_TIMEOUT : JABBER_BS_CONTIMEOUT); return FALSE; } -/* +/* * Polls the socket, checks for errors and removes a connect timer * if there is one. */ -gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents ) +gboolean jabber_bs_poll(struct bs_transfer *bt, int fd, short *revents) { - struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR }; - - if ( bt->connect_timeout ) - { - b_event_remove( bt->connect_timeout ); + struct pollfd pfd = { .fd = fd, .events = POLLHUP | POLLERR }; + + if (bt->connect_timeout) { + b_event_remove(bt->connect_timeout); bt->connect_timeout = 0; } - ASSERTSOCKOP( poll( &pfd, 1, 0 ), "poll()" ) + ASSERTSOCKOP(poll(&pfd, 1, 0), "poll()") - if( pfd.revents & POLLERR ) - { + if (pfd.revents & POLLERR) { int sockerror; - socklen_t errlen = sizeof( sockerror ); + socklen_t errlen = sizeof(sockerror); + + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen)) { + return jabber_bs_abort(bt, + "getsockopt() failed, unknown socket error during SOCKS5 handshake (weird!)"); + } - if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) ) - return jabber_bs_abort( bt, "getsockopt() failed, unknown socket error during SOCKS5 handshake (weird!)" ); + if (bt->phase == BS_PHASE_CONNECTED) { + return jabber_bs_abort(bt, "connect failed: %s", strerror(sockerror)); + } - if ( bt->phase == BS_PHASE_CONNECTED ) - return jabber_bs_abort( bt, "connect failed: %s", strerror( sockerror ) ); + return jabber_bs_abort(bt, "Socket error during SOCKS5 handshake(weird!): %s", strerror(sockerror)); + } - return jabber_bs_abort( bt, "Socket error during SOCKS5 handshake(weird!): %s", strerror( sockerror ) ); + if (pfd.revents & POLLHUP) { + return jabber_bs_abort(bt, "Remote end closed connection"); } - if( pfd.revents & POLLHUP ) - return jabber_bs_abort( bt, "Remote end closed connection" ); - *revents = pfd.revents; - + return TRUE; } /* * Used for receive and send path. */ -gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... ) +gboolean jabber_bs_abort(struct bs_transfer *bt, char *format, ...) { va_list params; - va_start( params, format ); + + va_start(params, format); char error[128]; - if( vsnprintf( error, 128, format, params ) < 0 ) - sprintf( error, "internal error parsing error string (BUG)" ); - va_end( params ); - if( bt->tf->ft->sending ) - return jabber_bs_send_handshake_abort( bt, error ); - else - return jabber_bs_recv_handshake_abort( bt, error ); + if (vsnprintf(error, 128, format, params) < 0) { + sprintf(error, "internal error parsing error string (BUG)"); + } + va_end(params); + if (bt->tf->ft->sending) { + return jabber_bs_send_handshake_abort(bt, error); + } else { + return jabber_bs_recv_handshake_abort(bt, error); + } } /* Bad luck */ -void jabber_bs_canceled( file_transfer_t *ft , char *reason ) +void jabber_bs_canceled(file_transfer_t *ft, char *reason) { struct jabber_transfer *tf = ft->data; - imcb_log( tf->ic, "File transfer aborted: %s", reason ); + imcb_log(tf->ic, "File transfer aborted: %s", reason); } /* * Parses an incoming bytestream request and calls jabber_bs_handshake on success. */ -int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode) +int jabber_bs_recv_request(struct im_connection *ic, struct xt_node *node, struct xt_node *qnode) { char *sid, *ini_jid, *tgt_jid, *mode, *iq_id; struct jabber_data *jd = ic->proto_data; struct jabber_transfer *tf = NULL; GSList *tflist; struct bs_transfer *bt; - GSList *shlist=NULL; + GSList *shlist = NULL; struct xt_node *shnode; sha1_state_t sha; char hash_hex[41]; unsigned char hash[20]; int i; - - if( !(iq_id = xt_find_attr( node, "id" ) ) || - !(ini_jid = xt_find_attr( node, "from" ) ) || - !(tgt_jid = xt_find_attr( node, "to" ) ) || - !(sid = xt_find_attr( qnode, "sid" ) ) ) - { - imcb_log( ic, "WARNING: Received incomplete SI bytestream request"); + + if (!(iq_id = xt_find_attr(node, "id")) || + !(ini_jid = xt_find_attr(node, "from")) || + !(tgt_jid = xt_find_attr(node, "to")) || + !(sid = xt_find_attr(qnode, "sid"))) { + imcb_log(ic, "WARNING: Received incomplete SI bytestream request"); return XT_HANDLED; } - if( ( mode = xt_find_attr( qnode, "mode" ) ) && - ( strcmp( mode, "tcp" ) != 0 ) ) - { - imcb_log( ic, "WARNING: Received SI Request for unsupported bytestream mode %s", xt_find_attr( qnode, "mode" ) ); + if ((mode = xt_find_attr(qnode, "mode")) && + (strcmp(mode, "tcp") != 0)) { + imcb_log(ic, "WARNING: Received SI Request for unsupported bytestream mode %s", + xt_find_attr(qnode, "mode")); return XT_HANDLED; } shnode = qnode->children; - while( ( shnode = xt_find_node( shnode, "streamhost" ) ) ) - { + while ((shnode = xt_find_node(shnode, "streamhost"))) { char *jid, *host, *port_s; int port; - if( ( jid = xt_find_attr( shnode, "jid" ) ) && - ( host = xt_find_attr( shnode, "host" ) ) && - ( port_s = xt_find_attr( shnode, "port" ) ) && - ( sscanf( port_s, "%d", &port ) == 1 ) ) - { - jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 ); + if ((jid = xt_find_attr(shnode, "jid")) && + (host = xt_find_attr(shnode, "host")) && + (port_s = xt_find_attr(shnode, "port")) && + (sscanf(port_s, "%d", &port) == 1)) { + jabber_streamhost_t *sh = g_new0(jabber_streamhost_t, 1); sh->jid = g_strdup(jid); sh->host = g_strdup(host); - sprintf( sh->port, "%u", port ); - shlist = g_slist_append( shlist, sh ); + sprintf(sh->port, "%u", port); + shlist = g_slist_append(shlist, sh); } shnode = shnode->next; } - - if( !shlist ) - { - imcb_log( ic, "WARNING: Received incomplete SI bytestream request, no parseable streamhost entries"); + + if (!shlist) { + imcb_log(ic, "WARNING: Received incomplete SI bytestream request, no parseable streamhost entries"); return XT_HANDLED; } /* Let's see if we can find out what this bytestream should be for... */ - for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) - { + for (tflist = jd->filetransfers; tflist; tflist = g_slist_next(tflist)) { struct jabber_transfer *tft = tflist->data; - if( ( strcmp( tft->sid, sid ) == 0 ) && - ( strcmp( tft->ini_jid, ini_jid ) == 0 ) && - ( strcmp( tft->tgt_jid, tgt_jid ) == 0 ) ) - { - tf = tft; + if ((strcmp(tft->sid, sid) == 0) && + (strcmp(tft->ini_jid, ini_jid) == 0) && + (strcmp(tft->tgt_jid, tgt_jid) == 0)) { + tf = tft; break; } } - if (!tf) - { - imcb_log( ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid ); + if (!tf) { + imcb_log(ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid); return XT_HANDLED; } /* iq_id and canceled can be reused since SI is done */ - g_free( tf->iq_id ); - tf->iq_id = g_strdup( iq_id ); + g_free(tf->iq_id); + tf->iq_id = g_strdup(iq_id); tf->ft->canceled = jabber_bs_canceled; /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ - sha1_init( &sha ); - sha1_append( &sha, (unsigned char*) sid, strlen( sid ) ); - sha1_append( &sha, (unsigned char*) ini_jid, strlen( ini_jid ) ); - sha1_append( &sha, (unsigned char*) tgt_jid, strlen( tgt_jid ) ); - sha1_finish( &sha, hash ); - - for( i = 0; i < 20; i ++ ) - sprintf( hash_hex + i * 2, "%02x", hash[i] ); - - bt = g_new0( struct bs_transfer, 1 ); + sha1_init(&sha); + sha1_append(&sha, (unsigned char *) sid, strlen(sid)); + sha1_append(&sha, (unsigned char *) ini_jid, strlen(ini_jid)); + sha1_append(&sha, (unsigned char *) tgt_jid, strlen(tgt_jid)); + sha1_finish(&sha, hash); + + for (i = 0; i < 20; i++) { + sprintf(hash_hex + i * 2, "%02x", hash[i]); + } + + bt = g_new0(struct bs_transfer, 1); bt->tf = tf; bt->streamhosts = shlist; bt->sh = shlist->data; bt->phase = BS_PHASE_CONNECT; - bt->pseudoadr = g_strdup( hash_hex ); + bt->pseudoadr = g_strdup(hash_hex); tf->streamhandle = bt; tf->ft->free = jabber_bs_free_transfer; - jabber_bs_recv_handshake( bt, -1, 0 ); + jabber_bs_recv_handshake(bt, -1, 0); return XT_HANDLED; } @@ -375,155 +371,161 @@ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, stru * * All in all, it turned out quite nice :) */ -gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition cond ) +gboolean jabber_bs_recv_handshake(gpointer data, gint fd, b_input_condition cond) { struct bs_transfer *bt = data; short revents; int gret; - if ( ( fd != -1 ) && !jabber_bs_poll( bt, fd, &revents ) ) + if ((fd != -1) && !jabber_bs_poll(bt, fd, &revents)) { return FALSE; - - switch( bt->phase ) - { + } + + switch (bt->phase) { case BS_PHASE_CONNECT: - { - struct addrinfo hints, *rp; + { + struct addrinfo hints, *rp; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_socktype = SOCK_STREAM; - memset( &hints, 0, sizeof( struct addrinfo ) ); - hints.ai_socktype = SOCK_STREAM; + if ((gret = getaddrinfo(bt->sh->host, bt->sh->port, &hints, &rp)) != 0) { + return jabber_bs_abort(bt, "getaddrinfo() failed: %s", gai_strerror(gret)); + } - if ( ( gret = getaddrinfo( bt->sh->host, bt->sh->port, &hints, &rp ) ) != 0 ) - return jabber_bs_abort( bt, "getaddrinfo() failed: %s", gai_strerror( gret ) ); + ASSERTSOCKOP(bt->tf->fd = fd = socket(rp->ai_family, rp->ai_socktype, 0), "Opening socket"); - ASSERTSOCKOP( bt->tf->fd = fd = socket( rp->ai_family, rp->ai_socktype, 0 ), "Opening socket" ); + sock_make_nonblocking(fd); - sock_make_nonblocking( fd ); + imcb_log(bt->tf->ic, "File %s: Connecting to streamhost %s:%s", bt->tf->ft->file_name, bt->sh->host, + bt->sh->port); - imcb_log( bt->tf->ic, "File %s: Connecting to streamhost %s:%s", bt->tf->ft->file_name, bt->sh->host, bt->sh->port ); + if ((connect(fd, rp->ai_addr, rp->ai_addrlen) == -1) && + (errno != EINPROGRESS)) { + return jabber_bs_abort(bt, "connect() failed: %s", strerror(errno)); + } - if( ( connect( fd, rp->ai_addr, rp->ai_addrlen ) == -1 ) && - ( errno != EINPROGRESS ) ) - return jabber_bs_abort( bt , "connect() failed: %s", strerror( errno ) ); + freeaddrinfo(rp); - freeaddrinfo( rp ); + bt->phase = BS_PHASE_CONNECTED; - bt->phase = BS_PHASE_CONNECTED; - - bt->tf->watch_out = b_input_add( fd, B_EV_IO_WRITE, jabber_bs_recv_handshake, bt ); + bt->tf->watch_out = b_input_add(fd, B_EV_IO_WRITE, jabber_bs_recv_handshake, bt); - /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ - bt->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt ); + /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ + bt->connect_timeout = b_timeout_add(JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt); - bt->tf->watch_in = 0; - return FALSE; - } + bt->tf->watch_in = 0; + return FALSE; + } case BS_PHASE_CONNECTED: + { + struct { + unsigned char ver; + unsigned char nmethods; + unsigned char method; + } socks5_hello = { + .ver = 5, + .nmethods = 1, + .method = 0x00 /* no auth */ + /* one could also implement username/password. If you know + * a jabber client or proxy that actually does it, tell me. + */ + }; + + ASSERTSOCKOP(send(fd, &socks5_hello, sizeof(socks5_hello), 0), "Sending auth request"); + + bt->phase = BS_PHASE_REQUEST; + + bt->tf->watch_in = b_input_add(fd, B_EV_IO_READ, jabber_bs_recv_handshake, bt); + + bt->tf->watch_out = 0; + return FALSE; + } + case BS_PHASE_REQUEST: + { + struct socks5_message socks5_connect = { - struct { - unsigned char ver; - unsigned char nmethods; - unsigned char method; - } socks5_hello = { - .ver = 5, - .nmethods = 1, - .method = 0x00 /* no auth */ - /* one could also implement username/password. If you know - * a jabber client or proxy that actually does it, tell me. - */ - }; - - ASSERTSOCKOP( send( fd, &socks5_hello, sizeof( socks5_hello ) , 0 ), "Sending auth request" ); - - bt->phase = BS_PHASE_REQUEST; - - bt->tf->watch_in = b_input_add( fd, B_EV_IO_READ, jabber_bs_recv_handshake, bt ); - - bt->tf->watch_out = 0; + .ver = 5, + .cmdrep.cmd = 0x01, + .rsv = 0, + .atyp = 0x03, + .addrlen = strlen(bt->pseudoadr), + .port = 0 + }; + int ret; + char buf[2]; + + /* If someone's trying to be funny and sends only one byte at a time we'll fail :) */ + ASSERTSOCKOP(ret = recv(fd, buf, 2, 0), "Receiving auth reply"); + + if (!(ret == 2) || + !(buf[0] == 5) || + !(buf[1] == 0)) { + return jabber_bs_abort(bt, "Auth not accepted by streamhost (reply: len=%d, ver=%d, status=%d)", + ret, buf[0], buf[1]); + } + + /* copy hash into connect message */ + memcpy(socks5_connect.address, bt->pseudoadr, socks5_connect.addrlen); + + ASSERTSOCKOP(send(fd, &socks5_connect, sizeof(struct socks5_message), 0), "Sending SOCKS5 Connect"); + + bt->phase = BS_PHASE_REPLY; + + return TRUE; + } + case BS_PHASE_REPLY: + { + struct socks5_message socks5_reply; + int ret; + + if (!(ret = jabber_bs_peek(bt, &socks5_reply, sizeof(struct socks5_message)))) { return FALSE; } - case BS_PHASE_REQUEST: - { - struct socks5_message socks5_connect = - { - .ver = 5, - .cmdrep.cmd = 0x01, - .rsv = 0, - .atyp = 0x03, - .addrlen = strlen( bt->pseudoadr ), - .port = 0 - }; - int ret; - char buf[2]; - - /* If someone's trying to be funny and sends only one byte at a time we'll fail :) */ - ASSERTSOCKOP( ret = recv( fd, buf, 2, 0 ) , "Receiving auth reply" ); - - if( !( ret == 2 ) || - !( buf[0] == 5 ) || - !( buf[1] == 0 ) ) - return jabber_bs_abort( bt, "Auth not accepted by streamhost (reply: len=%d, ver=%d, status=%d)", - ret, buf[0], buf[1] ); - - /* copy hash into connect message */ - memcpy( socks5_connect.address, bt->pseudoadr, socks5_connect.addrlen ); - - ASSERTSOCKOP( send( fd, &socks5_connect, sizeof( struct socks5_message ), 0 ) , "Sending SOCKS5 Connect" ); - - bt->phase = BS_PHASE_REPLY; + if (ret < 5) { /* header up to address length */ return TRUE; + } else if (ret < sizeof(struct socks5_message)) { + /* Either a buggy proxy or just one that doesnt regard + * the SHOULD in XEP-0065 saying the reply SHOULD + * contain the address. We'll take it, so make sure the + * next jabber_bs_peek starts with an empty buffer. */ + bt->peek_buf_len = 0; } - case BS_PHASE_REPLY: - { - struct socks5_message socks5_reply; - int ret; - - if ( !( ret = jabber_bs_peek( bt, &socks5_reply, sizeof( struct socks5_message ) ) ) ) - return FALSE; - - if ( ret < 5 ) /* header up to address length */ - return TRUE; - else if( ret < sizeof( struct socks5_message ) ) - { - /* Either a buggy proxy or just one that doesnt regard - * the SHOULD in XEP-0065 saying the reply SHOULD - * contain the address. We'll take it, so make sure the - * next jabber_bs_peek starts with an empty buffer. */ - bt->peek_buf_len = 0; - } - if( !( socks5_reply.ver == 5 ) || - !( socks5_reply.cmdrep.rep == 0 ) ) { - char errstr[128] = ""; - if( ( socks5_reply.ver == 5 ) && ( socks5_reply.cmdrep.rep < - ( sizeof( socks5_reply_code ) / sizeof( socks5_reply_code[0] ) ) ) ) { - sprintf( errstr, "with \"%s\" ", socks5_reply_code[ socks5_reply.cmdrep.rep ] ); - } - return jabber_bs_abort( bt, "SOCKS5 CONNECT failed %s(reply: ver=%d, rep=%d, atyp=%d, addrlen=%d)", - errstr, - socks5_reply.ver, - socks5_reply.cmdrep.rep, - socks5_reply.atyp, - socks5_reply.addrlen); + if (!(socks5_reply.ver == 5) || + !(socks5_reply.cmdrep.rep == 0)) { + char errstr[128] = ""; + if ((socks5_reply.ver == 5) && (socks5_reply.cmdrep.rep < + (sizeof(socks5_reply_code) / sizeof(socks5_reply_code[0])))) { + sprintf(errstr, "with \"%s\" ", socks5_reply_code[ socks5_reply.cmdrep.rep ]); } - - /* usually a proxy sends back the 40 bytes address but I encountered at least one (of jabber.cz) - * that sends atyp=0 addrlen=0 and only 6 bytes (one less than one would expect). - * Therefore I removed the wait for more bytes. Since we don't care about what else the proxy - * is sending, it shouldnt matter */ + return jabber_bs_abort(bt, + "SOCKS5 CONNECT failed %s(reply: ver=%d, rep=%d, atyp=%d, addrlen=%d)", + errstr, + socks5_reply.ver, + socks5_reply.cmdrep.rep, + socks5_reply.atyp, + socks5_reply.addrlen); + } - if( bt->tf->ft->sending ) - jabber_bs_send_activate( bt ); - else - jabber_bs_recv_answer_request( bt ); + /* usually a proxy sends back the 40 bytes address but I encountered at least one (of jabber.cz) + * that sends atyp=0 addrlen=0 and only 6 bytes (one less than one would expect). + * Therefore I removed the wait for more bytes. Since we don't care about what else the proxy + * is sending, it shouldnt matter */ - return FALSE; + if (bt->tf->ft->sending) { + jabber_bs_send_activate(bt); + } else { + jabber_bs_recv_answer_request(bt); } + + return FALSE; + } default: /* BUG */ - imcb_log( bt->tf->ic, "BUG in file transfer code: undefined handshake phase" ); + imcb_log(bt->tf->ic, "BUG in file transfer code: undefined handshake phase"); bt->tf->watch_in = 0; return FALSE; @@ -533,156 +535,156 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con /* * If the handshake failed we can try the next streamhost, if there is one. * An intelligent sender would probably specify himself as the first streamhost and - * a proxy as the second (Kopete and PSI are examples here). That way, a (potentially) + * a proxy as the second (Kopete and PSI are examples here). That way, a (potentially) * slow proxy is only used if neccessary. This of course also means, that the timeout * per streamhost should be kept short. If one or two firewalled adresses are specified, * they have to timeout first before a proxy is tried. */ -gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) +gboolean jabber_bs_recv_handshake_abort(struct bs_transfer *bt, char *error) { struct jabber_transfer *tf = bt->tf; struct xt_node *reply, *iqnode; GSList *shlist; - imcb_log( tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)", - tf->ft->file_name, - bt->sh->host, - bt->sh->port, - error ); + imcb_log(tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)", + tf->ft->file_name, + bt->sh->host, + bt->sh->port, + error); /* Alright, this streamhost failed, let's try the next... */ bt->phase = BS_PHASE_CONNECT; - shlist = g_slist_find( bt->streamhosts, bt->sh ); - if( shlist && shlist->next ) - { + shlist = g_slist_find(bt->streamhosts, bt->sh); + if (shlist && shlist->next) { bt->sh = shlist->next->data; - return jabber_bs_recv_handshake( bt, -1, 0 ); + return jabber_bs_recv_handshake(bt, -1, 0); } /* out of stream hosts */ - iqnode = jabber_make_packet( "iq", "result", tf->ini_jid, NULL ); - reply = jabber_make_error_packet( iqnode, "item-not-found", "cancel" , "404" ); - xt_free_node( iqnode ); + iqnode = jabber_make_packet("iq", "result", tf->ini_jid, NULL); + reply = jabber_make_error_packet(iqnode, "item-not-found", "cancel", "404"); + xt_free_node(iqnode); + + xt_add_attr(reply, "id", tf->iq_id); - xt_add_attr( reply, "id", tf->iq_id ); - - if( !jabber_write_packet( tf->ic, reply ) ) - imcb_log( tf->ic, "WARNING: Error transmitting bytestream response" ); - xt_free_node( reply ); + if (!jabber_write_packet(tf->ic, reply)) { + imcb_log(tf->ic, "WARNING: Error transmitting bytestream response"); + } + xt_free_node(reply); - imcb_file_canceled( tf->ic, tf->ft, "couldn't connect to any streamhosts" ); + imcb_file_canceled(tf->ic, tf->ft, "couldn't connect to any streamhosts"); /* MUST always return FALSE! */ return FALSE; } -/* +/* * After the SOCKS5 handshake succeeds we need to inform the initiator which streamhost we chose. * If he is the streamhost himself, he might already know that. However, if it's a proxy, * the initiator will have to make a connection himself. */ -void jabber_bs_recv_answer_request( struct bs_transfer *bt ) +void jabber_bs_recv_answer_request(struct bs_transfer *bt) { struct jabber_transfer *tf = bt->tf; struct xt_node *reply; - imcb_log( tf->ic, "File %s: established SOCKS5 connection to %s:%s", - tf->ft->file_name, - bt->sh->host, - bt->sh->port ); + imcb_log(tf->ic, "File %s: established SOCKS5 connection to %s:%s", + tf->ft->file_name, + bt->sh->host, + bt->sh->port); tf->ft->data = tf; - tf->watch_in = b_input_add( tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt ); + tf->watch_in = b_input_add(tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt); tf->ft->write_request = jabber_bs_recv_write_request; - reply = xt_new_node( "streamhost-used", NULL, NULL ); - xt_add_attr( reply, "jid", bt->sh->jid ); + reply = xt_new_node("streamhost-used", NULL, NULL); + xt_add_attr(reply, "jid", bt->sh->jid); + + reply = xt_new_node("query", NULL, reply); + xt_add_attr(reply, "xmlns", XMLNS_BYTESTREAMS); - reply = xt_new_node( "query", NULL, reply ); - xt_add_attr( reply, "xmlns", XMLNS_BYTESTREAMS ); + reply = jabber_make_packet("iq", "result", tf->ini_jid, reply); - reply = jabber_make_packet( "iq", "result", tf->ini_jid, reply ); + xt_add_attr(reply, "id", tf->iq_id); - xt_add_attr( reply, "id", tf->iq_id ); - - if( !jabber_write_packet( tf->ic, reply ) ) - imcb_file_canceled( tf->ic, tf->ft, "Error transmitting bytestream response" ); - xt_free_node( reply ); + if (!jabber_write_packet(tf->ic, reply)) { + imcb_file_canceled(tf->ic, tf->ft, "Error transmitting bytestream response"); + } + xt_free_node(reply); } -/* +/* * This function is called from write_request directly. If no data is available, it will install itself * as a watcher for input on fd and once that happens, deliver the data and unschedule itself again. */ -gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) +gboolean jabber_bs_recv_read(gpointer data, gint fd, b_input_condition cond) { int ret; struct bs_transfer *bt = data; struct jabber_transfer *tf = bt->tf; - if( fd != -1 ) /* called via event thread */ - { + if (fd != -1) { /* called via event thread */ tf->watch_in = 0; - ASSERTSOCKOP( ret = recv( fd, tf->ft->buffer, sizeof( tf->ft->buffer ), 0 ) , "Receiving" ); - } - else - { + ASSERTSOCKOP(ret = recv(fd, tf->ft->buffer, sizeof(tf->ft->buffer), 0), "Receiving"); + } else { /* called directly. There might not be any data available. */ - if( ( ( ret = recv( tf->fd, tf->ft->buffer, sizeof( tf->ft->buffer ), 0 ) ) == -1 ) && - ( errno != EAGAIN ) ) - return jabber_bs_abort( bt, "Receiving: %s", strerror( errno ) ); + if (((ret = recv(tf->fd, tf->ft->buffer, sizeof(tf->ft->buffer), 0)) == -1) && + (errno != EAGAIN)) { + return jabber_bs_abort(bt, "Receiving: %s", strerror(errno)); + } - if( ( ret == -1 ) && ( errno == EAGAIN ) ) - { - tf->watch_in = b_input_add( tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt ); + if ((ret == -1) && (errno == EAGAIN)) { + tf->watch_in = b_input_add(tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt); return FALSE; } } /* shouldn't happen since we know the file size */ - if( ret == 0 ) - return jabber_bs_abort( bt, "Remote end closed connection" ); - + if (ret == 0) { + return jabber_bs_abort(bt, "Remote end closed connection"); + } + tf->bytesread += ret; - if( tf->bytesread >= tf->ft->file_size ) - imcb_file_finished( tf->ic, tf->ft ); + if (tf->bytesread >= tf->ft->file_size) { + imcb_file_finished(tf->ic, tf->ft); + } - tf->ft->write( tf->ft, tf->ft->buffer, ret ); + tf->ft->write(tf->ft, tf->ft->buffer, ret); return FALSE; } -/* +/* * imc callback that is invoked when it is ready to receive some data. */ -gboolean jabber_bs_recv_write_request( file_transfer_t *ft ) +gboolean jabber_bs_recv_write_request(file_transfer_t *ft) { struct jabber_transfer *tf = ft->data; - if( tf->watch_in ) - { - imcb_file_canceled( tf->ic, ft, "BUG in jabber file transfer: write_request called when already watching for input" ); + if (tf->watch_in) { + imcb_file_canceled(tf->ic, ft, + "BUG in jabber file transfer: write_request called when already watching for input"); return FALSE; } - - jabber_bs_recv_read( tf->streamhandle, -1 , 0 ); + + jabber_bs_recv_read(tf->streamhandle, -1, 0); return TRUE; } -/* +/* * Issues a write_request to imc. * */ -gboolean jabber_bs_send_can_write( gpointer data, gint fd, b_input_condition cond ) +gboolean jabber_bs_send_can_write(gpointer data, gint fd, b_input_condition cond) { struct bs_transfer *bt = data; bt->tf->watch_out = 0; - bt->tf->ft->write_request( bt->tf->ft ); + bt->tf->ft->write_request(bt->tf->ft); return FALSE; } @@ -691,36 +693,40 @@ gboolean jabber_bs_send_can_write( gpointer data, gint fd, b_input_condition con * This should only be called if we can write, so just do it. * Add a write watch so we can write more during the next cycle (if possible). */ -gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int len ) +gboolean jabber_bs_send_write(file_transfer_t *ft, char *buffer, unsigned int len) { struct jabber_transfer *tf = ft->data; struct bs_transfer *bt = tf->streamhandle; int ret; - if( tf->watch_out ) - return jabber_bs_abort( bt, "BUG: write() called while watching " ); - + if (tf->watch_out) { + return jabber_bs_abort(bt, "BUG: write() called while watching "); + } + /* TODO: catch broken pipe */ - ASSERTSOCKOP( ret = send( tf->fd, buffer, len, 0 ), "Sending" ); + ASSERTSOCKOP(ret = send(tf->fd, buffer, len, 0), "Sending"); tf->byteswritten += ret; - + /* TODO: this should really not be fatal */ - if( ret < len ) - return jabber_bs_abort( bt, "send() sent %d instead of %d (send buffer too big!)", ret, len ); - - if( tf->byteswritten >= ft->file_size ) - imcb_file_finished( tf->ic, ft ); - else - bt->tf->watch_out = b_input_add( tf->fd, B_EV_IO_WRITE, jabber_bs_send_can_write, bt ); - + if (ret < len) { + return jabber_bs_abort(bt, "send() sent %d instead of %d (send buffer too big!)", ret, len); + } + + if (tf->byteswritten >= ft->file_size) { + imcb_file_finished(tf->ic, ft); + } else { + bt->tf->watch_out = b_input_add(tf->fd, B_EV_IO_WRITE, jabber_bs_send_can_write, bt); + } + return TRUE; } /* * Handles the reply by the receiver containing the used streamhost. */ -static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { +static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt_node *node, struct xt_node *orig) +{ struct jabber_transfer *tf = NULL; struct jabber_data *jd = ic->proto_data; struct bs_transfer *bt; @@ -728,37 +734,31 @@ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt struct xt_node *c; char *sid, *jid; - if( !( c = xt_find_node( node->children, "query" ) ) || - !( c = xt_find_node( c->children, "streamhost-used" ) ) || - !( jid = xt_find_attr( c, "jid" ) ) ) - - { - imcb_log( ic, "WARNING: Received incomplete bytestream reply" ); + if (!(c = xt_find_node(node->children, "query")) || + !(c = xt_find_node(c->children, "streamhost-used")) || + !(jid = xt_find_attr(c, "jid"))) { + imcb_log(ic, "WARNING: Received incomplete bytestream reply"); return XT_HANDLED; } - - if( !( c = xt_find_node( orig->children, "query" ) ) || - !( sid = xt_find_attr( c, "sid" ) ) ) - { - imcb_log( ic, "WARNING: Error parsing request corresponding to the incoming bytestream reply" ); + + if (!(c = xt_find_node(orig->children, "query")) || + !(sid = xt_find_attr(c, "sid"))) { + imcb_log(ic, "WARNING: Error parsing request corresponding to the incoming bytestream reply"); return XT_HANDLED; } /* Let's see if we can find out what this bytestream should be for... */ - for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) - { + for (tflist = jd->filetransfers; tflist; tflist = g_slist_next(tflist)) { struct jabber_transfer *tft = tflist->data; - if( ( strcmp( tft->sid, sid ) == 0 ) ) - { - tf = tft; + if ((strcmp(tft->sid, sid) == 0)) { + tf = tft; break; } } - if( !tf ) - { - imcb_log( ic, "WARNING: Received SOCKS5 bytestream reply to unknown request" ); + if (!tf) { + imcb_log(ic, "WARNING: Received SOCKS5 bytestream reply to unknown request"); return XT_HANDLED; } @@ -766,82 +766,75 @@ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt tf->accepted = TRUE; - if( strcmp( jid, tf->ini_jid ) == 0 ) - { + if (strcmp(jid, tf->ini_jid) == 0) { /* we're streamhost and target */ - if( bt->phase == BS_PHASE_REPLY ) - { + if (bt->phase == BS_PHASE_REPLY) { /* handshake went through, let's start transferring */ - tf->ft->write_request( tf->ft ); + tf->ft->write_request(tf->ft); } - } else - { + } else { /* using a proxy, abort listen */ - if( tf->watch_in ) - { - b_event_remove( tf->watch_in ); + if (tf->watch_in) { + b_event_remove(tf->watch_in); tf->watch_in = 0; } - - if( tf->fd != -1 ) { - closesocket( tf->fd ); + + if (tf->fd != -1) { + closesocket(tf->fd); tf->fd = -1; } - if ( bt->connect_timeout ) - { - b_event_remove( bt->connect_timeout ); + if (bt->connect_timeout) { + b_event_remove(bt->connect_timeout); bt->connect_timeout = 0; } GSList *shlist; - for( shlist = jd->streamhosts ; shlist ; shlist = g_slist_next( shlist ) ) - { + for (shlist = jd->streamhosts; shlist; shlist = g_slist_next(shlist)) { jabber_streamhost_t *sh = shlist->data; - if( strcmp( sh->jid, jid ) == 0 ) - { + if (strcmp(sh->jid, jid) == 0) { bt->sh = sh; - jabber_bs_recv_handshake( bt, -1, 0 ); + jabber_bs_recv_handshake(bt, -1, 0); return XT_HANDLED; } } - imcb_log( ic, "WARNING: Received SOCKS5 bytestream reply with unknown streamhost %s", jid ); + imcb_log(ic, "WARNING: Received SOCKS5 bytestream reply with unknown streamhost %s", jid); } return XT_HANDLED; } -/* +/* * Tell the proxy to activate the stream. Looks like this: * * <iq type=set> - * <query xmlns=bs sid=sid> - * <activate>tgt_jid</activate> - * </query> + * <query xmlns=bs sid=sid> + * <activate>tgt_jid</activate> + * </query> * </iq> */ -void jabber_bs_send_activate( struct bs_transfer *bt ) +void jabber_bs_send_activate(struct bs_transfer *bt) { struct xt_node *node; - node = xt_new_node( "activate", bt->tf->tgt_jid, NULL ); - node = xt_new_node( "query", NULL, node ); - xt_add_attr( node, "xmlns", XMLNS_BYTESTREAMS ); - xt_add_attr( node, "sid", bt->tf->sid ); - node = jabber_make_packet( "iq", "set", bt->sh->jid, node ); + node = xt_new_node("activate", bt->tf->tgt_jid, NULL); + node = xt_new_node("query", NULL, node); + xt_add_attr(node, "xmlns", XMLNS_BYTESTREAMS); + xt_add_attr(node, "sid", bt->tf->sid); + node = jabber_make_packet("iq", "set", bt->sh->jid, node); - jabber_cache_add( bt->tf->ic, node, jabber_bs_send_handle_activate ); + jabber_cache_add(bt->tf->ic, node, jabber_bs_send_handle_activate); - jabber_write_packet( bt->tf->ic, node ); + jabber_write_packet(bt->tf->ic, node); } /* * The proxy has activated the bytestream. * We can finally start pushing some data out. */ -static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) +static xt_status jabber_bs_send_handle_activate(struct im_connection *ic, struct xt_node *node, struct xt_node *orig) { char *sid; GSList *tflist; @@ -849,100 +842,101 @@ static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struc struct xt_node *query; struct jabber_data *jd = ic->proto_data; - query = xt_find_node( orig->children, "query" ); - sid = xt_find_attr( query, "sid" ); + query = xt_find_node(orig->children, "query"); + sid = xt_find_attr(query, "sid"); - for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) - { + for (tflist = jd->filetransfers; tflist; tflist = g_slist_next(tflist)) { struct jabber_transfer *tft = tflist->data; - if( ( strcmp( tft->sid, sid ) == 0 ) ) - { - tf = tft; + if ((strcmp(tft->sid, sid) == 0)) { + tf = tft; break; } } - if( !tf ) - { - imcb_log( ic, "WARNING: Received SOCKS5 bytestream activation for unknown stream" ); + if (!tf) { + imcb_log(ic, "WARNING: Received SOCKS5 bytestream activation for unknown stream"); return XT_HANDLED; } - imcb_log( tf->ic, "File %s: SOCKS5 handshake and activation successful! Transfer about to start...", tf->ft->file_name ); + imcb_log(tf->ic, "File %s: SOCKS5 handshake and activation successful! Transfer about to start...", + tf->ft->file_name); /* handshake went through, let's start transferring */ - tf->ft->write_request( tf->ft ); + tf->ft->write_request(tf->ft); return XT_HANDLED; } -jabber_streamhost_t *jabber_si_parse_proxy( struct im_connection *ic, char *proxy ) +jabber_streamhost_t *jabber_si_parse_proxy(struct im_connection *ic, char *proxy) { char *host, *port, *jid; jabber_streamhost_t *sh; - if( ( ( host = strchr( proxy, ',' ) ) == 0 ) || - ( ( port = strchr( host+1, ',' ) ) == 0 ) ) - { - imcb_log( ic, "Error parsing proxy setting: \"%s\" (ignored)", proxy ); + if (((host = strchr(proxy, ',')) == 0) || + ((port = strchr(host + 1, ',')) == 0)) { + imcb_log(ic, "Error parsing proxy setting: \"%s\" (ignored)", proxy); return NULL; } - + jid = proxy; *host++ = '\0'; *port++ = '\0'; - sh = g_new0( jabber_streamhost_t, 1 ); - sh->jid = g_strdup( jid ); - sh->host = g_strdup( host ); - g_snprintf( sh->port, sizeof( sh->port ), "%s", port ); + sh = g_new0(jabber_streamhost_t, 1); + sh->jid = g_strdup(jid); + sh->host = g_strdup(host); + g_snprintf(sh->port, sizeof(sh->port), "%s", port); return sh; } -void jabber_si_set_proxies( struct bs_transfer *bt ) +void jabber_si_set_proxies(struct bs_transfer *bt) { struct jabber_transfer *tf = bt->tf; struct jabber_data *jd = tf->ic->proto_data; - char *proxysetting = g_strdup ( set_getstr( &tf->ic->acc->set, "proxy" ) ); + char *proxysetting = g_strdup(set_getstr(&tf->ic->acc->set, "proxy")); char *proxy, *next, *errmsg = NULL; char port[6]; - char host[HOST_NAME_MAX+1]; + char host[HOST_NAME_MAX + 1]; jabber_streamhost_t *sh, *sh2; GSList *streamhosts = jd->streamhosts; proxy = proxysetting; - while ( proxy && ( *proxy!='\0' ) ) { - if( ( next = strchr( proxy, ';' ) ) ) - *next++ = '\0'; - - if( strcmp( proxy, "<local>" ) == 0 ) { - if( ( tf->fd = ft_listen( &tf->saddr, host, port, jd->fd, FALSE, &errmsg ) ) != -1 ) { - sh = g_new0( jabber_streamhost_t, 1 ); - sh->jid = g_strdup( tf->ini_jid ); - sh->host = g_strdup( host ); - g_snprintf( sh->port, sizeof( sh->port ), "%s", port ); - bt->streamhosts = g_slist_append( bt->streamhosts, sh ); - - bt->tf->watch_in = b_input_add( tf->fd, B_EV_IO_READ, jabber_bs_send_handshake, bt ); - bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); + while (proxy && (*proxy != '\0')) { + if ((next = strchr(proxy, ';'))) { + *next++ = '\0'; + } + + if (strcmp(proxy, "<local>") == 0) { + if ((tf->fd = ft_listen(&tf->saddr, host, port, jd->fd, FALSE, &errmsg)) != -1) { + sh = g_new0(jabber_streamhost_t, 1); + sh->jid = g_strdup(tf->ini_jid); + sh->host = g_strdup(host); + g_snprintf(sh->port, sizeof(sh->port), "%s", port); + bt->streamhosts = g_slist_append(bt->streamhosts, sh); + + bt->tf->watch_in = b_input_add(tf->fd, B_EV_IO_READ, jabber_bs_send_handshake, bt); + bt->connect_timeout = b_timeout_add(JABBER_BS_LISTEN_TIMEOUT * 1000, + jabber_bs_connect_timeout, bt); } else { - imcb_log( tf->ic, "Transferring file %s: couldn't listen locally(non fatal, check your ft_listen setting in bitlbee.conf): %s", - tf->ft->file_name, - errmsg ); + imcb_log(tf->ic, + "Transferring file %s: couldn't listen locally(non fatal, check your ft_listen setting in bitlbee.conf): %s", + tf->ft->file_name, + errmsg); } - } else if( strcmp( proxy, "<auto>" ) == 0 ) { - while ( streamhosts ) { - sh = g_new0( jabber_streamhost_t, 1 ); + } else if (strcmp(proxy, "<auto>") == 0) { + while (streamhosts) { + sh = g_new0(jabber_streamhost_t, 1); sh2 = streamhosts->data; - sh->jid = g_strdup( sh2->jid ); - sh->host = g_strdup( sh2->host ); - strcpy( sh->port, sh2->port ); - bt->streamhosts = g_slist_append( bt->streamhosts, sh ); - streamhosts = g_slist_next( streamhosts ); + sh->jid = g_strdup(sh2->jid); + sh->host = g_strdup(sh2->host); + strcpy(sh->port, sh2->port); + bt->streamhosts = g_slist_append(bt->streamhosts, sh); + streamhosts = g_slist_next(streamhosts); } - } else if( ( sh = jabber_si_parse_proxy( tf->ic, proxy ) ) ) - bt->streamhosts = g_slist_append( bt->streamhosts, sh ); + } else if ((sh = jabber_si_parse_proxy(tf->ic, proxy))) { + bt->streamhosts = g_slist_append(bt->streamhosts, sh); + } proxy = next; } } @@ -950,83 +944,86 @@ void jabber_si_set_proxies( struct bs_transfer *bt ) /* * Starts a bytestream. */ -gboolean jabber_bs_send_start( struct jabber_transfer *tf ) +gboolean jabber_bs_send_start(struct jabber_transfer *tf) { struct bs_transfer *bt; sha1_state_t sha; char hash_hex[41]; unsigned char hash[20]; - int i,ret; + int i, ret; /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ - sha1_init( &sha ); - sha1_append( &sha, (unsigned char*) tf->sid, strlen( tf->sid ) ); - sha1_append( &sha, (unsigned char*) tf->ini_jid, strlen( tf->ini_jid ) ); - sha1_append( &sha, (unsigned char*) tf->tgt_jid, strlen( tf->tgt_jid ) ); - sha1_finish( &sha, hash ); - - for( i = 0; i < 20; i ++ ) - sprintf( hash_hex + i * 2, "%02x", hash[i] ); - - bt = g_new0( struct bs_transfer, 1 ); + sha1_init(&sha); + sha1_append(&sha, (unsigned char *) tf->sid, strlen(tf->sid)); + sha1_append(&sha, (unsigned char *) tf->ini_jid, strlen(tf->ini_jid)); + sha1_append(&sha, (unsigned char *) tf->tgt_jid, strlen(tf->tgt_jid)); + sha1_finish(&sha, hash); + + for (i = 0; i < 20; i++) { + sprintf(hash_hex + i * 2, "%02x", hash[i]); + } + + bt = g_new0(struct bs_transfer, 1); bt->tf = tf; bt->phase = BS_PHASE_CONNECT; - bt->pseudoadr = g_strdup( hash_hex ); + bt->pseudoadr = g_strdup(hash_hex); tf->streamhandle = bt; tf->ft->free = jabber_bs_free_transfer; tf->ft->canceled = jabber_bs_canceled; - jabber_si_set_proxies( bt ); + jabber_si_set_proxies(bt); - ret = jabber_bs_send_request( tf, bt->streamhosts); + ret = jabber_bs_send_request(tf, bt->streamhosts); return ret; } -gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts ) +gboolean jabber_bs_send_request(struct jabber_transfer *tf, GSList *streamhosts) { struct xt_node *shnode, *query, *iq; - query = xt_new_node( "query", NULL, NULL ); - xt_add_attr( query, "xmlns", XMLNS_BYTESTREAMS ); - xt_add_attr( query, "sid", tf->sid ); - xt_add_attr( query, "mode", "tcp" ); + query = xt_new_node("query", NULL, NULL); + xt_add_attr(query, "xmlns", XMLNS_BYTESTREAMS); + xt_add_attr(query, "sid", tf->sid); + xt_add_attr(query, "mode", "tcp"); - while( streamhosts ) { + while (streamhosts) { jabber_streamhost_t *sh = streamhosts->data; - shnode = xt_new_node( "streamhost", NULL, NULL ); - xt_add_attr( shnode, "jid", sh->jid ); - xt_add_attr( shnode, "host", sh->host ); - xt_add_attr( shnode, "port", sh->port ); + shnode = xt_new_node("streamhost", NULL, NULL); + xt_add_attr(shnode, "jid", sh->jid); + xt_add_attr(shnode, "host", sh->host); + xt_add_attr(shnode, "port", sh->port); - xt_add_child( query, shnode ); + xt_add_child(query, shnode); - streamhosts = g_slist_next( streamhosts ); + streamhosts = g_slist_next(streamhosts); } - iq = jabber_make_packet( "iq", "set", tf->tgt_jid, query ); - xt_add_attr( iq, "from", tf->ini_jid ); + iq = jabber_make_packet("iq", "set", tf->tgt_jid, query); + xt_add_attr(iq, "from", tf->ini_jid); - jabber_cache_add( tf->ic, iq, jabber_bs_send_handle_reply ); + jabber_cache_add(tf->ic, iq, jabber_bs_send_handle_reply); - if( !jabber_write_packet( tf->ic, iq ) ) - imcb_file_canceled( tf->ic, tf->ft, "Error transmitting bytestream request" ); + if (!jabber_write_packet(tf->ic, iq)) { + imcb_file_canceled(tf->ic, tf->ft, "Error transmitting bytestream request"); + } return TRUE; } -gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) +gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error) { struct jabber_transfer *tf = bt->tf; struct jabber_data *jd = tf->ic->proto_data; /* TODO: did the receiver get here somehow??? */ - imcb_log( tf->ic, "Transferring file %s: SOCKS5 handshake failed: %s", - tf->ft->file_name, - error ); + imcb_log(tf->ic, "Transferring file %s: SOCKS5 handshake failed: %s", + tf->ft->file_name, + error); - if( jd->streamhosts==NULL ) /* we're done here unless we have a proxy to try */ - imcb_file_canceled( tf->ic, tf->ft, error ); + if (jd->streamhosts == NULL) { /* we're done here unless we have a proxy to try */ + imcb_file_canceled(tf->ic, tf->ft, error); + } /* MUST always return FALSE! */ return FALSE; @@ -1035,123 +1032,136 @@ gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) /* * SOCKS5BYTESTREAM protocol for the sender */ -gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition cond ) +gboolean jabber_bs_send_handshake(gpointer data, gint fd, b_input_condition cond) { struct bs_transfer *bt = data; struct jabber_transfer *tf = bt->tf; short revents; - if ( !jabber_bs_poll( bt, fd, &revents ) ) + if (!jabber_bs_poll(bt, fd, &revents)) { return FALSE; - - switch( bt->phase ) - { + } + + switch (bt->phase) { case BS_PHASE_CONNECT: - { - struct sockaddr_storage clt_addr; - socklen_t ssize = sizeof( clt_addr ); - - /* Connect */ + { + struct sockaddr_storage clt_addr; + socklen_t ssize = sizeof(clt_addr); - ASSERTSOCKOP( tf->fd = accept( fd, (struct sockaddr *) &clt_addr, &ssize ), "Accepting connection" ); + /* Connect */ - closesocket( fd ); - fd = tf->fd; - sock_make_nonblocking( fd ); - - bt->phase = BS_PHASE_CONNECTED; + ASSERTSOCKOP(tf->fd = accept(fd, (struct sockaddr *) &clt_addr, &ssize), "Accepting connection"); - bt->tf->watch_in = b_input_add( fd, B_EV_IO_READ, jabber_bs_send_handshake, bt ); + closesocket(fd); + fd = tf->fd; + sock_make_nonblocking(fd); + + bt->phase = BS_PHASE_CONNECTED; + + bt->tf->watch_in = b_input_add(fd, B_EV_IO_READ, jabber_bs_send_handshake, bt); + return FALSE; + } + case BS_PHASE_CONNECTED: + { + int ret, have_noauth = FALSE; + struct { + unsigned char ver; + unsigned char method; + } socks5_auth_reply = { .ver = 5, .method = 0 }; + struct { + unsigned char ver; + unsigned char nmethods; + unsigned char method; + } socks5_hello; + + if (!(ret = jabber_bs_peek(bt, &socks5_hello, sizeof(socks5_hello)))) { return FALSE; } - case BS_PHASE_CONNECTED: - { - int ret, have_noauth=FALSE; - struct { - unsigned char ver; - unsigned char method; - } socks5_auth_reply = { .ver = 5, .method = 0 }; - struct { - unsigned char ver; - unsigned char nmethods; - unsigned char method; - } socks5_hello; - - if( !( ret = jabber_bs_peek( bt, &socks5_hello, sizeof( socks5_hello ) ) ) ) - return FALSE; - - if( ret < sizeof( socks5_hello ) ) - return TRUE; - - if( !( socks5_hello.ver == 5 ) || - !( socks5_hello.nmethods >= 1 ) || - !( socks5_hello.nmethods < 32 ) ) - return jabber_bs_abort( bt, "Invalid auth request ver=%d nmethods=%d method=%d", socks5_hello.ver, socks5_hello.nmethods, socks5_hello.method ); - - have_noauth = socks5_hello.method == 0; - - if( socks5_hello.nmethods > 1 ) - { - char mbuf[32]; - int i; - ASSERTSOCKOP( ret = recv( fd, mbuf, socks5_hello.nmethods - 1, 0 ) , "Receiving auth methods" ); - if( ret < ( socks5_hello.nmethods - 1 ) ) - return jabber_bs_abort( bt, "Partial auth request"); - for( i = 0 ; !have_noauth && ( i < socks5_hello.nmethods - 1 ) ; i ++ ) - if( mbuf[i] == 0 ) - have_noauth = TRUE; - } - - if( !have_noauth ) - return jabber_bs_abort( bt, "Auth request didn't include no authentication" ); - ASSERTSOCKOP( send( fd, &socks5_auth_reply, sizeof( socks5_auth_reply ) , 0 ), "Sending auth reply" ); + if (ret < sizeof(socks5_hello)) { + return TRUE; + } - bt->phase = BS_PHASE_REQUEST; + if (!(socks5_hello.ver == 5) || + !(socks5_hello.nmethods >= 1) || + !(socks5_hello.nmethods < 32)) { + return jabber_bs_abort(bt, "Invalid auth request ver=%d nmethods=%d method=%d", + socks5_hello.ver, socks5_hello.nmethods, socks5_hello.method); + } - return TRUE; + have_noauth = socks5_hello.method == 0; + + if (socks5_hello.nmethods > 1) { + char mbuf[32]; + int i; + ASSERTSOCKOP(ret = recv(fd, mbuf, socks5_hello.nmethods - 1, 0), "Receiving auth methods"); + if (ret < (socks5_hello.nmethods - 1)) { + return jabber_bs_abort(bt, "Partial auth request"); + } + for (i = 0; !have_noauth && (i < socks5_hello.nmethods - 1); i++) { + if (mbuf[i] == 0) { + have_noauth = TRUE; + } + } } - case BS_PHASE_REQUEST: - { - struct socks5_message socks5_connect; - int msgsize = sizeof( struct socks5_message ); - int ret; - if( !( ret = jabber_bs_peek( bt, &socks5_connect, msgsize ) ) ) - return FALSE; + if (!have_noauth) { + return jabber_bs_abort(bt, "Auth request didn't include no authentication"); + } - if( ret < msgsize ) - return TRUE; + ASSERTSOCKOP(send(fd, &socks5_auth_reply, sizeof(socks5_auth_reply), 0), "Sending auth reply"); - if( !( socks5_connect.ver == 5) || - !( socks5_connect.cmdrep.cmd == 1 ) || - !( socks5_connect.atyp == 3 ) || - !(socks5_connect.addrlen == 40 ) ) - return jabber_bs_abort( bt, "Invalid SOCKS5 Connect message (addrlen=%d, ver=%d, cmd=%d, atyp=%d)", socks5_connect.addrlen, socks5_connect.ver, socks5_connect.cmdrep.cmd, socks5_connect.atyp ); - if( !( memcmp( socks5_connect.address, bt->pseudoadr, 40 ) == 0 ) ) - return jabber_bs_abort( bt, "SOCKS5 Connect message contained wrong digest"); + bt->phase = BS_PHASE_REQUEST; - socks5_connect.cmdrep.rep = 0; + return TRUE; + } + case BS_PHASE_REQUEST: + { + struct socks5_message socks5_connect; + int msgsize = sizeof(struct socks5_message); + int ret; + + if (!(ret = jabber_bs_peek(bt, &socks5_connect, msgsize))) { + return FALSE; + } + + if (ret < msgsize) { + return TRUE; + } - ASSERTSOCKOP( send( fd, &socks5_connect, msgsize, 0 ), "Sending connect reply" ); + if (!(socks5_connect.ver == 5) || + !(socks5_connect.cmdrep.cmd == 1) || + !(socks5_connect.atyp == 3) || + !(socks5_connect.addrlen == 40)) { + return jabber_bs_abort(bt, + "Invalid SOCKS5 Connect message (addrlen=%d, ver=%d, cmd=%d, atyp=%d)", + socks5_connect.addrlen, socks5_connect.ver, socks5_connect.cmdrep.cmd, + socks5_connect.atyp); + } + if (!(memcmp(socks5_connect.address, bt->pseudoadr, 40) == 0)) { + return jabber_bs_abort(bt, "SOCKS5 Connect message contained wrong digest"); + } - bt->phase = BS_PHASE_REPLY; + socks5_connect.cmdrep.rep = 0; - imcb_log( tf->ic, "File %s: SOCKS5 handshake successful! Transfer about to start...", tf->ft->file_name ); + ASSERTSOCKOP(send(fd, &socks5_connect, msgsize, 0), "Sending connect reply"); - if( tf->accepted ) - { - /* streamhost-used message came already in(possible?), let's start sending */ - tf->ft->write_request( tf->ft ); - } + bt->phase = BS_PHASE_REPLY; - tf->watch_in = 0; - return FALSE; + imcb_log(tf->ic, "File %s: SOCKS5 handshake successful! Transfer about to start...", tf->ft->file_name); + if (tf->accepted) { + /* streamhost-used message came already in(possible?), let's start sending */ + tf->ft->write_request(tf->ft); } + + tf->watch_in = 0; + return FALSE; + + } default: /* BUG */ - imcb_log( bt->tf->ic, "BUG in file transfer code: undefined handshake phase" ); + imcb_log(bt->tf->ic, "BUG in file transfer code: undefined handshake phase"); bt->tf->watch_in = 0; return FALSE; |