source: protocols/jabber/s5bytestream.c @ ef9ee0e

Last change on this file since ef9ee0e was 9216eff, checked in by dequis <dx@…>, at 2015-02-22T18:53:21Z

s5bytestream: fix segfault (cleanup before trying next streamhost)

This segfault happened when none of the available streamhosts can be
connected to - or if at least one of them fails to connect.

Before this commit, it can be reproduced reliably by setting the "proxy"
setting of the account to nonsense, for example, this is what i used:

proxy.example.org,1.2.3.4,7777;proxy.example.com,173.194.42.65,80

jabber_bs_recv_handshake_abort() calls jabber_bs_recv_handshake(), which
is supposed to restart the handshake with the next streamhost. And it
replaced bt->tf->watch_out, which held an event ID, with a newer event
ID. So the replaced event ID doesn't get removed, and it gets called
again when its socket is closed by the timeout - and by the time that
happens, the memory is free()'d already. Boom.

The patch is simple - created jabber_bs_remove_events() to cleanup those
events, and use it before any code that expects to restart the cycle.

So basically the same as doing b_event_remove(bt->tf->watch_out).

I hope there aren't more bugs like this in this code.

  • Property mode set to 100644
File size: 32.0 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Jabber module - SOCKS5 Bytestreams ( XEP-0065 )                          *
5*                                                                           *
6*  Copyright 2007 Uli Meis <a.sporto+bee@gmail.com>                         *
7*                                                                           *
8*  This program is free software; you can redistribute it and/or modify     *
9*  it under the terms of the GNU General Public License as published by     *
10*  the Free Software Foundation; either version 2 of the License, or        *
11*  (at your option) any later version.                                      *
12*                                                                           *
13*  This program is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
16*  GNU General Public License for more details.                             *
17*                                                                           *
18*  You should have received a copy of the GNU General Public License along  *
19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
21*                                                                           *
22\***************************************************************************/
23
24#include "jabber.h"
25#include "lib/ftutil.h"
26#include <poll.h>
27
28struct bs_transfer {
29
30        struct jabber_transfer *tf;
31
32        jabber_streamhost_t *sh;
33        GSList *streamhosts;
34
35        enum {
36                BS_PHASE_CONNECT,
37                BS_PHASE_CONNECTED,
38                BS_PHASE_REQUEST,
39                BS_PHASE_REPLY
40        } phase;
41
42        /* SHA1( SID + Initiator JID + Target JID) */
43        char *pseudoaddr;
44
45        gint connect_timeout;
46
47        char peek_buf[64];
48        int peek_buf_len;
49};
50
51struct socks5_message {
52        unsigned char ver;
53        union {
54                unsigned char cmd;
55                unsigned char rep;
56        } cmdrep;
57        unsigned char rsv;
58        unsigned char atyp;
59        unsigned char addrlen;
60        unsigned char address[40];
61        in_port_t port;
62} __attribute__ ((packed));
63
64char *socks5_reply_code[] = {
65        "succeeded",
66        "general SOCKS server failure",
67        "connection not allowed by ruleset",
68        "Network unreachable",
69        "Host unreachable",
70        "Connection refused",
71        "TTL expired",
72        "Command not supported",
73        "Address type not supported",
74        "unassigned"
75};
76
77/* connect() timeout in seconds. */
78#define JABBER_BS_CONTIMEOUT 15
79/* listen timeout */
80#define JABBER_BS_LISTEN_TIMEOUT  90
81
82/* very useful */
83#define ASSERTSOCKOP(op, msg) \
84        if ((op) == -1) { \
85                return jabber_bs_abort(bt, msg ": %s", strerror(errno)); }
86
87gboolean jabber_bs_abort(struct bs_transfer *bt, char *format, ...);
88void jabber_bs_canceled(file_transfer_t *ft, char *reason);
89void jabber_bs_free_transfer(file_transfer_t *ft);
90gboolean jabber_bs_connect_timeout(gpointer data, gint fd, b_input_condition cond);
91gboolean jabber_bs_poll(struct bs_transfer *bt, int fd, short *revents);
92gboolean jabber_bs_peek(struct bs_transfer *bt, void *buffer, int buflen);
93
94void jabber_bs_recv_answer_request(struct bs_transfer *bt);
95gboolean jabber_bs_recv_read(gpointer data, gint fd, b_input_condition cond);
96gboolean jabber_bs_recv_write_request(file_transfer_t *ft);
97gboolean jabber_bs_recv_handshake(gpointer data, gint fd, b_input_condition cond);
98gboolean jabber_bs_recv_handshake_abort(struct bs_transfer *bt, char *error);
99int jabber_bs_recv_request(struct im_connection *ic, struct xt_node *node, struct xt_node *qnode);
100
101gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error);
102gboolean jabber_bs_send_request(struct jabber_transfer *tf, GSList *streamhosts);
103gboolean jabber_bs_send_handshake(gpointer data, gint fd, b_input_condition cond);
104static xt_status jabber_bs_send_handle_activate(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
105void jabber_bs_send_activate(struct bs_transfer *bt);
106
107/*
108 * Frees a bs_transfer struct and calls the SI free function
109 */
110void jabber_bs_free_transfer(file_transfer_t *ft)
111{
112        struct jabber_transfer *tf = ft->data;
113        struct bs_transfer *bt = tf->streamhandle;
114        jabber_streamhost_t *sh;
115
116        if (bt->connect_timeout) {
117                b_event_remove(bt->connect_timeout);
118                bt->connect_timeout = 0;
119        }
120
121        if (tf->watch_in) {
122                b_event_remove(tf->watch_in);
123                tf->watch_in = 0;
124        }
125
126        if (tf->watch_out) {
127                b_event_remove(tf->watch_out);
128                tf->watch_out = 0;
129        }
130
131        g_free(bt->pseudoaddr);
132
133        while (bt->streamhosts) {
134                sh = bt->streamhosts->data;
135                bt->streamhosts = g_slist_remove(bt->streamhosts, sh);
136                g_free(sh->jid);
137                g_free(sh->host);
138                g_free(sh);
139        }
140
141        g_free(bt);
142
143        jabber_si_free_transfer(ft);
144}
145
146/*
147 * Checks if buflen data is available on the socket and
148 * writes it to buffer if that's the case.
149 */
150gboolean jabber_bs_peek(struct bs_transfer *bt, void *buffer, int buflen)
151{
152        int ret;
153        int fd = bt->tf->fd;
154
155        if (buflen > sizeof(bt->peek_buf)) {
156                return jabber_bs_abort(bt, "BUG: %d > sizeof(peek_buf)", buflen);
157        }
158
159        ASSERTSOCKOP(ret = recv(fd, bt->peek_buf + bt->peek_buf_len,
160                                buflen - bt->peek_buf_len, 0), "recv() on SOCKS5 connection");
161
162        if (ret == 0) {
163                return jabber_bs_abort(bt, "Remote end closed connection");
164        }
165
166        bt->peek_buf_len += ret;
167        memcpy(buffer, bt->peek_buf, bt->peek_buf_len);
168
169        if (bt->peek_buf_len == buflen) {
170                /* If we have everything the caller wanted, reset the peek buffer. */
171                bt->peek_buf_len = 0;
172                return buflen;
173        } else {
174                return bt->peek_buf_len;
175        }
176}
177
178
179/*
180 * This function is scheduled in bs_handshake via b_timeout_add after a (non-blocking) connect().
181 */
182gboolean jabber_bs_connect_timeout(gpointer data, gint fd, b_input_condition cond)
183{
184        struct bs_transfer *bt = data;
185
186        bt->connect_timeout = 0;
187
188        jabber_bs_abort(bt, "no connection after %d seconds",
189                        bt->tf->ft->sending ? JABBER_BS_LISTEN_TIMEOUT : JABBER_BS_CONTIMEOUT);
190
191        return FALSE;
192}
193
194/*
195 * Polls the socket, checks for errors and removes a connect timer
196 * if there is one.
197 */
198gboolean jabber_bs_poll(struct bs_transfer *bt, int fd, short *revents)
199{
200        struct pollfd pfd = { .fd = fd, .events = POLLHUP | POLLERR };
201
202        if (bt->connect_timeout) {
203                b_event_remove(bt->connect_timeout);
204                bt->connect_timeout = 0;
205        }
206
207        ASSERTSOCKOP(poll(&pfd, 1, 0), "poll()")
208
209        if (pfd.revents & POLLERR) {
210                int sockerror;
211                socklen_t errlen = sizeof(sockerror);
212
213                if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen)) {
214                        return jabber_bs_abort(bt,
215                                               "getsockopt() failed, unknown socket error during SOCKS5 handshake (weird!)");
216                }
217
218                if (bt->phase == BS_PHASE_CONNECTED) {
219                        return jabber_bs_abort(bt, "connect failed: %s", strerror(sockerror));
220                }
221
222                return jabber_bs_abort(bt, "Socket error during SOCKS5 handshake(weird!): %s", strerror(sockerror));
223        }
224
225        if (pfd.revents & POLLHUP) {
226                return jabber_bs_abort(bt, "Remote end closed connection");
227        }
228
229        *revents = pfd.revents;
230
231        return TRUE;
232}
233
234/*
235 * Used for receive and send path.
236 */
237gboolean jabber_bs_abort(struct bs_transfer *bt, char *format, ...)
238{
239        va_list params;
240
241        va_start(params, format);
242        char error[128];
243
244        if (vsnprintf(error, 128, format, params) < 0) {
245                sprintf(error, "internal error parsing error string (BUG)");
246        }
247        va_end(params);
248        if (bt->tf->ft->sending) {
249                return jabber_bs_send_handshake_abort(bt, error);
250        } else {
251                return jabber_bs_recv_handshake_abort(bt, error);
252        }
253}
254
255void jabber_bs_remove_events(struct bs_transfer *bt)
256{
257        struct jabber_transfer *tf = bt->tf;
258
259        if (tf->watch_out) {
260                b_event_remove(tf->watch_out);
261                tf->watch_out = 0;
262        }
263
264        if (tf->watch_in) {
265                b_event_remove(tf->watch_in);
266                tf->watch_in = 0;
267        }
268
269        if (tf->fd != -1) {
270                closesocket(tf->fd);
271                tf->fd = -1;
272        }
273
274        if (bt->connect_timeout) {
275                b_event_remove(bt->connect_timeout);
276                bt->connect_timeout = 0;
277        }
278}
279
280/* Bad luck */
281void jabber_bs_canceled(file_transfer_t *ft, char *reason)
282{
283        struct jabber_transfer *tf = ft->data;
284
285        imcb_log(tf->ic, "File transfer aborted: %s", reason);
286}
287
288static struct jabber_transfer *get_ft_by_sid(GSList *tflist, char *sid) {
289        GSList *l;
290        for (l = tflist; l; l = g_slist_next(l)) {
291                struct jabber_transfer *tft = l->data;
292                if ((strcmp(tft->sid, sid) == 0)) { 
293                        return tft;
294                }
295        }
296        return NULL;
297}
298
299/* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value
300 * Returns a newly allocated string */
301static char *generate_pseudoaddr(char *sid, char *ini_jid, char *tgt_jid) {
302        char *contents = g_strconcat(sid, ini_jid, tgt_jid, NULL);
303        char *hash_hex = g_compute_checksum_for_string(G_CHECKSUM_SHA1, contents, -1);
304        g_free(contents);
305        return hash_hex;
306}
307
308static jabber_streamhost_t *jabber_streamhost_new(char *jid, char *host, int port)
309{
310        jabber_streamhost_t *sh = g_new0(jabber_streamhost_t, 1);
311        sh->jid = g_strdup(jid);
312        sh->host = g_strdup(host);
313        if (port) {
314                g_snprintf(sh->port, sizeof(sh->port), "%u", port);
315        }
316        return sh;
317}
318
319/*
320 * Parses an incoming bytestream request and calls jabber_bs_handshake on success.
321 */
322int jabber_bs_recv_request(struct im_connection *ic, struct xt_node *node, struct xt_node *qnode)
323{
324        char *sid, *ini_jid, *tgt_jid, *mode, *iq_id;
325        struct jabber_data *jd = ic->proto_data;
326        struct jabber_transfer *tf = NULL;
327        struct bs_transfer *bt;
328        GSList *shlist = NULL;
329        struct xt_node *shnode;
330
331        if (!(iq_id   = xt_find_attr(node, "id")) ||
332            !(ini_jid = xt_find_attr(node, "from")) ||
333            !(tgt_jid = xt_find_attr(node, "to")) ||
334            !(sid     = xt_find_attr(qnode, "sid"))) {
335                imcb_log(ic, "WARNING: Received incomplete SI bytestream request");
336                return XT_HANDLED;
337        }
338
339        if ((mode = xt_find_attr(qnode, "mode")) &&
340            (strcmp(mode, "tcp") != 0)) {
341                imcb_log(ic, "WARNING: Received SI Request for unsupported bytestream mode %s",
342                         xt_find_attr(qnode, "mode"));
343                return XT_HANDLED;
344        }
345
346        shnode = qnode->children;
347        while ((shnode = xt_find_node(shnode, "streamhost"))) {
348                char *jid, *host, *port_s;
349                int port;
350                if ((jid = xt_find_attr(shnode, "jid")) &&
351                    (host = xt_find_attr(shnode, "host")) &&
352                    (port_s = xt_find_attr(shnode, "port")) &&
353                    (sscanf(port_s, "%d", &port) == 1)) {
354                        shlist = g_slist_append(shlist, jabber_streamhost_new(jid, host, port));
355                }
356                shnode = shnode->next;
357        }
358
359        if (!shlist) {
360                imcb_log(ic, "WARNING: Received incomplete SI bytestream request, no parseable streamhost entries");
361                return XT_HANDLED;
362        }
363
364        if (!(tf = get_ft_by_sid(jd->filetransfers, sid))) {
365                imcb_log(ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid);
366                return XT_HANDLED;
367        }
368
369        /* iq_id and canceled can be reused since SI is done */
370        g_free(tf->iq_id);
371        tf->iq_id = g_strdup(iq_id);
372
373        tf->ft->canceled = jabber_bs_canceled;
374
375        bt = g_new0(struct bs_transfer, 1);
376        bt->tf = tf;
377        bt->streamhosts = shlist;
378        bt->sh = shlist->data;
379        bt->phase = BS_PHASE_CONNECT;
380        bt->pseudoaddr = generate_pseudoaddr(sid, ini_jid, tgt_jid);
381        tf->streamhandle = bt;
382        tf->ft->free = jabber_bs_free_transfer;
383
384        jabber_bs_recv_handshake(bt, -1, 0);
385
386        return XT_HANDLED;
387}
388
389/*
390 * This is what a protocol handshake can look like in cooperative multitasking :)
391 * Might be confusing at first because it's called from different places and is recursing.
392 * (places being the event thread, bs_request, bs_handshake_abort, and itself)
393 *
394 * All in all, it turned out quite nice :)
395 */
396gboolean jabber_bs_recv_handshake(gpointer data, gint fd, b_input_condition cond)
397{
398
399        struct bs_transfer *bt = data;
400        short revents;
401        int gret;
402
403        if ((fd != -1) && !jabber_bs_poll(bt, fd, &revents)) {
404                return FALSE;
405        }
406
407        switch (bt->phase) {
408        case BS_PHASE_CONNECT:
409        {
410                struct addrinfo hints, *rp;
411
412                memset(&hints, 0, sizeof(struct addrinfo));
413                hints.ai_socktype = SOCK_STREAM;
414
415                if ((gret = getaddrinfo(bt->sh->host, bt->sh->port, &hints, &rp)) != 0) {
416                        return jabber_bs_abort(bt, "getaddrinfo() failed: %s", gai_strerror(gret));
417                }
418
419                ASSERTSOCKOP(bt->tf->fd = fd = socket(rp->ai_family, rp->ai_socktype, 0), "Opening socket");
420
421                sock_make_nonblocking(fd);
422
423                imcb_log(bt->tf->ic, "File %s: Connecting to streamhost %s:%s", bt->tf->ft->file_name, bt->sh->host,
424                         bt->sh->port);
425
426                if ((connect(fd, rp->ai_addr, rp->ai_addrlen) == -1) &&
427                    (errno != EINPROGRESS)) {
428                        return jabber_bs_abort(bt, "connect() failed: %s", strerror(errno));
429                }
430
431                freeaddrinfo(rp);
432
433                bt->phase = BS_PHASE_CONNECTED;
434
435                bt->tf->watch_out = b_input_add(fd, B_EV_IO_WRITE, jabber_bs_recv_handshake, bt);
436
437                /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */
438                bt->connect_timeout = b_timeout_add(JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt);
439
440                bt->tf->watch_in = 0;
441                return FALSE;
442        }
443        case BS_PHASE_CONNECTED:
444        {
445                struct {
446                        unsigned char ver;
447                        unsigned char nmethods;
448                        unsigned char method;
449                } socks5_hello = {
450                        .ver = 5,
451                        .nmethods = 1,
452                        .method = 0x00         /* no auth */
453                                  /* one could also implement username/password. If you know
454                                   * a jabber client or proxy that actually does it, tell me.
455                                   */
456                };
457
458                ASSERTSOCKOP(send(fd, &socks5_hello, sizeof(socks5_hello), 0), "Sending auth request");
459
460                bt->phase = BS_PHASE_REQUEST;
461
462                bt->tf->watch_in = b_input_add(fd, B_EV_IO_READ, jabber_bs_recv_handshake, bt);
463
464                bt->tf->watch_out = 0;
465                return FALSE;
466        }
467        case BS_PHASE_REQUEST:
468        {
469                struct socks5_message socks5_connect =
470                {
471                        .ver = 5,
472                        .cmdrep.cmd = 0x01,
473                        .rsv = 0,
474                        .atyp = 0x03,
475                        .addrlen = strlen(bt->pseudoaddr),
476                        .port = 0
477                };
478                int ret;
479                char buf[2];
480
481                /* If someone's trying to be funny and sends only one byte at a time we'll fail :) */
482                ASSERTSOCKOP(ret = recv(fd, buf, 2, 0), "Receiving auth reply");
483
484                if (!(ret == 2) ||
485                    !(buf[0] == 5) ||
486                    !(buf[1] == 0)) {
487                        return jabber_bs_abort(bt, "Auth not accepted by streamhost (reply: len=%d, ver=%d, status=%d)",
488                                               ret, buf[0], buf[1]);
489                }
490
491                /* copy hash into connect message */
492                memcpy(socks5_connect.address, bt->pseudoaddr, socks5_connect.addrlen);
493
494                ASSERTSOCKOP(send(fd, &socks5_connect, sizeof(struct socks5_message), 0), "Sending SOCKS5 Connect");
495
496                bt->phase = BS_PHASE_REPLY;
497
498                return TRUE;
499        }
500        case BS_PHASE_REPLY:
501        {
502                struct socks5_message socks5_reply;
503                int ret;
504
505                if (!(ret = jabber_bs_peek(bt, &socks5_reply, sizeof(struct socks5_message)))) {
506                        return FALSE;
507                }
508
509                if (ret < 5) {         /* header up to address length */
510                        return TRUE;
511                } else if (ret < sizeof(struct socks5_message)) {
512                        /* Either a buggy proxy or just one that doesnt regard
513                         * the SHOULD in XEP-0065 saying the reply SHOULD
514                         * contain the address. We'll take it, so make sure the
515                         * next jabber_bs_peek starts with an empty buffer. */
516                        bt->peek_buf_len = 0;
517                }
518
519                if (!(socks5_reply.ver == 5) ||
520                    !(socks5_reply.cmdrep.rep == 0)) {
521                        char errstr[128] = "";
522                        if ((socks5_reply.ver == 5) && (socks5_reply.cmdrep.rep <
523                                                        (sizeof(socks5_reply_code) / sizeof(socks5_reply_code[0])))) {
524                                sprintf(errstr, "with \"%s\" ", socks5_reply_code[ socks5_reply.cmdrep.rep ]);
525                        }
526                        return jabber_bs_abort(bt,
527                                               "SOCKS5 CONNECT failed %s(reply: ver=%d, rep=%d, atyp=%d, addrlen=%d)",
528                                               errstr,
529                                               socks5_reply.ver,
530                                               socks5_reply.cmdrep.rep,
531                                               socks5_reply.atyp,
532                                               socks5_reply.addrlen);
533                }
534
535                /* usually a proxy sends back the 40 bytes address but I encountered at least one (of jabber.cz)
536                 * that sends atyp=0 addrlen=0 and only 6 bytes (one less than one would expect).
537                 * Therefore I removed the wait for more bytes. Since we don't care about what else the proxy
538                 * is sending, it shouldnt matter */
539
540                if (bt->tf->ft->sending) {
541                        jabber_bs_send_activate(bt);
542                } else {
543                        jabber_bs_recv_answer_request(bt);
544                }
545
546                return FALSE;
547        }
548        default:
549                /* BUG */
550                imcb_log(bt->tf->ic, "BUG in file transfer code: undefined handshake phase");
551
552                bt->tf->watch_in = 0;
553                return FALSE;
554        }
555}
556
557/*
558 * If the handshake failed we can try the next streamhost, if there is one.
559 * An intelligent sender would probably specify himself as the first streamhost and
560 * a proxy as the second (Kopete and PSI are examples here). That way, a (potentially)
561 * slow proxy is only used if neccessary. This of course also means, that the timeout
562 * per streamhost should be kept short. If one or two firewalled adresses are specified,
563 * they have to timeout first before a proxy is tried.
564 */
565gboolean jabber_bs_recv_handshake_abort(struct bs_transfer *bt, char *error)
566{
567        struct jabber_transfer *tf = bt->tf;
568        struct xt_node *reply, *iqnode;
569        GSList *shlist;
570
571        imcb_log(tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)",
572                 tf->ft->file_name,
573                 bt->sh->host,
574                 bt->sh->port,
575                 error);
576
577        /* Alright, this streamhost failed, let's try the next... */
578        bt->phase = BS_PHASE_CONNECT;
579        shlist = g_slist_find(bt->streamhosts, bt->sh);
580        if (shlist && shlist->next) {
581                bt->sh = shlist->next->data;
582                jabber_bs_remove_events(bt);
583                return jabber_bs_recv_handshake(bt, -1, 0);
584        }
585
586
587        /* out of stream hosts */
588
589        iqnode = jabber_make_packet("iq", "result", tf->ini_jid, NULL);
590        reply = jabber_make_error_packet(iqnode, "item-not-found", "cancel", "404");
591        xt_free_node(iqnode);
592
593        xt_add_attr(reply, "id", tf->iq_id);
594
595        if (!jabber_write_packet(tf->ic, reply)) {
596                imcb_log(tf->ic, "WARNING: Error transmitting bytestream response");
597        }
598        xt_free_node(reply);
599
600        imcb_file_canceled(tf->ic, tf->ft, "couldn't connect to any streamhosts");
601
602        /* MUST always return FALSE! */
603        return FALSE;
604}
605
606/*
607 * After the SOCKS5 handshake succeeds we need to inform the initiator which streamhost we chose.
608 * If he is the streamhost himself, he might already know that. However, if it's a proxy,
609 * the initiator will have to make a connection himself.
610 */
611void jabber_bs_recv_answer_request(struct bs_transfer *bt)
612{
613        struct jabber_transfer *tf = bt->tf;
614        struct xt_node *reply;
615
616        imcb_log(tf->ic, "File %s: established SOCKS5 connection to %s:%s",
617                 tf->ft->file_name,
618                 bt->sh->host,
619                 bt->sh->port);
620
621        tf->ft->data = tf;
622        tf->watch_in = b_input_add(tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt);
623        tf->ft->write_request = jabber_bs_recv_write_request;
624
625        reply = xt_new_node("streamhost-used", NULL, NULL);
626        xt_add_attr(reply, "jid", bt->sh->jid);
627
628        reply = xt_new_node("query", NULL, reply);
629        xt_add_attr(reply, "xmlns", XMLNS_BYTESTREAMS);
630
631        reply = jabber_make_packet("iq", "result", tf->ini_jid, reply);
632
633        xt_add_attr(reply, "id", tf->iq_id);
634
635        if (!jabber_write_packet(tf->ic, reply)) {
636                imcb_file_canceled(tf->ic, tf->ft, "Error transmitting bytestream response");
637        }
638        xt_free_node(reply);
639}
640
641/*
642 * This function is called from write_request directly. If no data is available, it will install itself
643 * as a watcher for input on fd and once that happens, deliver the data and unschedule itself again.
644 */
645gboolean jabber_bs_recv_read(gpointer data, gint fd, b_input_condition cond)
646{
647        int ret;
648        struct bs_transfer *bt = data;
649        struct jabber_transfer *tf = bt->tf;
650
651        if (fd != -1) { /* called via event thread */
652                tf->watch_in = 0;
653                ASSERTSOCKOP(ret = recv(fd, tf->ft->buffer, sizeof(tf->ft->buffer), 0), "Receiving");
654        } else {
655                /* called directly. There might not be any data available. */
656                if (((ret = recv(tf->fd, tf->ft->buffer, sizeof(tf->ft->buffer), 0)) == -1) &&
657                    (errno != EAGAIN)) {
658                        return jabber_bs_abort(bt, "Receiving: %s", strerror(errno));
659                }
660
661                if ((ret == -1) && (errno == EAGAIN)) {
662                        tf->watch_in = b_input_add(tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt);
663                        return FALSE;
664                }
665        }
666
667        /* shouldn't happen since we know the file size */
668        if (ret == 0) {
669                return jabber_bs_abort(bt, "Remote end closed connection");
670        }
671
672        tf->bytesread += ret;
673
674        if (tf->bytesread >= tf->ft->file_size) {
675                imcb_file_finished(tf->ic, tf->ft);
676        }
677
678        tf->ft->write(tf->ft, tf->ft->buffer, ret);
679
680        return FALSE;
681}
682
683/*
684 * imc callback that is invoked when it is ready to receive some data.
685 */
686gboolean jabber_bs_recv_write_request(file_transfer_t *ft)
687{
688        struct jabber_transfer *tf = ft->data;
689
690        if (tf->watch_in) {
691                imcb_file_canceled(tf->ic, ft,
692                                   "BUG in jabber file transfer: write_request called when already watching for input");
693                return FALSE;
694        }
695
696        jabber_bs_recv_read(tf->streamhandle, -1, 0);
697
698        return TRUE;
699}
700
701/*
702 * Issues a write_request to imc.
703 * */
704gboolean jabber_bs_send_can_write(gpointer data, gint fd, b_input_condition cond)
705{
706        struct bs_transfer *bt = data;
707
708        bt->tf->watch_out = 0;
709
710        bt->tf->ft->write_request(bt->tf->ft);
711
712        return FALSE;
713}
714
715/*
716 * This should only be called if we can write, so just do it.
717 * Add a write watch so we can write more during the next cycle (if possible).
718 */
719gboolean jabber_bs_send_write(file_transfer_t *ft, char *buffer, unsigned int len)
720{
721        struct jabber_transfer *tf = ft->data;
722        struct bs_transfer *bt = tf->streamhandle;
723        int ret;
724
725        if (tf->watch_out) {
726                return jabber_bs_abort(bt, "BUG: write() called while watching ");
727        }
728
729        /* TODO: catch broken pipe */
730        ASSERTSOCKOP(ret = send(tf->fd, buffer, len, 0), "Sending");
731
732        tf->byteswritten += ret;
733
734        /* TODO: this should really not be fatal */
735        if (ret < len) {
736                return jabber_bs_abort(bt, "send() sent %d instead of %d (send buffer too big!)", ret, len);
737        }
738
739        if (tf->byteswritten >= ft->file_size) {
740                imcb_file_finished(tf->ic, ft);
741        } else {
742                bt->tf->watch_out = b_input_add(tf->fd, B_EV_IO_WRITE, jabber_bs_send_can_write, bt);
743        }
744
745        return TRUE;
746}
747
748/*
749 * Handles the reply by the receiver containing the used streamhost.
750 */
751static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
752{
753        struct jabber_transfer *tf = NULL;
754        struct jabber_data *jd = ic->proto_data;
755        struct bs_transfer *bt;
756        struct xt_node *c;
757        char *sid, *jid;
758
759        if (!(c = xt_find_node(node->children, "query")) ||
760            !(c = xt_find_node(c->children, "streamhost-used")) ||
761            !(jid = xt_find_attr(c, "jid"))) {
762                imcb_log(ic, "WARNING: Received incomplete bytestream reply");
763                return XT_HANDLED;
764        }
765
766        if (!(c = xt_find_node(orig->children, "query")) ||
767            !(sid = xt_find_attr(c, "sid"))) {
768                imcb_log(ic, "WARNING: Error parsing request corresponding to the incoming bytestream reply");
769                return XT_HANDLED;
770        }
771
772        /* Let's see if we can find out what this bytestream should be for... */
773
774        if (!(tf = get_ft_by_sid(jd->filetransfers, sid))) {
775                imcb_log(ic, "WARNING: Received SOCKS5 bytestream reply to unknown request");
776                return XT_HANDLED;
777        }
778
779        bt = tf->streamhandle;
780
781        tf->accepted = TRUE;
782
783        if (strcmp(jid, tf->ini_jid) == 0) {
784                /* we're streamhost and target */
785                if (bt->phase == BS_PHASE_REPLY) {
786                        /* handshake went through, let's start transferring */
787                        tf->ft->write_request(tf->ft);
788                }
789        } else {
790                /* using a proxy, abort listen */
791
792                jabber_bs_remove_events(bt);
793
794                GSList *shlist;
795                for (shlist = jd->streamhosts; shlist; shlist = g_slist_next(shlist)) {
796                        jabber_streamhost_t *sh = shlist->data;
797                        if (strcmp(sh->jid, jid) == 0) {
798                                bt->sh = sh;
799                                jabber_bs_recv_handshake(bt, -1, 0);
800                                return XT_HANDLED;
801                        }
802                }
803
804                imcb_log(ic, "WARNING: Received SOCKS5 bytestream reply with unknown streamhost %s", jid);
805        }
806
807        return XT_HANDLED;
808}
809
810/*
811 * Tell the proxy to activate the stream. Looks like this:
812 *
813 * <iq type=set>
814 *      <query xmlns=bs sid=sid>
815 *              <activate>tgt_jid</activate>
816 *      </query>
817 * </iq>
818 */
819void jabber_bs_send_activate(struct bs_transfer *bt)
820{
821        struct xt_node *node;
822
823        node = xt_new_node("activate", bt->tf->tgt_jid, NULL);
824        node = xt_new_node("query", NULL, node);
825        xt_add_attr(node, "xmlns", XMLNS_BYTESTREAMS);
826        xt_add_attr(node, "sid", bt->tf->sid);
827        node = jabber_make_packet("iq", "set", bt->sh->jid, node);
828
829        jabber_cache_add(bt->tf->ic, node, jabber_bs_send_handle_activate);
830
831        jabber_write_packet(bt->tf->ic, node);
832}
833
834/*
835 * The proxy has activated the bytestream.
836 * We can finally start pushing some data out.
837 */
838static xt_status jabber_bs_send_handle_activate(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
839{
840        char *sid;
841        struct jabber_transfer *tf = NULL;
842        struct xt_node *query;
843        struct jabber_data *jd = ic->proto_data;
844
845        query = xt_find_node(orig->children, "query");
846        sid = xt_find_attr(query, "sid");
847
848        if (!(tf = get_ft_by_sid(jd->filetransfers, sid))) {
849                imcb_log(ic, "WARNING: Received SOCKS5 bytestream activation for unknown stream");
850                return XT_HANDLED;
851        }
852
853        imcb_log(tf->ic, "File %s: SOCKS5 handshake and activation successful! Transfer about to start...",
854                 tf->ft->file_name);
855
856        /* handshake went through, let's start transferring */
857        tf->ft->write_request(tf->ft);
858
859        return XT_HANDLED;
860}
861
862jabber_streamhost_t *jabber_si_parse_proxy(struct im_connection *ic, char *proxy)
863{
864        char *host, *port, *jid;
865        jabber_streamhost_t *sh;
866
867        if (((host = strchr(proxy, ',')) == 0) ||
868            ((port = strchr(host + 1, ',')) == 0)) {
869                imcb_log(ic, "Error parsing proxy setting: \"%s\" (ignored)", proxy);
870                return NULL;
871        }
872
873        jid = proxy;
874        *host++ = '\0';
875        *port++ = '\0';
876
877        sh = jabber_streamhost_new(jid, host, 0);
878        strncpy(sh->port, port, sizeof(sh->port));
879
880        return sh;
881}
882
883void jabber_si_set_proxies(struct bs_transfer *bt)
884{
885        struct jabber_transfer *tf = bt->tf;
886        struct jabber_data *jd = tf->ic->proto_data;
887        char *proxysetting = g_strdup(set_getstr(&tf->ic->acc->set, "proxy"));
888        char *proxy, *next, *errmsg = NULL;
889        char port[6];
890        char host[HOST_NAME_MAX + 1];
891        jabber_streamhost_t *sh, *sh2;
892        GSList *streamhosts = jd->streamhosts;
893
894        proxy = proxysetting;
895        while (proxy && (*proxy != '\0')) {
896                if ((next = strchr(proxy, ';'))) {
897                        *next++ = '\0';
898                }
899
900                if (strcmp(proxy, "<local>") == 0) {
901                        if ((tf->fd = ft_listen(&tf->saddr, host, port, jd->fd, FALSE, &errmsg)) != -1) {
902                                sh = jabber_streamhost_new(tf->ini_jid, host, 0);
903                                strncpy(sh->port, port, sizeof(sh->port));
904                                bt->streamhosts = g_slist_append(bt->streamhosts, sh);
905
906                                bt->tf->watch_in = b_input_add(tf->fd, B_EV_IO_READ, jabber_bs_send_handshake, bt);
907                                bt->connect_timeout = b_timeout_add(JABBER_BS_LISTEN_TIMEOUT * 1000,
908                                                                    jabber_bs_connect_timeout, bt);
909                        } else {
910                                imcb_log(tf->ic,
911                                         "Transferring file %s: couldn't listen locally(non fatal, check your ft_listen setting in bitlbee.conf): %s",
912                                         tf->ft->file_name,
913                                         errmsg);
914                        }
915                } else if (strcmp(proxy, "<auto>") == 0) {
916                        while (streamhosts) {
917                                sh = g_new0(jabber_streamhost_t, 1);
918                                sh2 = streamhosts->data;
919                                sh->jid = g_strdup(sh2->jid);
920                                sh->host = g_strdup(sh2->host);
921                                strcpy(sh->port, sh2->port);
922                                bt->streamhosts = g_slist_append(bt->streamhosts, sh);
923                                streamhosts = g_slist_next(streamhosts);
924                        }
925                } else if ((sh = jabber_si_parse_proxy(tf->ic, proxy))) {
926                        bt->streamhosts = g_slist_append(bt->streamhosts, sh);
927                }
928                proxy = next;
929        }
930}
931
932/*
933 * Starts a bytestream.
934 */
935gboolean jabber_bs_send_start(struct jabber_transfer *tf)
936{
937        struct bs_transfer *bt;
938
939        bt = g_new0(struct bs_transfer, 1);
940        bt->tf = tf;
941        bt->phase = BS_PHASE_CONNECT;
942        bt->pseudoaddr = generate_pseudoaddr(tf->sid, tf->ini_jid, tf->tgt_jid);
943        tf->streamhandle = bt;
944        tf->ft->free = jabber_bs_free_transfer;
945        tf->ft->canceled = jabber_bs_canceled;
946
947        jabber_si_set_proxies(bt);
948
949        return jabber_bs_send_request(tf, bt->streamhosts);
950}
951
952gboolean jabber_bs_send_request(struct jabber_transfer *tf, GSList *streamhosts)
953{
954        struct xt_node *shnode, *query, *iq;
955
956        query = xt_new_node("query", NULL, NULL);
957        xt_add_attr(query, "xmlns", XMLNS_BYTESTREAMS);
958        xt_add_attr(query, "sid", tf->sid);
959        xt_add_attr(query, "mode", "tcp");
960
961        while (streamhosts) {
962                jabber_streamhost_t *sh = streamhosts->data;
963                shnode = xt_new_node("streamhost", NULL, NULL);
964                xt_add_attr(shnode, "jid", sh->jid);
965                xt_add_attr(shnode, "host", sh->host);
966                xt_add_attr(shnode, "port", sh->port);
967
968                xt_add_child(query, shnode);
969
970                streamhosts = g_slist_next(streamhosts);
971        }
972
973
974        iq = jabber_make_packet("iq", "set", tf->tgt_jid, query);
975        xt_add_attr(iq, "from", tf->ini_jid);
976
977        jabber_cache_add(tf->ic, iq, jabber_bs_send_handle_reply);
978
979        if (!jabber_write_packet(tf->ic, iq)) {
980                imcb_file_canceled(tf->ic, tf->ft, "Error transmitting bytestream request");
981        }
982        return TRUE;
983}
984
985gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error)
986{
987        struct jabber_transfer *tf = bt->tf;
988        struct jabber_data *jd = tf->ic->proto_data;
989
990        /* TODO: did the receiver get here somehow??? */
991        imcb_log(tf->ic, "Transferring file %s: SOCKS5 handshake failed: %s",
992                 tf->ft->file_name,
993                 error);
994
995        if (jd->streamhosts == NULL) { /* we're done here unless we have a proxy to try */
996                imcb_file_canceled(tf->ic, tf->ft, error);
997        }
998
999        /* MUST always return FALSE! */
1000        return FALSE;
1001}
1002
1003/*
1004 * SOCKS5BYTESTREAM protocol for the sender
1005 */
1006gboolean jabber_bs_send_handshake(gpointer data, gint fd, b_input_condition cond)
1007{
1008        struct bs_transfer *bt = data;
1009        struct jabber_transfer *tf = bt->tf;
1010        short revents;
1011
1012        if (!jabber_bs_poll(bt, fd, &revents)) {
1013                return FALSE;
1014        }
1015
1016        switch (bt->phase) {
1017        case BS_PHASE_CONNECT:
1018        {
1019                struct sockaddr_storage clt_addr;
1020                socklen_t ssize = sizeof(clt_addr);
1021
1022                /* Connect */
1023
1024                ASSERTSOCKOP(tf->fd = accept(fd, (struct sockaddr *) &clt_addr, &ssize), "Accepting connection");
1025
1026                closesocket(fd);
1027                fd = tf->fd;
1028                sock_make_nonblocking(fd);
1029
1030                bt->phase = BS_PHASE_CONNECTED;
1031
1032                bt->tf->watch_in = b_input_add(fd, B_EV_IO_READ, jabber_bs_send_handshake, bt);
1033                return FALSE;
1034        }
1035        case BS_PHASE_CONNECTED:
1036        {
1037                int ret, have_noauth = FALSE;
1038                struct {
1039                        unsigned char ver;
1040                        unsigned char method;
1041                } socks5_auth_reply = { .ver = 5, .method = 0 };
1042                struct {
1043                        unsigned char ver;
1044                        unsigned char nmethods;
1045                        unsigned char method;
1046                } socks5_hello;
1047
1048                if (!(ret = jabber_bs_peek(bt, &socks5_hello, sizeof(socks5_hello)))) {
1049                        return FALSE;
1050                }
1051
1052                if (ret < sizeof(socks5_hello)) {
1053                        return TRUE;
1054                }
1055
1056                if (!(socks5_hello.ver == 5) ||
1057                    !(socks5_hello.nmethods >= 1) ||
1058                    !(socks5_hello.nmethods < 32)) {
1059                        return jabber_bs_abort(bt, "Invalid auth request ver=%d nmethods=%d method=%d",
1060                                               socks5_hello.ver, socks5_hello.nmethods, socks5_hello.method);
1061                }
1062
1063                have_noauth = socks5_hello.method == 0;
1064
1065                if (socks5_hello.nmethods > 1) {
1066                        char mbuf[32];
1067                        int i;
1068                        ASSERTSOCKOP(ret = recv(fd, mbuf, socks5_hello.nmethods - 1, 0), "Receiving auth methods");
1069                        if (ret < (socks5_hello.nmethods - 1)) {
1070                                return jabber_bs_abort(bt, "Partial auth request");
1071                        }
1072                        for (i = 0; !have_noauth && (i < socks5_hello.nmethods - 1); i++) {
1073                                if (mbuf[i] == 0) {
1074                                        have_noauth = TRUE;
1075                                }
1076                        }
1077                }
1078
1079                if (!have_noauth) {
1080                        return jabber_bs_abort(bt, "Auth request didn't include no authentication");
1081                }
1082
1083                ASSERTSOCKOP(send(fd, &socks5_auth_reply, sizeof(socks5_auth_reply), 0), "Sending auth reply");
1084
1085                bt->phase = BS_PHASE_REQUEST;
1086
1087                return TRUE;
1088        }
1089        case BS_PHASE_REQUEST:
1090        {
1091                struct socks5_message socks5_connect;
1092                int msgsize = sizeof(struct socks5_message);
1093                int ret;
1094
1095                if (!(ret = jabber_bs_peek(bt, &socks5_connect, msgsize))) {
1096                        return FALSE;
1097                }
1098
1099                if (ret < msgsize) {
1100                        return TRUE;
1101                }
1102
1103                if (!(socks5_connect.ver == 5) ||
1104                    !(socks5_connect.cmdrep.cmd == 1) ||
1105                    !(socks5_connect.atyp == 3) ||
1106                    !(socks5_connect.addrlen == 40)) {
1107                        return jabber_bs_abort(bt,
1108                                               "Invalid SOCKS5 Connect message (addrlen=%d, ver=%d, cmd=%d, atyp=%d)",
1109                                               socks5_connect.addrlen, socks5_connect.ver, socks5_connect.cmdrep.cmd,
1110                                               socks5_connect.atyp);
1111                }
1112                if (!(memcmp(socks5_connect.address, bt->pseudoaddr, 40) == 0)) {
1113                        return jabber_bs_abort(bt, "SOCKS5 Connect message contained wrong digest");
1114                }
1115
1116                socks5_connect.cmdrep.rep = 0;
1117
1118                ASSERTSOCKOP(send(fd, &socks5_connect, msgsize, 0), "Sending connect reply");
1119
1120                bt->phase = BS_PHASE_REPLY;
1121
1122                imcb_log(tf->ic, "File %s: SOCKS5 handshake successful! Transfer about to start...", tf->ft->file_name);
1123
1124                if (tf->accepted) {
1125                        /* streamhost-used message came already in(possible?), let's start sending */
1126                        tf->ft->write_request(tf->ft);
1127                }
1128
1129                tf->watch_in = 0;
1130                return FALSE;
1131
1132        }
1133        default:
1134                /* BUG */
1135                imcb_log(bt->tf->ic, "BUG in file transfer code: undefined handshake phase");
1136
1137                bt->tf->watch_in = 0;
1138                return FALSE;
1139        }
1140}
1141#undef ASSERTSOCKOP
Note: See TracBrowser for help on using the repository browser.