source: dcc.c @ b86f37d

Last change on this file since b86f37d was 9767d03, checked in by dequis <dx@…>, at 2018-07-31T04:41:55Z

Modify server-time implementation to not change API

The new functions with the different paramters are flagged as internal,
so it doesn't even add new symbols.

abi-compliance checker says it's 100% compatible, yay.

  • Property mode set to 100644
File size: 14.9 KB
Line 
1/********************************************************************\
2* BitlBee -- An IRC to other IM-networks gateway                     *
3*                                                                    *
4* Copyright 2007 Uli Meis <a.sporto+bee@gmail.com>                   *
5\********************************************************************/
6
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 with
19  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
20  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
21  Fifth Floor, Boston, MA  02110-1301  USA
22*/
23
24#define BITLBEE_CORE
25#include "bitlbee.h"
26#include "ft.h"
27#include "dcc.h"
28#include <netinet/tcp.h>
29#include <regex.h>
30#include "lib/ftutil.h"
31
32/*
33 * Since that might be confusing a note on naming:
34 *
35 * Generic dcc functions start with
36 *
37 *      dcc_
38 *
39 * ,methods specific to DCC SEND start with
40 *
41 *      dccs_
42 *
43 * . Since we can be on both ends of a DCC SEND,
44 * functions specific to one end are called
45 *
46 *      dccs_send and dccs_recv
47 *
48 * ,respectively.
49 */
50
51
52/*
53 * used to generate a unique local transfer id the user
54 * can use to reject/cancel transfers
55 */
56unsigned int local_transfer_id = 1;
57
58/*
59 * just for debugging the nr. of chunks we received from im-protocols and the total data
60 */
61unsigned int receivedchunks = 0, receiveddata = 0;
62
63void dcc_finish(file_transfer_t *file);
64void dcc_close(file_transfer_t *file);
65gboolean dccs_send_proto(gpointer data, gint fd, b_input_condition cond);
66int dccs_send_request(struct dcc_file_transfer *df, irc_user_t *iu, struct sockaddr_storage *saddr);
67gboolean dccs_recv_proto(gpointer data, gint fd, b_input_condition cond);
68gboolean dccs_recv_write_request(file_transfer_t *ft);
69gboolean dcc_progress(gpointer data, gint fd, b_input_condition cond);
70gboolean dcc_abort(dcc_file_transfer_t *df, char *reason, ...);
71
72dcc_file_transfer_t *dcc_alloc_transfer(const char *file_name, size_t file_size, struct im_connection *ic)
73{
74        file_transfer_t *file = g_new0(file_transfer_t, 1);
75        dcc_file_transfer_t *df = file->priv = g_new0(dcc_file_transfer_t, 1);
76
77        file->file_size = file_size;
78        file->file_name = g_strdup(file_name);
79        file->local_id = local_transfer_id++;
80        file->ic = df->ic = ic;
81        df->ft = file;
82
83        return df;
84}
85
86/* This is where the sending magic starts... */
87file_transfer_t *dccs_send_start(struct im_connection *ic, irc_user_t *iu, const char *file_name, size_t file_size)
88{
89        file_transfer_t *file;
90        dcc_file_transfer_t *df;
91        irc_t *irc = (irc_t *) ic->bee->ui_data;
92        struct sockaddr_storage saddr;
93        char *errmsg;
94        char host[NI_MAXHOST];
95        char port[6];
96
97        if (file_size > global.conf->ft_max_size) {
98                return NULL;
99        }
100
101        df = dcc_alloc_transfer(file_name, file_size, ic);
102        file = df->ft;
103        file->write = dccs_send_write;
104
105        /* listen and request */
106
107        if ((df->fd = ft_listen(&saddr, host, port, irc->fd, TRUE, &errmsg)) == -1) {
108                dcc_abort(df, "Failed to listen locally, check your ft_listen setting in bitlbee.conf: %s", errmsg);
109                return NULL;
110        }
111
112        file->status = FT_STATUS_LISTENING;
113
114        if (!dccs_send_request(df, iu, &saddr)) {
115                return NULL;
116        }
117
118        /* watch */
119        df->watch_in = b_input_add(df->fd, B_EV_IO_READ, dccs_send_proto, df);
120
121        irc->file_transfers = g_slist_prepend(irc->file_transfers, file);
122
123        df->progress_timeout = b_timeout_add(DCC_MAX_STALL * 1000, dcc_progress, df);
124
125        imcb_log(ic, "File transfer request from %s for %s (%zd kb).\n"
126                 "Accept the file transfer if you'd like the file. If you don't, "
127                 "issue the 'transfer reject' command.",
128                 iu->nick, file_name, file_size / 1024);
129
130        return file;
131}
132
133/* Used pretty much everywhere in the code to abort a transfer */
134gboolean dcc_abort(dcc_file_transfer_t *df, char *reason, ...)
135{
136        file_transfer_t *file = df->ft;
137        va_list params;
138
139        va_start(params, reason);
140        char *msg = g_strdup_vprintf(reason, params);
141        va_end(params);
142
143        file->status |= FT_STATUS_CANCELED;
144
145        if (file->canceled) {
146                file->canceled(file, msg);
147        }
148
149        imcb_log(df->ic, "File %s: DCC transfer aborted: %s", file->file_name, msg);
150
151        g_free(msg);
152
153        dcc_close(df->ft);
154
155        return FALSE;
156}
157
158gboolean dcc_progress(gpointer data, gint fd, b_input_condition cond)
159{
160        struct dcc_file_transfer *df = data;
161
162        if (df->bytes_sent == df->progress_bytes_last) {
163                /* no progress. cancel */
164                if (df->bytes_sent == 0) {
165                        return dcc_abort(df, "Couldn't establish transfer within %d seconds", DCC_MAX_STALL);
166                } else {
167                        return dcc_abort(df, "Transfer stalled for %d seconds at %d kb", DCC_MAX_STALL,
168                                         df->bytes_sent / 1024);
169                }
170
171        }
172
173        df->progress_bytes_last = df->bytes_sent;
174
175        return TRUE;
176}
177
178/* used extensively for socket operations */
179#define ASSERTSOCKOP(op, msg) \
180        if ((op) == -1) { \
181                return dcc_abort(df, msg ": %s", strerror(errno)); }
182
183/* Creates the "DCC SEND" line and sends it to the server */
184int dccs_send_request(struct dcc_file_transfer *df, irc_user_t *iu, struct sockaddr_storage *saddr)
185{
186        char ipaddr[INET6_ADDRSTRLEN];
187        const void *netaddr;
188        int port;
189        char *cmd;
190
191        if (saddr->ss_family == AF_INET) {
192                struct sockaddr_in *saddr_ipv4 = ( struct sockaddr_in *) saddr;
193
194                sprintf(ipaddr, "%d",
195                        ntohl(saddr_ipv4->sin_addr.s_addr));
196                port = saddr_ipv4->sin_port;
197        } else {
198                struct sockaddr_in6 *saddr_ipv6 = ( struct sockaddr_in6 *) saddr;
199
200                netaddr = &saddr_ipv6->sin6_addr.s6_addr;
201                port = saddr_ipv6->sin6_port;
202
203                /*
204                 * Didn't find docs about this, but it seems that's the way irssi does it
205                 */
206                if (!inet_ntop(saddr->ss_family, netaddr, ipaddr, sizeof(ipaddr))) {
207                        return dcc_abort(df, "inet_ntop failed: %s", strerror(errno));
208                }
209        }
210
211        port = ntohs(port);
212
213        cmd = g_strdup_printf("\001DCC SEND %s %s %u %zu\001",
214                              df->ft->file_name, ipaddr, port, df->ft->file_size);
215
216        irc_send_msg_raw(iu, "PRIVMSG", iu->irc->user->nick, cmd);
217
218        g_free(cmd);
219
220        return TRUE;
221}
222
223/*
224 * After setup, the transfer itself is handled entirely by this function.
225 * There are basically four things to handle: connect, receive, send, and error.
226 */
227gboolean dccs_send_proto(gpointer data, gint fd, b_input_condition cond)
228{
229        dcc_file_transfer_t *df = data;
230        file_transfer_t *file = df->ft;
231
232        if ((cond & B_EV_IO_READ) &&
233            (file->status & FT_STATUS_LISTENING)) {
234                struct sockaddr *clt_addr;
235                socklen_t ssize = sizeof(clt_addr);
236
237                /* Connect */
238
239                ASSERTSOCKOP(df->fd = accept(fd, (struct sockaddr *) &clt_addr, &ssize), "Accepting connection");
240
241                closesocket(fd);
242                fd = df->fd;
243                file->status = FT_STATUS_TRANSFERRING;
244                sock_make_nonblocking(fd);
245
246                /* IM protocol callback */
247                if (file->accept) {
248                        file->accept(file);
249                }
250
251                /* reschedule for reading on new fd */
252                df->watch_in = b_input_add(fd, B_EV_IO_READ, dccs_send_proto, df);
253
254                return FALSE;
255        }
256
257        if (cond & B_EV_IO_READ) {
258                int ret;
259
260                ASSERTSOCKOP(ret = recv(fd, ((char *) &df->acked) + df->acked_len,
261                                        sizeof(df->acked) - df->acked_len, 0), "Receiving");
262
263                if (ret == 0) {
264                        return dcc_abort(df, "Remote end closed connection");
265                }
266
267                /* How likely is it that a 32-bit integer gets split across
268                   packet boundaries? Chances are rarely 0 so let's be sure. */
269                if ((df->acked_len = (df->acked_len + ret) % 4) > 0) {
270                        return TRUE;
271                }
272
273                df->acked = ntohl(df->acked);
274
275                /* If any of this is actually happening, the receiver should buy a new IRC client */
276
277                if (df->acked > df->bytes_sent) {
278                        return dcc_abort(df,
279                                         "Receiver magically received more bytes than sent ( %d > %d ) (BUG at receiver?)", df->acked,
280                                         df->bytes_sent);
281                }
282
283                if (df->acked < file->bytes_transferred) {
284                        return dcc_abort(df, "Receiver lost bytes? ( has %d, had %d ) (BUG at receiver?)", df->acked,
285                                         file->bytes_transferred);
286                }
287
288                file->bytes_transferred = df->acked;
289
290                if (file->bytes_transferred >= file->file_size) {
291                        if (df->proto_finished) {
292                                dcc_finish(file);
293                        }
294                        return FALSE;
295                }
296
297                return TRUE;
298        }
299
300        return TRUE;
301}
302
303gboolean dccs_recv_start(file_transfer_t *ft)
304{
305        dcc_file_transfer_t *df = ft->priv;
306        struct sockaddr_storage *saddr = &df->saddr;
307        int fd;
308        char ipaddr[INET6_ADDRSTRLEN];
309        socklen_t sa_len = saddr->ss_family == AF_INET ?
310                           sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
311
312        if (!ft->write) {
313                return dcc_abort(df, "BUG: protocol didn't register write()");
314        }
315
316        ASSERTSOCKOP(fd = df->fd = socket(saddr->ss_family, SOCK_STREAM, 0), "Opening Socket");
317
318        sock_make_nonblocking(fd);
319
320        if ((connect(fd, (struct sockaddr *) saddr, sa_len) == -1) &&
321            (errno != EINPROGRESS)) {
322                return dcc_abort(df, "Connecting to %s:%d : %s",
323                                 inet_ntop(saddr->ss_family,
324                                           saddr->ss_family == AF_INET ?
325                                           ( void * ) &(( struct sockaddr_in *) saddr)->sin_addr.s_addr :
326                                           ( void * ) &(( struct sockaddr_in6 *) saddr)->sin6_addr.s6_addr,
327                                           ipaddr,
328                                           sizeof(ipaddr)),
329                                 ntohs(saddr->ss_family == AF_INET ?
330                                       (( struct sockaddr_in *) saddr)->sin_port :
331                                       (( struct sockaddr_in6 *) saddr)->sin6_port),
332                                 strerror(errno));
333        }
334
335        ft->status = FT_STATUS_CONNECTING;
336
337        /* watch */
338        df->watch_out = b_input_add(df->fd, B_EV_IO_WRITE, dccs_recv_proto, df);
339        ft->write_request = dccs_recv_write_request;
340
341        df->progress_timeout = b_timeout_add(DCC_MAX_STALL * 1000, dcc_progress, df);
342
343        return TRUE;
344}
345
346gboolean dccs_recv_proto(gpointer data, gint fd, b_input_condition cond)
347{
348        dcc_file_transfer_t *df = data;
349        file_transfer_t *ft = df->ft;
350
351        if ((cond & B_EV_IO_WRITE) &&
352            (ft->status & FT_STATUS_CONNECTING)) {
353                ft->status = FT_STATUS_TRANSFERRING;
354
355                //df->watch_in = b_input_add( df->fd, B_EV_IO_READ, dccs_recv_proto, df );
356
357                df->watch_out = 0;
358                return FALSE;
359        }
360
361        if (cond & B_EV_IO_READ) {
362                int ret, done;
363
364                ASSERTSOCKOP(ret = recv(fd, ft->buffer, sizeof(ft->buffer), 0), "Receiving");
365
366                if (ret == 0) {
367                        return dcc_abort(df, "Remote end closed connection");
368                }
369
370                if (!ft->write(df->ft, ft->buffer, ret)) {
371                        return FALSE;
372                }
373
374                df->bytes_sent += ret;
375
376                done = df->bytes_sent >= ft->file_size;
377
378                if (((df->bytes_sent - ft->bytes_transferred) > DCC_PACKET_SIZE) ||
379                    done) {
380                        guint32 ack = htonl(ft->bytes_transferred = df->bytes_sent);
381                        int ackret;
382
383                        ASSERTSOCKOP(ackret = send(fd, &ack, 4, 0), "Sending DCC ACK");
384
385                        if (ackret != 4) {
386                                return dcc_abort(df, "Error sending DCC ACK, sent %d instead of 4 bytes", ackret);
387                        }
388                }
389
390                if (df->bytes_sent == ret) {
391                        ft->started = time(NULL);
392                }
393
394                if (done) {
395                        if (df->watch_out) {
396                                b_event_remove(df->watch_out);
397                        }
398
399                        df->watch_in = 0;
400
401                        if (df->proto_finished) {
402                                dcc_finish(ft);
403                        }
404
405                        return FALSE;
406                }
407
408                df->watch_in = 0;
409                return FALSE;
410        }
411
412        return TRUE;
413}
414
415gboolean dccs_recv_write_request(file_transfer_t *ft)
416{
417        dcc_file_transfer_t *df = ft->priv;
418
419        if (df->watch_in) {
420                return dcc_abort(df, "BUG: write_request() called while watching");
421        }
422
423        df->watch_in = b_input_add(df->fd, B_EV_IO_READ, dccs_recv_proto, df);
424
425        return TRUE;
426}
427
428gboolean dccs_send_can_write(gpointer data, gint fd, b_input_condition cond)
429{
430        struct dcc_file_transfer *df = data;
431
432        df->watch_out = 0;
433
434        df->ft->write_request(df->ft);
435        return FALSE;
436}
437
438/*
439 * Incoming data.
440 *
441 */
442gboolean dccs_send_write(file_transfer_t *file, char *data, unsigned int data_len)
443{
444        dcc_file_transfer_t *df = file->priv;
445        int ret;
446
447        receivedchunks++; receiveddata += data_len;
448
449        if (df->watch_out) {
450                return dcc_abort(df, "BUG: write() called while watching");
451        }
452
453        ASSERTSOCKOP(ret = send(df->fd, data, data_len, 0), "Sending data");
454
455        if (ret == 0) {
456                return dcc_abort(df, "Remote end closed connection");
457        }
458
459        /* TODO: this should really not be fatal */
460        if (ret < data_len) {
461                return dcc_abort(df, "send() sent %d instead of %d", ret, data_len);
462        }
463
464        if (df->bytes_sent == 0) {
465                file->started = time(NULL);
466        }
467
468        df->bytes_sent += ret;
469
470        if (df->bytes_sent < df->ft->file_size) {
471                df->watch_out = b_input_add(df->fd, B_EV_IO_WRITE, dccs_send_can_write, df);
472        }
473
474        return TRUE;
475}
476
477/*
478 * Cleans up after a transfer.
479 */
480void dcc_close(file_transfer_t *file)
481{
482        dcc_file_transfer_t *df = file->priv;
483        irc_t *irc = (irc_t *) df->ic->bee->ui_data;
484
485        if (file->free) {
486                file->free(file);
487        }
488
489        closesocket(df->fd);
490
491        if (df->watch_in) {
492                b_event_remove(df->watch_in);
493        }
494
495        if (df->watch_out) {
496                b_event_remove(df->watch_out);
497        }
498
499        if (df->progress_timeout) {
500                b_event_remove(df->progress_timeout);
501        }
502
503        irc->file_transfers = g_slist_remove(irc->file_transfers, file);
504
505        g_free(df);
506        g_free(file->file_name);
507        g_free(file);
508}
509
510void dcc_finish(file_transfer_t *file)
511{
512        dcc_file_transfer_t *df = file->priv;
513        time_t diff = time(NULL) - file->started ? : 1;
514
515        file->status |= FT_STATUS_FINISHED;
516
517        if (file->finished) {
518                file->finished(file);
519        }
520
521        imcb_log(df->ic, "File %s transferred successfully at %d kb/s!", file->file_name,
522                 (int) (file->bytes_transferred / 1024 / diff));
523        dcc_close(file);
524}
525
526/*
527 * DCC SEND <filename> <IP> <port> <filesize>
528 *
529 * filename can be in "" or not. If it is, " can probably be escaped...
530 * IP can be an unsigned int (IPV4) or something else (IPV6)
531 *
532 */
533file_transfer_t *dcc_request(struct im_connection *ic, char* const* ctcp)
534{
535        irc_t *irc = (irc_t *) ic->bee->ui_data;
536        file_transfer_t *ft;
537        dcc_file_transfer_t *df;
538        int gret;
539        size_t filesize;
540
541        if (ctcp[5] != NULL &&
542            sscanf(ctcp[4], "%zd", &filesize) == 1 &&   /* Just int. validation. */
543            sscanf(ctcp[5], "%zd", &filesize) == 1) {
544                char *filename, *host, *port;
545                struct addrinfo hints, *rp;
546
547                filename = ctcp[2];
548
549                host = ctcp[3];
550                while (*host && g_ascii_isdigit(*host)) {
551                        host++;                                    /* Just digits? */
552                }
553                if (*host == '\0') {
554                        struct in_addr ipaddr = { .s_addr = htonl(atoll(ctcp[3])) };
555                        host = inet_ntoa(ipaddr);
556                } else {
557                        /* Contains non-numbers, hopefully an IPV6 address */
558                        host = ctcp[3];
559                }
560
561                port = ctcp[4];
562                filesize = atoll(ctcp[5]);
563
564                memset(&hints, 0, sizeof(struct addrinfo));
565                hints.ai_socktype = SOCK_STREAM;
566                hints.ai_flags = AI_NUMERICSERV;
567
568                if ((gret = getaddrinfo(host, port, &hints, &rp))) {
569                        imcb_log(ic, "DCC: getaddrinfo() failed with %s "
570                                 "when parsing incoming 'DCC SEND': "
571                                 "host %s, port %s",
572                                 gai_strerror(gret), host, port);
573                        return NULL;
574                }
575
576                df = dcc_alloc_transfer(filename, filesize, ic);
577                ft = df->ft;
578                ft->sending = TRUE;
579                memcpy(&df->saddr, rp->ai_addr, rp->ai_addrlen);
580
581                freeaddrinfo(rp);
582
583                irc->file_transfers = g_slist_prepend(irc->file_transfers, ft);
584
585                return ft;
586        } else {
587                imcb_log(ic, "DCC: couldnt parse `DCC SEND' line");
588        }
589
590        return NULL;
591}
Note: See TracBrowser for help on using the repository browser.