source: protocols/jabber/s5bytestream.c @ da6f167

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

s5bytestream: refactor some copypasted code into functions

  • get_ft_by_sid()
  • generate_pseudoaddr() (also uses g_compute_checksum_for_string() to make the code shorter)
  • jabber_streamhost_new()

Behavior should be the same.

  • Property mode set to 100644
File size: 31.7 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
255/* Bad luck */
256void jabber_bs_canceled(file_transfer_t *ft, char *reason)
257{
258        struct jabber_transfer *tf = ft->data;
259
260        imcb_log(tf->ic, "File transfer aborted: %s", reason);
261}
262
263static struct jabber_transfer *get_ft_by_sid(GSList *tflist, char *sid) {
264        GSList *l;
265        for (l = tflist; l; l = g_slist_next(l)) {
266                struct jabber_transfer *tft = l->data;
267                if ((strcmp(tft->sid, sid) == 0)) { 
268                        return tft;
269                }
270        }
271        return NULL;
272}
273
274/* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value
275 * Returns a newly allocated string */
276static char *generate_pseudoaddr(char *sid, char *ini_jid, char *tgt_jid) {
277        char *contents = g_strconcat(sid, ini_jid, tgt_jid, NULL);
278        char *hash_hex = g_compute_checksum_for_string(G_CHECKSUM_SHA1, contents, -1);
279        g_free(contents);
280        return hash_hex;
281}
282
283static jabber_streamhost_t *jabber_streamhost_new(char *jid, char *host, int port)
284{
285        jabber_streamhost_t *sh = g_new0(jabber_streamhost_t, 1);
286        sh->jid = g_strdup(jid);
287        sh->host = g_strdup(host);
288        if (port) {
289                g_snprintf(sh->port, sizeof(sh->port), "%u", port);
290        }
291        return sh;
292}
293
294/*
295 * Parses an incoming bytestream request and calls jabber_bs_handshake on success.
296 */
297int jabber_bs_recv_request(struct im_connection *ic, struct xt_node *node, struct xt_node *qnode)
298{
299        char *sid, *ini_jid, *tgt_jid, *mode, *iq_id;
300        struct jabber_data *jd = ic->proto_data;
301        struct jabber_transfer *tf = NULL;
302        struct bs_transfer *bt;
303        GSList *shlist = NULL;
304        struct xt_node *shnode;
305
306        if (!(iq_id   = xt_find_attr(node, "id")) ||
307            !(ini_jid = xt_find_attr(node, "from")) ||
308            !(tgt_jid = xt_find_attr(node, "to")) ||
309            !(sid     = xt_find_attr(qnode, "sid"))) {
310                imcb_log(ic, "WARNING: Received incomplete SI bytestream request");
311                return XT_HANDLED;
312        }
313
314        if ((mode = xt_find_attr(qnode, "mode")) &&
315            (strcmp(mode, "tcp") != 0)) {
316                imcb_log(ic, "WARNING: Received SI Request for unsupported bytestream mode %s",
317                         xt_find_attr(qnode, "mode"));
318                return XT_HANDLED;
319        }
320
321        shnode = qnode->children;
322        while ((shnode = xt_find_node(shnode, "streamhost"))) {
323                char *jid, *host, *port_s;
324                int port;
325                if ((jid = xt_find_attr(shnode, "jid")) &&
326                    (host = xt_find_attr(shnode, "host")) &&
327                    (port_s = xt_find_attr(shnode, "port")) &&
328                    (sscanf(port_s, "%d", &port) == 1)) {
329                        shlist = g_slist_append(shlist, jabber_streamhost_new(jid, host, port));
330                }
331                shnode = shnode->next;
332        }
333
334        if (!shlist) {
335                imcb_log(ic, "WARNING: Received incomplete SI bytestream request, no parseable streamhost entries");
336                return XT_HANDLED;
337        }
338
339        if (!(tf = get_ft_by_sid(jd->filetransfers, sid))) {
340                imcb_log(ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid);
341                return XT_HANDLED;
342        }
343
344        /* iq_id and canceled can be reused since SI is done */
345        g_free(tf->iq_id);
346        tf->iq_id = g_strdup(iq_id);
347
348        tf->ft->canceled = jabber_bs_canceled;
349
350        bt = g_new0(struct bs_transfer, 1);
351        bt->tf = tf;
352        bt->streamhosts = shlist;
353        bt->sh = shlist->data;
354        bt->phase = BS_PHASE_CONNECT;
355        bt->pseudoaddr = generate_pseudoaddr(sid, ini_jid, tgt_jid);
356        tf->streamhandle = bt;
357        tf->ft->free = jabber_bs_free_transfer;
358
359        jabber_bs_recv_handshake(bt, -1, 0);
360
361        return XT_HANDLED;
362}
363
364/*
365 * This is what a protocol handshake can look like in cooperative multitasking :)
366 * Might be confusing at first because it's called from different places and is recursing.
367 * (places being the event thread, bs_request, bs_handshake_abort, and itself)
368 *
369 * All in all, it turned out quite nice :)
370 */
371gboolean jabber_bs_recv_handshake(gpointer data, gint fd, b_input_condition cond)
372{
373
374        struct bs_transfer *bt = data;
375        short revents;
376        int gret;
377
378        if ((fd != -1) && !jabber_bs_poll(bt, fd, &revents)) {
379                return FALSE;
380        }
381
382        switch (bt->phase) {
383        case BS_PHASE_CONNECT:
384        {
385                struct addrinfo hints, *rp;
386
387                memset(&hints, 0, sizeof(struct addrinfo));
388                hints.ai_socktype = SOCK_STREAM;
389
390                if ((gret = getaddrinfo(bt->sh->host, bt->sh->port, &hints, &rp)) != 0) {
391                        return jabber_bs_abort(bt, "getaddrinfo() failed: %s", gai_strerror(gret));
392                }
393
394                ASSERTSOCKOP(bt->tf->fd = fd = socket(rp->ai_family, rp->ai_socktype, 0), "Opening socket");
395
396                sock_make_nonblocking(fd);
397
398                imcb_log(bt->tf->ic, "File %s: Connecting to streamhost %s:%s", bt->tf->ft->file_name, bt->sh->host,
399                         bt->sh->port);
400
401                if ((connect(fd, rp->ai_addr, rp->ai_addrlen) == -1) &&
402                    (errno != EINPROGRESS)) {
403                        return jabber_bs_abort(bt, "connect() failed: %s", strerror(errno));
404                }
405
406                freeaddrinfo(rp);
407
408                bt->phase = BS_PHASE_CONNECTED;
409
410                bt->tf->watch_out = b_input_add(fd, B_EV_IO_WRITE, jabber_bs_recv_handshake, bt);
411
412                /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */
413                bt->connect_timeout = b_timeout_add(JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt);
414
415                bt->tf->watch_in = 0;
416                return FALSE;
417        }
418        case BS_PHASE_CONNECTED:
419        {
420                struct {
421                        unsigned char ver;
422                        unsigned char nmethods;
423                        unsigned char method;
424                } socks5_hello = {
425                        .ver = 5,
426                        .nmethods = 1,
427                        .method = 0x00         /* no auth */
428                                  /* one could also implement username/password. If you know
429                                   * a jabber client or proxy that actually does it, tell me.
430                                   */
431                };
432
433                ASSERTSOCKOP(send(fd, &socks5_hello, sizeof(socks5_hello), 0), "Sending auth request");
434
435                bt->phase = BS_PHASE_REQUEST;
436
437                bt->tf->watch_in = b_input_add(fd, B_EV_IO_READ, jabber_bs_recv_handshake, bt);
438
439                bt->tf->watch_out = 0;
440                return FALSE;
441        }
442        case BS_PHASE_REQUEST:
443        {
444                struct socks5_message socks5_connect =
445                {
446                        .ver = 5,
447                        .cmdrep.cmd = 0x01,
448                        .rsv = 0,
449                        .atyp = 0x03,
450                        .addrlen = strlen(bt->pseudoaddr),
451                        .port = 0
452                };
453                int ret;
454                char buf[2];
455
456                /* If someone's trying to be funny and sends only one byte at a time we'll fail :) */
457                ASSERTSOCKOP(ret = recv(fd, buf, 2, 0), "Receiving auth reply");
458
459                if (!(ret == 2) ||
460                    !(buf[0] == 5) ||
461                    !(buf[1] == 0)) {
462                        return jabber_bs_abort(bt, "Auth not accepted by streamhost (reply: len=%d, ver=%d, status=%d)",
463                                               ret, buf[0], buf[1]);
464                }
465
466                /* copy hash into connect message */
467                memcpy(socks5_connect.address, bt->pseudoaddr, socks5_connect.addrlen);
468
469                ASSERTSOCKOP(send(fd, &socks5_connect, sizeof(struct socks5_message), 0), "Sending SOCKS5 Connect");
470
471                bt->phase = BS_PHASE_REPLY;
472
473                return TRUE;
474        }
475        case BS_PHASE_REPLY:
476        {
477                struct socks5_message socks5_reply;
478                int ret;
479
480                if (!(ret = jabber_bs_peek(bt, &socks5_reply, sizeof(struct socks5_message)))) {
481                        return FALSE;
482                }
483
484                if (ret < 5) {         /* header up to address length */
485                        return TRUE;
486                } else if (ret < sizeof(struct socks5_message)) {
487                        /* Either a buggy proxy or just one that doesnt regard
488                         * the SHOULD in XEP-0065 saying the reply SHOULD
489                         * contain the address. We'll take it, so make sure the
490                         * next jabber_bs_peek starts with an empty buffer. */
491                        bt->peek_buf_len = 0;
492                }
493
494                if (!(socks5_reply.ver == 5) ||
495                    !(socks5_reply.cmdrep.rep == 0)) {
496                        char errstr[128] = "";
497                        if ((socks5_reply.ver == 5) && (socks5_reply.cmdrep.rep <
498                                                        (sizeof(socks5_reply_code) / sizeof(socks5_reply_code[0])))) {
499                                sprintf(errstr, "with \"%s\" ", socks5_reply_code[ socks5_reply.cmdrep.rep ]);
500                        }
501                        return jabber_bs_abort(bt,
502                                               "SOCKS5 CONNECT failed %s(reply: ver=%d, rep=%d, atyp=%d, addrlen=%d)",
503                                               errstr,
504                                               socks5_reply.ver,
505                                               socks5_reply.cmdrep.rep,
506                                               socks5_reply.atyp,
507                                               socks5_reply.addrlen);
508                }
509
510                /* usually a proxy sends back the 40 bytes address but I encountered at least one (of jabber.cz)
511                 * that sends atyp=0 addrlen=0 and only 6 bytes (one less than one would expect).
512                 * Therefore I removed the wait for more bytes. Since we don't care about what else the proxy
513                 * is sending, it shouldnt matter */
514
515                if (bt->tf->ft->sending) {
516                        jabber_bs_send_activate(bt);
517                } else {
518                        jabber_bs_recv_answer_request(bt);
519                }
520
521                return FALSE;
522        }
523        default:
524                /* BUG */
525                imcb_log(bt->tf->ic, "BUG in file transfer code: undefined handshake phase");
526
527                bt->tf->watch_in = 0;
528                return FALSE;
529        }
530}
531
532/*
533 * If the handshake failed we can try the next streamhost, if there is one.
534 * An intelligent sender would probably specify himself as the first streamhost and
535 * a proxy as the second (Kopete and PSI are examples here). That way, a (potentially)
536 * slow proxy is only used if neccessary. This of course also means, that the timeout
537 * per streamhost should be kept short. If one or two firewalled adresses are specified,
538 * they have to timeout first before a proxy is tried.
539 */
540gboolean jabber_bs_recv_handshake_abort(struct bs_transfer *bt, char *error)
541{
542        struct jabber_transfer *tf = bt->tf;
543        struct xt_node *reply, *iqnode;
544        GSList *shlist;
545
546        imcb_log(tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)",
547                 tf->ft->file_name,
548                 bt->sh->host,
549                 bt->sh->port,
550                 error);
551
552        /* Alright, this streamhost failed, let's try the next... */
553        bt->phase = BS_PHASE_CONNECT;
554        shlist = g_slist_find(bt->streamhosts, bt->sh);
555        if (shlist && shlist->next) {
556                bt->sh = shlist->next->data;
557                return jabber_bs_recv_handshake(bt, -1, 0);
558        }
559
560
561        /* out of stream hosts */
562
563        iqnode = jabber_make_packet("iq", "result", tf->ini_jid, NULL);
564        reply = jabber_make_error_packet(iqnode, "item-not-found", "cancel", "404");
565        xt_free_node(iqnode);
566
567        xt_add_attr(reply, "id", tf->iq_id);
568
569        if (!jabber_write_packet(tf->ic, reply)) {
570                imcb_log(tf->ic, "WARNING: Error transmitting bytestream response");
571        }
572        xt_free_node(reply);
573
574        imcb_file_canceled(tf->ic, tf->ft, "couldn't connect to any streamhosts");
575
576        /* MUST always return FALSE! */
577        return FALSE;
578}
579
580/*
581 * After the SOCKS5 handshake succeeds we need to inform the initiator which streamhost we chose.
582 * If he is the streamhost himself, he might already know that. However, if it's a proxy,
583 * the initiator will have to make a connection himself.
584 */
585void jabber_bs_recv_answer_request(struct bs_transfer *bt)
586{
587        struct jabber_transfer *tf = bt->tf;
588        struct xt_node *reply;
589
590        imcb_log(tf->ic, "File %s: established SOCKS5 connection to %s:%s",
591                 tf->ft->file_name,
592                 bt->sh->host,
593                 bt->sh->port);
594
595        tf->ft->data = tf;
596        tf->watch_in = b_input_add(tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt);
597        tf->ft->write_request = jabber_bs_recv_write_request;
598
599        reply = xt_new_node("streamhost-used", NULL, NULL);
600        xt_add_attr(reply, "jid", bt->sh->jid);
601
602        reply = xt_new_node("query", NULL, reply);
603        xt_add_attr(reply, "xmlns", XMLNS_BYTESTREAMS);
604
605        reply = jabber_make_packet("iq", "result", tf->ini_jid, reply);
606
607        xt_add_attr(reply, "id", tf->iq_id);
608
609        if (!jabber_write_packet(tf->ic, reply)) {
610                imcb_file_canceled(tf->ic, tf->ft, "Error transmitting bytestream response");
611        }
612        xt_free_node(reply);
613}
614
615/*
616 * This function is called from write_request directly. If no data is available, it will install itself
617 * as a watcher for input on fd and once that happens, deliver the data and unschedule itself again.
618 */
619gboolean jabber_bs_recv_read(gpointer data, gint fd, b_input_condition cond)
620{
621        int ret;
622        struct bs_transfer *bt = data;
623        struct jabber_transfer *tf = bt->tf;
624
625        if (fd != -1) { /* called via event thread */
626                tf->watch_in = 0;
627                ASSERTSOCKOP(ret = recv(fd, tf->ft->buffer, sizeof(tf->ft->buffer), 0), "Receiving");
628        } else {
629                /* called directly. There might not be any data available. */
630                if (((ret = recv(tf->fd, tf->ft->buffer, sizeof(tf->ft->buffer), 0)) == -1) &&
631                    (errno != EAGAIN)) {
632                        return jabber_bs_abort(bt, "Receiving: %s", strerror(errno));
633                }
634
635                if ((ret == -1) && (errno == EAGAIN)) {
636                        tf->watch_in = b_input_add(tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt);
637                        return FALSE;
638                }
639        }
640
641        /* shouldn't happen since we know the file size */
642        if (ret == 0) {
643                return jabber_bs_abort(bt, "Remote end closed connection");
644        }
645
646        tf->bytesread += ret;
647
648        if (tf->bytesread >= tf->ft->file_size) {
649                imcb_file_finished(tf->ic, tf->ft);
650        }
651
652        tf->ft->write(tf->ft, tf->ft->buffer, ret);
653
654        return FALSE;
655}
656
657/*
658 * imc callback that is invoked when it is ready to receive some data.
659 */
660gboolean jabber_bs_recv_write_request(file_transfer_t *ft)
661{
662        struct jabber_transfer *tf = ft->data;
663
664        if (tf->watch_in) {
665                imcb_file_canceled(tf->ic, ft,
666                                   "BUG in jabber file transfer: write_request called when already watching for input");
667                return FALSE;
668        }
669
670        jabber_bs_recv_read(tf->streamhandle, -1, 0);
671
672        return TRUE;
673}
674
675/*
676 * Issues a write_request to imc.
677 * */
678gboolean jabber_bs_send_can_write(gpointer data, gint fd, b_input_condition cond)
679{
680        struct bs_transfer *bt = data;
681
682        bt->tf->watch_out = 0;
683
684        bt->tf->ft->write_request(bt->tf->ft);
685
686        return FALSE;
687}
688
689/*
690 * This should only be called if we can write, so just do it.
691 * Add a write watch so we can write more during the next cycle (if possible).
692 */
693gboolean jabber_bs_send_write(file_transfer_t *ft, char *buffer, unsigned int len)
694{
695        struct jabber_transfer *tf = ft->data;
696        struct bs_transfer *bt = tf->streamhandle;
697        int ret;
698
699        if (tf->watch_out) {
700                return jabber_bs_abort(bt, "BUG: write() called while watching ");
701        }
702
703        /* TODO: catch broken pipe */
704        ASSERTSOCKOP(ret = send(tf->fd, buffer, len, 0), "Sending");
705
706        tf->byteswritten += ret;
707
708        /* TODO: this should really not be fatal */
709        if (ret < len) {
710                return jabber_bs_abort(bt, "send() sent %d instead of %d (send buffer too big!)", ret, len);
711        }
712
713        if (tf->byteswritten >= ft->file_size) {
714                imcb_file_finished(tf->ic, ft);
715        } else {
716                bt->tf->watch_out = b_input_add(tf->fd, B_EV_IO_WRITE, jabber_bs_send_can_write, bt);
717        }
718
719        return TRUE;
720}
721
722/*
723 * Handles the reply by the receiver containing the used streamhost.
724 */
725static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
726{
727        struct jabber_transfer *tf = NULL;
728        struct jabber_data *jd = ic->proto_data;
729        struct bs_transfer *bt;
730        struct xt_node *c;
731        char *sid, *jid;
732
733        if (!(c = xt_find_node(node->children, "query")) ||
734            !(c = xt_find_node(c->children, "streamhost-used")) ||
735            !(jid = xt_find_attr(c, "jid"))) {
736                imcb_log(ic, "WARNING: Received incomplete bytestream reply");
737                return XT_HANDLED;
738        }
739
740        if (!(c = xt_find_node(orig->children, "query")) ||
741            !(sid = xt_find_attr(c, "sid"))) {
742                imcb_log(ic, "WARNING: Error parsing request corresponding to the incoming bytestream reply");
743                return XT_HANDLED;
744        }
745
746        /* Let's see if we can find out what this bytestream should be for... */
747
748        if (!(tf = get_ft_by_sid(jd->filetransfers, sid))) {
749                imcb_log(ic, "WARNING: Received SOCKS5 bytestream reply to unknown request");
750                return XT_HANDLED;
751        }
752
753        bt = tf->streamhandle;
754
755        tf->accepted = TRUE;
756
757        if (strcmp(jid, tf->ini_jid) == 0) {
758                /* we're streamhost and target */
759                if (bt->phase == BS_PHASE_REPLY) {
760                        /* handshake went through, let's start transferring */
761                        tf->ft->write_request(tf->ft);
762                }
763        } else {
764                /* using a proxy, abort listen */
765
766                if (tf->watch_in) {
767                        b_event_remove(tf->watch_in);
768                        tf->watch_in = 0;
769                }
770
771                if (tf->fd != -1) {
772                        closesocket(tf->fd);
773                        tf->fd = -1;
774                }
775
776                if (bt->connect_timeout) {
777                        b_event_remove(bt->connect_timeout);
778                        bt->connect_timeout = 0;
779                }
780
781                GSList *shlist;
782                for (shlist = jd->streamhosts; shlist; shlist = g_slist_next(shlist)) {
783                        jabber_streamhost_t *sh = shlist->data;
784                        if (strcmp(sh->jid, jid) == 0) {
785                                bt->sh = sh;
786                                jabber_bs_recv_handshake(bt, -1, 0);
787                                return XT_HANDLED;
788                        }
789                }
790
791                imcb_log(ic, "WARNING: Received SOCKS5 bytestream reply with unknown streamhost %s", jid);
792        }
793
794        return XT_HANDLED;
795}
796
797/*
798 * Tell the proxy to activate the stream. Looks like this:
799 *
800 * <iq type=set>
801 *      <query xmlns=bs sid=sid>
802 *              <activate>tgt_jid</activate>
803 *      </query>
804 * </iq>
805 */
806void jabber_bs_send_activate(struct bs_transfer *bt)
807{
808        struct xt_node *node;
809
810        node = xt_new_node("activate", bt->tf->tgt_jid, NULL);
811        node = xt_new_node("query", NULL, node);
812        xt_add_attr(node, "xmlns", XMLNS_BYTESTREAMS);
813        xt_add_attr(node, "sid", bt->tf->sid);
814        node = jabber_make_packet("iq", "set", bt->sh->jid, node);
815
816        jabber_cache_add(bt->tf->ic, node, jabber_bs_send_handle_activate);
817
818        jabber_write_packet(bt->tf->ic, node);
819}
820
821/*
822 * The proxy has activated the bytestream.
823 * We can finally start pushing some data out.
824 */
825static xt_status jabber_bs_send_handle_activate(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
826{
827        char *sid;
828        struct jabber_transfer *tf = NULL;
829        struct xt_node *query;
830        struct jabber_data *jd = ic->proto_data;
831
832        query = xt_find_node(orig->children, "query");
833        sid = xt_find_attr(query, "sid");
834
835        if (!(tf = get_ft_by_sid(jd->filetransfers, sid))) {
836                imcb_log(ic, "WARNING: Received SOCKS5 bytestream activation for unknown stream");
837                return XT_HANDLED;
838        }
839
840        imcb_log(tf->ic, "File %s: SOCKS5 handshake and activation successful! Transfer about to start...",
841                 tf->ft->file_name);
842
843        /* handshake went through, let's start transferring */
844        tf->ft->write_request(tf->ft);
845
846        return XT_HANDLED;
847}
848
849jabber_streamhost_t *jabber_si_parse_proxy(struct im_connection *ic, char *proxy)
850{
851        char *host, *port, *jid;
852        jabber_streamhost_t *sh;
853
854        if (((host = strchr(proxy, ',')) == 0) ||
855            ((port = strchr(host + 1, ',')) == 0)) {
856                imcb_log(ic, "Error parsing proxy setting: \"%s\" (ignored)", proxy);
857                return NULL;
858        }
859
860        jid = proxy;
861        *host++ = '\0';
862        *port++ = '\0';
863
864        sh = jabber_streamhost_new(jid, host, 0);
865        strncpy(sh->port, port, sizeof(sh->port));
866
867        return sh;
868}
869
870void jabber_si_set_proxies(struct bs_transfer *bt)
871{
872        struct jabber_transfer *tf = bt->tf;
873        struct jabber_data *jd = tf->ic->proto_data;
874        char *proxysetting = g_strdup(set_getstr(&tf->ic->acc->set, "proxy"));
875        char *proxy, *next, *errmsg = NULL;
876        char port[6];
877        char host[HOST_NAME_MAX + 1];
878        jabber_streamhost_t *sh, *sh2;
879        GSList *streamhosts = jd->streamhosts;
880
881        proxy = proxysetting;
882        while (proxy && (*proxy != '\0')) {
883                if ((next = strchr(proxy, ';'))) {
884                        *next++ = '\0';
885                }
886
887                if (strcmp(proxy, "<local>") == 0) {
888                        if ((tf->fd = ft_listen(&tf->saddr, host, port, jd->fd, FALSE, &errmsg)) != -1) {
889                                sh = jabber_streamhost_new(tf->ini_jid, host, 0);
890                                strncpy(sh->port, port, sizeof(sh->port));
891                                bt->streamhosts = g_slist_append(bt->streamhosts, sh);
892
893                                bt->tf->watch_in = b_input_add(tf->fd, B_EV_IO_READ, jabber_bs_send_handshake, bt);
894                                bt->connect_timeout = b_timeout_add(JABBER_BS_LISTEN_TIMEOUT * 1000,
895                                                                    jabber_bs_connect_timeout, bt);
896                        } else {
897                                imcb_log(tf->ic,
898                                         "Transferring file %s: couldn't listen locally(non fatal, check your ft_listen setting in bitlbee.conf): %s",
899                                         tf->ft->file_name,
900                                         errmsg);
901                        }
902                } else if (strcmp(proxy, "<auto>") == 0) {
903                        while (streamhosts) {
904                                sh = g_new0(jabber_streamhost_t, 1);
905                                sh2 = streamhosts->data;
906                                sh->jid = g_strdup(sh2->jid);
907                                sh->host = g_strdup(sh2->host);
908                                strcpy(sh->port, sh2->port);
909                                bt->streamhosts = g_slist_append(bt->streamhosts, sh);
910                                streamhosts = g_slist_next(streamhosts);
911                        }
912                } else if ((sh = jabber_si_parse_proxy(tf->ic, proxy))) {
913                        bt->streamhosts = g_slist_append(bt->streamhosts, sh);
914                }
915                proxy = next;
916        }
917}
918
919/*
920 * Starts a bytestream.
921 */
922gboolean jabber_bs_send_start(struct jabber_transfer *tf)
923{
924        struct bs_transfer *bt;
925
926        bt = g_new0(struct bs_transfer, 1);
927        bt->tf = tf;
928        bt->phase = BS_PHASE_CONNECT;
929        bt->pseudoaddr = generate_pseudoaddr(tf->sid, tf->ini_jid, tf->tgt_jid);
930        tf->streamhandle = bt;
931        tf->ft->free = jabber_bs_free_transfer;
932        tf->ft->canceled = jabber_bs_canceled;
933
934        jabber_si_set_proxies(bt);
935
936        return jabber_bs_send_request(tf, bt->streamhosts);
937}
938
939gboolean jabber_bs_send_request(struct jabber_transfer *tf, GSList *streamhosts)
940{
941        struct xt_node *shnode, *query, *iq;
942
943        query = xt_new_node("query", NULL, NULL);
944        xt_add_attr(query, "xmlns", XMLNS_BYTESTREAMS);
945        xt_add_attr(query, "sid", tf->sid);
946        xt_add_attr(query, "mode", "tcp");
947
948        while (streamhosts) {
949                jabber_streamhost_t *sh = streamhosts->data;
950                shnode = xt_new_node("streamhost", NULL, NULL);
951                xt_add_attr(shnode, "jid", sh->jid);
952                xt_add_attr(shnode, "host", sh->host);
953                xt_add_attr(shnode, "port", sh->port);
954
955                xt_add_child(query, shnode);
956
957                streamhosts = g_slist_next(streamhosts);
958        }
959
960
961        iq = jabber_make_packet("iq", "set", tf->tgt_jid, query);
962        xt_add_attr(iq, "from", tf->ini_jid);
963
964        jabber_cache_add(tf->ic, iq, jabber_bs_send_handle_reply);
965
966        if (!jabber_write_packet(tf->ic, iq)) {
967                imcb_file_canceled(tf->ic, tf->ft, "Error transmitting bytestream request");
968        }
969        return TRUE;
970}
971
972gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error)
973{
974        struct jabber_transfer *tf = bt->tf;
975        struct jabber_data *jd = tf->ic->proto_data;
976
977        /* TODO: did the receiver get here somehow??? */
978        imcb_log(tf->ic, "Transferring file %s: SOCKS5 handshake failed: %s",
979                 tf->ft->file_name,
980                 error);
981
982        if (jd->streamhosts == NULL) { /* we're done here unless we have a proxy to try */
983                imcb_file_canceled(tf->ic, tf->ft, error);
984        }
985
986        /* MUST always return FALSE! */
987        return FALSE;
988}
989
990/*
991 * SOCKS5BYTESTREAM protocol for the sender
992 */
993gboolean jabber_bs_send_handshake(gpointer data, gint fd, b_input_condition cond)
994{
995        struct bs_transfer *bt = data;
996        struct jabber_transfer *tf = bt->tf;
997        short revents;
998
999        if (!jabber_bs_poll(bt, fd, &revents)) {
1000                return FALSE;
1001        }
1002
1003        switch (bt->phase) {
1004        case BS_PHASE_CONNECT:
1005        {
1006                struct sockaddr_storage clt_addr;
1007                socklen_t ssize = sizeof(clt_addr);
1008
1009                /* Connect */
1010
1011                ASSERTSOCKOP(tf->fd = accept(fd, (struct sockaddr *) &clt_addr, &ssize), "Accepting connection");
1012
1013                closesocket(fd);
1014                fd = tf->fd;
1015                sock_make_nonblocking(fd);
1016
1017                bt->phase = BS_PHASE_CONNECTED;
1018
1019                bt->tf->watch_in = b_input_add(fd, B_EV_IO_READ, jabber_bs_send_handshake, bt);
1020                return FALSE;
1021        }
1022        case BS_PHASE_CONNECTED:
1023        {
1024                int ret, have_noauth = FALSE;
1025                struct {
1026                        unsigned char ver;
1027                        unsigned char method;
1028                } socks5_auth_reply = { .ver = 5, .method = 0 };
1029                struct {
1030                        unsigned char ver;
1031                        unsigned char nmethods;
1032                        unsigned char method;
1033                } socks5_hello;
1034
1035                if (!(ret = jabber_bs_peek(bt, &socks5_hello, sizeof(socks5_hello)))) {
1036                        return FALSE;
1037                }
1038
1039                if (ret < sizeof(socks5_hello)) {
1040                        return TRUE;
1041                }
1042
1043                if (!(socks5_hello.ver == 5) ||
1044                    !(socks5_hello.nmethods >= 1) ||
1045                    !(socks5_hello.nmethods < 32)) {
1046                        return jabber_bs_abort(bt, "Invalid auth request ver=%d nmethods=%d method=%d",
1047                                               socks5_hello.ver, socks5_hello.nmethods, socks5_hello.method);
1048                }
1049
1050                have_noauth = socks5_hello.method == 0;
1051
1052                if (socks5_hello.nmethods > 1) {
1053                        char mbuf[32];
1054                        int i;
1055                        ASSERTSOCKOP(ret = recv(fd, mbuf, socks5_hello.nmethods - 1, 0), "Receiving auth methods");
1056                        if (ret < (socks5_hello.nmethods - 1)) {
1057                                return jabber_bs_abort(bt, "Partial auth request");
1058                        }
1059                        for (i = 0; !have_noauth && (i < socks5_hello.nmethods - 1); i++) {
1060                                if (mbuf[i] == 0) {
1061                                        have_noauth = TRUE;
1062                                }
1063                        }
1064                }
1065
1066                if (!have_noauth) {
1067                        return jabber_bs_abort(bt, "Auth request didn't include no authentication");
1068                }
1069
1070                ASSERTSOCKOP(send(fd, &socks5_auth_reply, sizeof(socks5_auth_reply), 0), "Sending auth reply");
1071
1072                bt->phase = BS_PHASE_REQUEST;
1073
1074                return TRUE;
1075        }
1076        case BS_PHASE_REQUEST:
1077        {
1078                struct socks5_message socks5_connect;
1079                int msgsize = sizeof(struct socks5_message);
1080                int ret;
1081
1082                if (!(ret = jabber_bs_peek(bt, &socks5_connect, msgsize))) {
1083                        return FALSE;
1084                }
1085
1086                if (ret < msgsize) {
1087                        return TRUE;
1088                }
1089
1090                if (!(socks5_connect.ver == 5) ||
1091                    !(socks5_connect.cmdrep.cmd == 1) ||
1092                    !(socks5_connect.atyp == 3) ||
1093                    !(socks5_connect.addrlen == 40)) {
1094                        return jabber_bs_abort(bt,
1095                                               "Invalid SOCKS5 Connect message (addrlen=%d, ver=%d, cmd=%d, atyp=%d)",
1096                                               socks5_connect.addrlen, socks5_connect.ver, socks5_connect.cmdrep.cmd,
1097                                               socks5_connect.atyp);
1098                }
1099                if (!(memcmp(socks5_connect.address, bt->pseudoaddr, 40) == 0)) {
1100                        return jabber_bs_abort(bt, "SOCKS5 Connect message contained wrong digest");
1101                }
1102
1103                socks5_connect.cmdrep.rep = 0;
1104
1105                ASSERTSOCKOP(send(fd, &socks5_connect, msgsize, 0), "Sending connect reply");
1106
1107                bt->phase = BS_PHASE_REPLY;
1108
1109                imcb_log(tf->ic, "File %s: SOCKS5 handshake successful! Transfer about to start...", tf->ft->file_name);
1110
1111                if (tf->accepted) {
1112                        /* streamhost-used message came already in(possible?), let's start sending */
1113                        tf->ft->write_request(tf->ft);
1114                }
1115
1116                tf->watch_in = 0;
1117                return FALSE;
1118
1119        }
1120        default:
1121                /* BUG */
1122                imcb_log(bt->tf->ic, "BUG in file transfer code: undefined handshake phase");
1123
1124                bt->tf->watch_in = 0;
1125                return FALSE;
1126        }
1127}
1128#undef ASSERTSOCKOP
Note: See TracBrowser for help on using the repository browser.