source: protocols/jabber/s5bytestream.c @ 1c7b646

Last change on this file since 1c7b646 was 2e8523b, checked in by dx <dx@…>, at 2016-12-31T20:40:09Z

Use NI_MAXHOST rather than HOST_NAME_MAX for host lengths.

This constant is always available and meant to be used with
getnameinfo().

This fixes the build on Debian GNU/kFreeBSD.

  • 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, ...) G_GNUC_PRINTF(2, 3);
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 = {0};
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 should not 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 necessary. 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[NI_MAXHOST + 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        g_free(proxysetting);
932}
933
934/*
935 * Starts a bytestream.
936 */
937gboolean jabber_bs_send_start(struct jabber_transfer *tf)
938{
939        struct bs_transfer *bt;
940
941        bt = g_new0(struct bs_transfer, 1);
942        bt->tf = tf;
943        bt->phase = BS_PHASE_CONNECT;
944        bt->pseudoaddr = generate_pseudoaddr(tf->sid, tf->ini_jid, tf->tgt_jid);
945        tf->streamhandle = bt;
946        tf->ft->free = jabber_bs_free_transfer;
947        tf->ft->canceled = jabber_bs_canceled;
948
949        jabber_si_set_proxies(bt);
950
951        return jabber_bs_send_request(tf, bt->streamhosts);
952}
953
954gboolean jabber_bs_send_request(struct jabber_transfer *tf, GSList *streamhosts)
955{
956        struct xt_node *shnode, *query, *iq;
957
958        query = xt_new_node("query", NULL, NULL);
959        xt_add_attr(query, "xmlns", XMLNS_BYTESTREAMS);
960        xt_add_attr(query, "sid", tf->sid);
961        xt_add_attr(query, "mode", "tcp");
962
963        while (streamhosts) {
964                jabber_streamhost_t *sh = streamhosts->data;
965                shnode = xt_new_node("streamhost", NULL, NULL);
966                xt_add_attr(shnode, "jid", sh->jid);
967                xt_add_attr(shnode, "host", sh->host);
968                xt_add_attr(shnode, "port", sh->port);
969
970                xt_add_child(query, shnode);
971
972                streamhosts = g_slist_next(streamhosts);
973        }
974
975
976        iq = jabber_make_packet("iq", "set", tf->tgt_jid, query);
977        xt_add_attr(iq, "from", tf->ini_jid);
978
979        jabber_cache_add(tf->ic, iq, jabber_bs_send_handle_reply);
980
981        if (!jabber_write_packet(tf->ic, iq)) {
982                imcb_file_canceled(tf->ic, tf->ft, "Error transmitting bytestream request");
983        }
984        return TRUE;
985}
986
987gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error)
988{
989        struct jabber_transfer *tf = bt->tf;
990        struct jabber_data *jd = tf->ic->proto_data;
991
992        /* TODO: did the receiver get here somehow??? */
993        imcb_log(tf->ic, "Transferring file %s: SOCKS5 handshake failed: %s",
994                 tf->ft->file_name,
995                 error);
996
997        if (jd->streamhosts == NULL) { /* we're done here unless we have a proxy to try */
998                imcb_file_canceled(tf->ic, tf->ft, error);
999        }
1000
1001        /* MUST always return FALSE! */
1002        return FALSE;
1003}
1004
1005/*
1006 * SOCKS5BYTESTREAM protocol for the sender
1007 */
1008gboolean jabber_bs_send_handshake(gpointer data, gint fd, b_input_condition cond)
1009{
1010        struct bs_transfer *bt = data;
1011        struct jabber_transfer *tf = bt->tf;
1012        short revents;
1013
1014        if (!jabber_bs_poll(bt, fd, &revents)) {
1015                return FALSE;
1016        }
1017
1018        switch (bt->phase) {
1019        case BS_PHASE_CONNECT:
1020        {
1021                struct sockaddr_storage clt_addr;
1022                socklen_t ssize = sizeof(clt_addr);
1023
1024                /* Connect */
1025
1026                ASSERTSOCKOP(tf->fd = accept(fd, (struct sockaddr *) &clt_addr, &ssize), "Accepting connection");
1027
1028                closesocket(fd);
1029                fd = tf->fd;
1030                sock_make_nonblocking(fd);
1031
1032                bt->phase = BS_PHASE_CONNECTED;
1033
1034                bt->tf->watch_in = b_input_add(fd, B_EV_IO_READ, jabber_bs_send_handshake, bt);
1035                return FALSE;
1036        }
1037        case BS_PHASE_CONNECTED:
1038        {
1039                int ret, have_noauth = FALSE;
1040                struct {
1041                        unsigned char ver;
1042                        unsigned char method;
1043                } socks5_auth_reply = { .ver = 5, .method = 0 };
1044                struct {
1045                        unsigned char ver;
1046                        unsigned char nmethods;
1047                        unsigned char method;
1048                } socks5_hello = {0};
1049
1050                if (!(ret = jabber_bs_peek(bt, &socks5_hello, sizeof(socks5_hello)))) {
1051                        return FALSE;
1052                }
1053
1054                if (ret < sizeof(socks5_hello)) {
1055                        return TRUE;
1056                }
1057
1058                if (!(socks5_hello.ver == 5) ||
1059                    !(socks5_hello.nmethods >= 1) ||
1060                    !(socks5_hello.nmethods < 32)) {
1061                        return jabber_bs_abort(bt, "Invalid auth request ver=%d nmethods=%d method=%d",
1062                                               socks5_hello.ver, socks5_hello.nmethods, socks5_hello.method);
1063                }
1064
1065                have_noauth = socks5_hello.method == 0;
1066
1067                if (socks5_hello.nmethods > 1) {
1068                        char mbuf[32];
1069                        int i;
1070                        ASSERTSOCKOP(ret = recv(fd, mbuf, socks5_hello.nmethods - 1, 0), "Receiving auth methods");
1071                        if (ret < (socks5_hello.nmethods - 1)) {
1072                                return jabber_bs_abort(bt, "Partial auth request");
1073                        }
1074                        for (i = 0; !have_noauth && (i < socks5_hello.nmethods - 1); i++) {
1075                                if (mbuf[i] == 0) {
1076                                        have_noauth = TRUE;
1077                                }
1078                        }
1079                }
1080
1081                if (!have_noauth) {
1082                        return jabber_bs_abort(bt, "Auth request didn't include no authentication");
1083                }
1084
1085                ASSERTSOCKOP(send(fd, &socks5_auth_reply, sizeof(socks5_auth_reply), 0), "Sending auth reply");
1086
1087                bt->phase = BS_PHASE_REQUEST;
1088
1089                return TRUE;
1090        }
1091        case BS_PHASE_REQUEST:
1092        {
1093                struct socks5_message socks5_connect = {0};
1094                int msgsize = sizeof(struct socks5_message);
1095                int ret;
1096
1097                if (!(ret = jabber_bs_peek(bt, &socks5_connect, msgsize))) {
1098                        return FALSE;
1099                }
1100
1101                if (ret < msgsize) {
1102                        return TRUE;
1103                }
1104
1105                if (!(socks5_connect.ver == 5) ||
1106                    !(socks5_connect.cmdrep.cmd == 1) ||
1107                    !(socks5_connect.atyp == 3) ||
1108                    !(socks5_connect.addrlen == 40)) {
1109                        return jabber_bs_abort(bt,
1110                                               "Invalid SOCKS5 Connect message (addrlen=%d, ver=%d, cmd=%d, atyp=%d)",
1111                                               socks5_connect.addrlen, socks5_connect.ver, socks5_connect.cmdrep.cmd,
1112                                               socks5_connect.atyp);
1113                }
1114                if (!(memcmp(socks5_connect.address, bt->pseudoaddr, 40) == 0)) {
1115                        return jabber_bs_abort(bt, "SOCKS5 Connect message contained wrong digest");
1116                }
1117
1118                socks5_connect.cmdrep.rep = 0;
1119
1120                ASSERTSOCKOP(send(fd, &socks5_connect, msgsize, 0), "Sending connect reply");
1121
1122                bt->phase = BS_PHASE_REPLY;
1123
1124                imcb_log(tf->ic, "File %s: SOCKS5 handshake successful! Transfer about to start...", tf->ft->file_name);
1125
1126                if (tf->accepted) {
1127                        /* streamhost-used message came already in(possible?), let's start sending */
1128                        tf->ft->write_request(tf->ft);
1129                }
1130
1131                tf->watch_in = 0;
1132                return FALSE;
1133
1134        }
1135        default:
1136                /* BUG */
1137                imcb_log(bt->tf->ic, "BUG in file transfer code: undefined handshake phase");
1138
1139                bt->tf->watch_in = 0;
1140                return FALSE;
1141        }
1142}
1143#undef ASSERTSOCKOP
Note: See TracBrowser for help on using the repository browser.