~liuxingcs/+junk/pidgin

« back to all changes in this revision

Viewing changes to libpurple/protocols/gg/lib/dcc7.c

  • Committer: liuxing
  • Date: 2013-04-25 11:10:17 UTC
  • Revision ID: liuxingcs@yeah.net-20130425111017-fm4mtfsuvhq0dbqd
pidgin

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: dcc7.c 1087 2011-04-14 20:53:25Z wojtekka $ */
 
2
 
 
3
/*
 
4
 *  (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
 
5
 *                          Tomasz Chiliński <chilek@chilan.com>
 
6
 *                          Adam Wysocki <gophi@ekg.chmurka.net>
 
7
 *                          Bartłomiej Zimoń <uzi18@o2.pl>
 
8
 *  
 
9
 *  Thanks to Jakub Zawadzki <darkjames@darkjames.ath.cx>
 
10
 *
 
11
 *  This program is free software; you can redistribute it and/or modify
 
12
 *  it under the terms of the GNU Lesser General Public License Version
 
13
 *  2.1 as published by the Free Software Foundation.
 
14
 *
 
15
 *  This program is distributed in the hope that it will be useful,
 
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
18
 *  GNU Lesser General Public License for more details.
 
19
 *
 
20
 *  You should have received a copy of the GNU Lesser General Public
 
21
 *  License along with this program; if not, write to the Free Software
 
22
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110,
 
23
 *  USA.
 
24
 */
 
25
 
 
26
/**
 
27
 * \file dcc7.c
 
28
 *
 
29
 * \brief Obsługa połączeń bezpośrednich od wersji Gadu-Gadu 7.x
 
30
 */
 
31
 
 
32
#include <sys/types.h>
 
33
#include <sys/stat.h>
 
34
#ifndef _WIN32
 
35
#  include <sys/ioctl.h>
 
36
#  include <sys/socket.h>
 
37
#  include <netinet/in.h>
 
38
#  include <arpa/inet.h>
 
39
#  ifdef sun
 
40
#    include <sys/filio.h>
 
41
#  endif
 
42
#endif
 
43
#include <time.h>
 
44
 
 
45
#include <ctype.h>
 
46
#include <errno.h>
 
47
#include <fcntl.h>
 
48
#include <stdarg.h>
 
49
#include <string.h>
 
50
#include <stdio.h>
 
51
#include <stdlib.h>
 
52
#include <unistd.h>
 
53
 
 
54
#include "compat.h"
 
55
#include "libgadu.h"
 
56
#include "protocol.h"
 
57
#include "resolver.h"
 
58
#include "libgadu-internal.h"
 
59
#include "libgadu-debug.h"
 
60
 
 
61
#define gg_debug_dcc(dcc, level, fmt...) \
 
62
        gg_debug_session(((dcc) != NULL) ? (dcc)->sess : NULL, level, fmt)
 
63
 
 
64
#define gg_debug_dump_dcc(dcc, level, buf, len) \
 
65
        gg_debug_dump(((dcc) != NULL) ? (dcc)->sess : NULL, level, buf, len)
 
66
 
 
67
/**
 
68
 * \internal Dodaje połączenie bezpośrednie do sesji.
 
69
 *
 
70
 * \param sess Struktura sesji
 
71
 * \param dcc Struktura połączenia
 
72
 *
 
73
 * \return 0 jeśli się powiodło, -1 w przypadku błędu
 
74
 */
 
75
static int gg_dcc7_session_add(struct gg_session *sess, struct gg_dcc7 *dcc)
 
76
{
 
77
        gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_add(%p, %p)\n", sess, dcc);
 
78
 
 
79
        if (!sess || !dcc || dcc->next) {
 
80
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_add() invalid parameters\n");
 
81
                errno = EINVAL;
 
82
                return -1;
 
83
        }
 
84
 
 
85
        dcc->next = sess->dcc7_list;
 
86
        sess->dcc7_list = dcc;
 
87
 
 
88
        return 0;
 
89
}
 
90
 
 
91
/**
 
92
 * \internal Usuwa połączenie bezpośrednie z sesji.
 
93
 *
 
94
 * \param sess Struktura sesji
 
95
 * \param dcc Struktura połączenia
 
96
 *
 
97
 * \return 0 jeśli się powiodło, -1 w przypadku błędu
 
98
 */
 
99
static int gg_dcc7_session_remove(struct gg_session *sess, struct gg_dcc7 *dcc)
 
100
{
 
101
        struct gg_dcc7 *tmp;
 
102
 
 
103
        gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_remove(%p, %p)\n", sess, dcc);
 
104
 
 
105
        if (sess == NULL || dcc == NULL) {
 
106
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n");
 
107
                errno = EINVAL;
 
108
                return -1;
 
109
        }
 
110
 
 
111
        if (sess->dcc7_list == dcc) {
 
112
                sess->dcc7_list = dcc->next;
 
113
                dcc->next = NULL;
 
114
                return 0;
 
115
        }
 
116
 
 
117
        for (tmp = sess->dcc7_list; tmp != NULL; tmp = tmp->next) {
 
118
                if (tmp->next == dcc) {
 
119
                        tmp->next = dcc->next;
 
120
                        dcc->next = NULL;
 
121
                        return 0;
 
122
                }
 
123
        }
 
124
 
 
125
        errno = ENOENT;
 
126
        return -1;
 
127
}
 
128
 
 
129
/**
 
130
 * \internal Zwraca strukturę połączenia o danym identyfikatorze.
 
131
 *
 
132
 * \param sess Struktura sesji
 
133
 * \param id Identyfikator połączenia
 
134
 * \param uin Numer nadawcy lub odbiorcy
 
135
 *
 
136
 * \return Struktura połączenia lub \c NULL jeśli nie znaleziono
 
137
 */
 
138
static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_t id, uin_t uin)
 
139
{
 
140
        struct gg_dcc7 *tmp;
 
141
        int empty;
 
142
 
 
143
        gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_find(%p, ..., %d)\n", sess, (int) uin);
 
144
 
 
145
        empty = !memcmp(&id, "\0\0\0\0\0\0\0\0", 8);
 
146
 
 
147
        for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
 
148
                if (empty) {
 
149
                        if (tmp->peer_uin == uin /*&& tmp->state != GG_STATE_WAITING_FOR_ACCEPT*/)
 
150
                                return tmp;
 
151
                } else {
 
152
                        if (!memcmp(&tmp->cid, &id, sizeof(id)))
 
153
                                return tmp;
 
154
                }
 
155
        }
 
156
 
 
157
        return NULL;
 
158
}
 
159
 
 
160
/**
 
161
 * \internal Rozpoczyna proces pobierania adresu
 
162
 *
 
163
 * \param dcc Struktura połączenia
 
164
 *
 
165
 * \return 0 jeśli się powiodło, -1 w przypadku błędu
 
166
 */
 
167
static int gg_dcc7_get_relay_addr(struct gg_dcc7 *dcc)
 
168
{
 
169
        gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_get_relay_addr(%p)\n", dcc);
 
170
 
 
171
        if (dcc == NULL || dcc->sess == NULL) {
 
172
                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() invalid parameters\n");
 
173
                errno = EINVAL;
 
174
                return -1;
 
175
        }
 
176
 
 
177
        if (dcc->sess->resolver_start(&dcc->fd, &dcc->resolver, GG_RELAY_HOST) == -1) {
 
178
                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
 
179
                return -1;
 
180
        }
 
181
 
 
182
        dcc->state = GG_STATE_RESOLVING_RELAY;
 
183
        dcc->check = GG_CHECK_READ;
 
184
        dcc->timeout = GG_DEFAULT_TIMEOUT;
 
185
 
 
186
        return 0;
 
187
}
 
188
 
 
189
/**
 
190
 * \internal Nawiązuje połączenie bezpośrednie
 
191
 *
 
192
 * \param dcc Struktura połączenia
 
193
 *
 
194
 * \return 0 jeśli się powiodło, -1 w przypadku błędu
 
195
 */
 
196
static int gg_dcc7_connect(struct gg_dcc7 *dcc)
 
197
{
 
198
        gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p)\n", dcc);
 
199
 
 
200
        if (dcc == NULL) {
 
201
                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_connect() invalid parameters\n");
 
202
                errno = EINVAL;
 
203
                return -1;
 
204
        }
 
205
 
 
206
        if ((dcc->fd = gg_connect(&dcc->remote_addr, dcc->remote_port, 1)) == -1) {
 
207
                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n");
 
208
                return -1;
 
209
        }
 
210
 
 
211
        dcc->state = GG_STATE_CONNECTING;
 
212
        dcc->check = GG_CHECK_WRITE;
 
213
        dcc->timeout = GG_DCC7_TIMEOUT_CONNECT;
 
214
        dcc->soft_timeout = 1;
 
215
 
 
216
        return 0;
 
217
}
 
218
 
 
219
/**
 
220
 * \internal Tworzy gniazdo nasłuchujące dla połączenia bezpośredniego
 
221
 *
 
222
 * \param dcc Struktura połączenia
 
223
 * \param addr Preferowany adres (jeśli równy 0, nasłuchujemy na wszystkich interfejsach)
 
224
 * \param port Preferowany port (jeśli równy 0, nasłuchujemy na losowym)
 
225
 *
 
226
 * \return 0 jeśli się powiodło, -1 w przypadku błędu
 
227
 */
 
228
static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint32_t addr, uint16_t port)
 
229
{
 
230
        struct sockaddr_in sin;
 
231
        socklen_t sin_len = sizeof(sin);
 
232
        int errsv;
 
233
        int fd;
 
234
 
 
235
        gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen(%p, %d)\n", dcc, port);
 
236
 
 
237
        if (!dcc) {
 
238
                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() invalid parameters\n");
 
239
                errno = EINVAL;
 
240
                return -1;
 
241
        }
 
242
 
 
243
        if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
 
244
                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() can't create socket (%s)\n", strerror(errno));
 
245
                return -1;
 
246
        }
 
247
 
 
248
        memset(&sin, 0, sizeof(sin));
 
249
        sin.sin_family = AF_INET;
 
250
        sin.sin_addr.s_addr = addr;
 
251
        sin.sin_port = htons(port);
 
252
 
 
253
        if (bind(fd, (struct sockaddr*) &sin, sizeof(sin)) == -1) {
 
254
                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to bind to %s:%d\n", inet_ntoa(sin.sin_addr), port);
 
255
                goto fail;
 
256
        }
 
257
 
 
258
        if (port == 0 && getsockname(fd, (struct sockaddr*) &sin, &sin_len) == -1) {
 
259
                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to bind to port %d\n", port);
 
260
                goto fail;
 
261
        }
 
262
 
 
263
        if (listen(fd, 1)) {
 
264
                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to listen (%s)\n", strerror(errno));
 
265
                goto fail;
 
266
        }
 
267
 
 
268
        dcc->fd = fd;
 
269
        dcc->local_addr = sin.sin_addr.s_addr;
 
270
        dcc->local_port = ntohs(sin.sin_port);
 
271
        
 
272
        dcc->state = GG_STATE_LISTENING;
 
273
        dcc->check = GG_CHECK_READ;
 
274
        dcc->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
 
275
 
 
276
        return 0;
 
277
 
 
278
fail:
 
279
        errsv = errno;
 
280
        close(fd);
 
281
        errno = errsv;
 
282
        return -1;
 
283
}
 
284
 
 
285
/**
 
286
 * \internal Tworzy gniazdo nasłuchujące i wysyła jego parametry
 
287
 *
 
288
 * \param dcc Struktura połączenia
 
289
 *
 
290
 * \return 0 jeśli się powiodło, -1 w przypadku błędu
 
291
 */
 
292
static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc)
 
293
{
 
294
        struct gg_dcc7_info pkt;
 
295
        uint16_t external_port;
 
296
        uint32_t external_addr;
 
297
        struct in_addr addr;
 
298
 
 
299
        gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc);
 
300
 
 
301
        if (gg_dcc7_listen(dcc, dcc->sess->client_addr, dcc->sess->client_port) == -1)
 
302
                return -1;
 
303
        
 
304
        if (dcc->sess->external_port != 0)
 
305
                external_port = dcc->sess->external_port;
 
306
        else
 
307
                external_port = dcc->local_port;
 
308
 
 
309
        if (dcc->sess->external_addr != 0)
 
310
                external_addr = dcc->sess->external_addr;
 
311
        else 
 
312
                external_addr = dcc->local_addr;
 
313
 
 
314
        addr.s_addr = external_addr;
 
315
 
 
316
        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// dcc7_listen_and_send_info() sending IP address %s and port %d\n", inet_ntoa(addr), external_port);
 
317
 
 
318
        memset(&pkt, 0, sizeof(pkt));
 
319
        pkt.uin = gg_fix32(dcc->peer_uin);
 
320
        pkt.type = GG_DCC7_TYPE_P2P;
 
321
        pkt.id = dcc->cid;
 
322
        snprintf((char*) pkt.info, sizeof(pkt.info), "%s %d", inet_ntoa(addr), external_port);
 
323
        snprintf((char*) pkt.hash, sizeof(pkt.hash), "%u", external_addr + external_port * rand());
 
324
 
 
325
        return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL);
 
326
}
 
327
 
 
328
/**
 
329
 * \internal Odwraca połączenie po nieudanym connect()
 
330
 *
 
331
 * \param dcc Struktura połączenia
 
332
 *
 
333
 * \return 0 jeśli się powiodło, -1 w przypadku błędu
 
334
 */
 
335
static int gg_dcc7_reverse_connect(struct gg_dcc7 *dcc)
 
336
{
 
337
        gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reverse_connect(%p)\n", dcc);
 
338
 
 
339
        if (dcc->reverse) {
 
340
                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() already reverse connection\n");
 
341
                return -1;
 
342
        }
 
343
 
 
344
        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() timeout, trying reverse connection\n");
 
345
        close(dcc->fd);
 
346
        dcc->fd = -1;
 
347
        dcc->reverse = 1;
 
348
 
 
349
        return gg_dcc7_listen_and_send_info(dcc);
 
350
}
 
351
 
 
352
/**
 
353
 * \internal Wysyła do serwera żądanie nadania identyfikatora sesji
 
354
 *
 
355
 * \param sess Struktura sesji
 
356
 * \param type Rodzaj połączenia (\c GG_DCC7_TYPE_FILE lub \c GG_DCC7_TYPE_VOICE)
 
357
 *
 
358
 * \return 0 jeśli się powiodło, -1 w przypadku błędu
 
359
 */
 
360
static int gg_dcc7_request_id(struct gg_session *sess, uint32_t type)
 
361
{
 
362
        struct gg_dcc7_id_request pkt;
 
363
 
 
364
        gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_request_id(%p, %d)\n", sess, type);
 
365
 
 
366
        if (!sess) {
 
367
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid parameters\n");
 
368
                errno = EFAULT;
 
369
                return -1;
 
370
        }
 
371
 
 
372
        if (sess->state != GG_STATE_CONNECTED) {
 
373
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() not connected\n");
 
374
                errno = ENOTCONN;
 
375
                return -1;
 
376
        }
 
377
 
 
378
        if (type != GG_DCC7_TYPE_VOICE && type != GG_DCC7_TYPE_FILE) {
 
379
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid transfer type (%d)\n", type);
 
380
                errno = EINVAL;
 
381
                return -1;
 
382
        }
 
383
        
 
384
        memset(&pkt, 0, sizeof(pkt));
 
385
        pkt.type = gg_fix32(type);
 
386
 
 
387
        return gg_send_packet(sess, GG_DCC7_ID_REQUEST, &pkt, sizeof(pkt), NULL);
 
388
}
 
389
 
 
390
/**
 
391
 * \internal Rozpoczyna wysyłanie pliku.
 
392
 *
 
393
 * Funkcja jest wykorzystywana przez \c gg_dcc7_send_file() oraz
 
394
 * \c gg_dcc_send_file_fd().
 
395
 *
 
396
 * \param sess Struktura sesji
 
397
 * \param rcpt Numer odbiorcy
 
398
 * \param fd Deskryptor pliku
 
399
 * \param size Rozmiar pliku
 
400
 * \param filename1250 Nazwa pliku w kodowaniu CP-1250
 
401
 * \param hash Skrót SHA-1 pliku
 
402
 * \param seek Flaga mówiąca, czy można używać lseek()
 
403
 *
 
404
 * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
 
405
 *
 
406
 * \ingroup dcc7
 
407
 */
 
408
static struct gg_dcc7 *gg_dcc7_send_file_common(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash, int seek)
 
409
{
 
410
        struct gg_dcc7 *dcc = NULL;
 
411
 
 
412
        if (!sess || !rcpt || !filename1250 || !hash || fd == -1) {
 
413
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() invalid parameters\n");
 
414
                errno = EINVAL;
 
415
                goto fail;
 
416
        }
 
417
 
 
418
        if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
 
419
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() not enough memory\n");
 
420
                goto fail;
 
421
        }
 
422
 
 
423
        if (gg_dcc7_request_id(sess, GG_DCC7_TYPE_FILE) == -1)
 
424
                goto fail;
 
425
 
 
426
        memset(dcc, 0, sizeof(struct gg_dcc7));
 
427
        dcc->type = GG_SESSION_DCC7_SEND;
 
428
        dcc->dcc_type = GG_DCC7_TYPE_FILE;
 
429
        dcc->state = GG_STATE_REQUESTING_ID;
 
430
        dcc->timeout = GG_DEFAULT_TIMEOUT;
 
431
        dcc->sess = sess;
 
432
        dcc->fd = -1;
 
433
        dcc->uin = sess->uin;
 
434
        dcc->peer_uin = rcpt;
 
435
        dcc->file_fd = fd;
 
436
        dcc->size = size;
 
437
        dcc->seek = seek;
 
438
 
 
439
        strncpy((char*) dcc->filename, filename1250, GG_DCC7_FILENAME_LEN - 1);
 
440
        dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
 
441
 
 
442
        memcpy(dcc->hash, hash, GG_DCC7_HASH_LEN);
 
443
 
 
444
        if (gg_dcc7_session_add(sess, dcc) == -1)
 
445
                goto fail;
 
446
 
 
447
        return dcc;
 
448
 
 
449
fail:
 
450
        free(dcc);
 
451
        return NULL;
 
452
}
 
453
 
 
454
/**
 
455
 * Rozpoczyna wysyłanie pliku o danej nazwie.
 
456
 *
 
457
 * \param sess Struktura sesji
 
458
 * \param rcpt Numer odbiorcy
 
459
 * \param filename Nazwa pliku w lokalnym systemie plików
 
460
 * \param filename1250 Nazwa pliku w kodowaniu CP-1250
 
461
 * \param hash Skrót SHA-1 pliku (lub \c NULL jeśli ma być wyznaczony)
 
462
 *
 
463
 * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
 
464
 *
 
465
 * \ingroup dcc7
 
466
 */
 
467
struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash)
 
468
{
 
469
        struct gg_dcc7 *dcc = NULL;
 
470
        const char *tmp;
 
471
        char hash_buf[GG_DCC7_HASH_LEN];
 
472
        struct stat st;
 
473
        int fd = -1;
 
474
 
 
475
        gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file(%p, %d, \"%s\", %p)\n", sess, rcpt, filename, hash);
 
476
 
 
477
        if (!sess || !rcpt || !filename) {
 
478
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() invalid parameters\n");
 
479
                errno = EINVAL;
 
480
                goto fail;
 
481
        }
 
482
 
 
483
        if (!filename1250)
 
484
                filename1250 = filename;
 
485
 
 
486
        if (stat(filename, &st) == -1) {
 
487
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() stat() failed (%s)\n", strerror(errno));
 
488
                goto fail;
 
489
        }
 
490
 
 
491
        if ((st.st_mode & S_IFDIR)) {
 
492
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() that's a directory\n");
 
493
                errno = EINVAL;
 
494
                goto fail;
 
495
        }
 
496
 
 
497
        if ((fd = open(filename, O_RDONLY)) == -1) {
 
498
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() open() failed (%s)\n", strerror(errno));
 
499
                goto fail;
 
500
        }
 
501
 
 
502
        if (!hash) {
 
503
                if (gg_file_hash_sha1(fd, (uint8_t*) hash_buf) == -1)
 
504
                        goto fail;
 
505
 
 
506
                hash = hash_buf;
 
507
        }
 
508
 
 
509
        if ((tmp = strrchr(filename1250, '/')))
 
510
                filename1250 = tmp + 1;
 
511
 
 
512
        if (!(dcc = gg_dcc7_send_file_common(sess, rcpt, fd, st.st_size, filename1250, hash, 1)))
 
513
                goto fail;
 
514
 
 
515
        return dcc;
 
516
 
 
517
fail:
 
518
        if (fd != -1) {
 
519
                int errsv = errno;
 
520
                close(fd);
 
521
                errno = errsv;
 
522
        }
 
523
 
 
524
        free(dcc);
 
525
        return NULL;
 
526
}
 
527
 
 
528
/**
 
529
 * \internal Rozpoczyna wysyłanie pliku o danym deskryptorze.
 
530
 *
 
531
 * \note Wysyłanie pliku nie będzie działać poprawnie, jeśli deskryptor
 
532
 * źródłowy jest w trybie nieblokującym i w pewnym momencie zabraknie danych.
 
533
 *
 
534
 * \param sess Struktura sesji
 
535
 * \param rcpt Numer odbiorcy
 
536
 * \param fd Deskryptor pliku
 
537
 * \param size Rozmiar pliku
 
538
 * \param filename1250 Nazwa pliku w kodowaniu CP-1250
 
539
 * \param hash Skrót SHA-1 pliku
 
540
 *
 
541
 * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
 
542
 *
 
543
 * \ingroup dcc7
 
544
 */
 
545
struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash)
 
546
{
 
547
        gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file_fd(%p, %d, %d, %u, \"%s\", %p)\n", sess, rcpt, fd, size, filename1250, hash);
 
548
 
 
549
        return gg_dcc7_send_file_common(sess, rcpt, fd, size, filename1250, hash, 0);
 
550
}
 
551
 
 
552
 
 
553
/**
 
554
 * Potwierdza chęć odebrania pliku.
 
555
 *
 
556
 * \param dcc Struktura połączenia
 
557
 * \param offset Początkowy offset przy wznawianiu przesyłania pliku
 
558
 *
 
559
 * \note Biblioteka nie zmienia położenia w odbieranych plikach. Jeśli offset
 
560
 * początkowy jest różny od zera, należy ustawić go funkcją \c lseek() lub
 
561
 * podobną.
 
562
 *
 
563
 * \return 0 jeśli się powiodło, -1 w przypadku błędu
 
564
 *
 
565
 * \ingroup dcc7
 
566
 */
 
567
int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset)
 
568
{
 
569
        struct gg_dcc7_accept pkt;
 
570
 
 
571
        gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_accept(%p, %d)\n", dcc, offset);
 
572
 
 
573
        if (!dcc || !dcc->sess) {
 
574
                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_accept() invalid parameters\n");
 
575
                errno = EFAULT;
 
576
                return -1;
 
577
        }
 
578
 
 
579
        memset(&pkt, 0, sizeof(pkt));
 
580
        pkt.uin = gg_fix32(dcc->peer_uin);
 
581
        pkt.id = dcc->cid;
 
582
        pkt.offset = gg_fix32(offset);
 
583
 
 
584
        if (gg_send_packet(dcc->sess, GG_DCC7_ACCEPT, &pkt, sizeof(pkt), NULL) == -1)
 
585
                return -1;
 
586
 
 
587
        dcc->offset = offset;
 
588
 
 
589
        return gg_dcc7_listen_and_send_info(dcc);
 
590
}
 
591
 
 
592
/**
 
593
 * Odrzuca próbę przesłania pliku.
 
594
 *
 
595
 * \param dcc Struktura połączenia
 
596
 * \param reason Powód odrzucenia
 
597
 *
 
598
 * \return 0 jeśli się powiodło, -1 w przypadku błędu
 
599
 *
 
600
 * \ingroup dcc7
 
601
 */
 
602
int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason)
 
603
{
 
604
        struct gg_dcc7_reject pkt;
 
605
 
 
606
        gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reject(%p, %d)\n", dcc, reason);
 
607
 
 
608
        if (!dcc || !dcc->sess) {
 
609
                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reject() invalid parameters\n");
 
610
                errno = EFAULT;
 
611
                return -1;
 
612
        }
 
613
 
 
614
        memset(&pkt, 0, sizeof(pkt));
 
615
        pkt.uin = gg_fix32(dcc->peer_uin);
 
616
        pkt.id = dcc->cid;
 
617
        pkt.reason = gg_fix32(reason);
 
618
 
 
619
        return gg_send_packet(dcc->sess, GG_DCC7_REJECT, &pkt, sizeof(pkt), NULL);
 
620
}
 
621
 
 
622
/**
 
623
 * \internal Obsługuje pakiet identyfikatora połączenia bezpośredniego.
 
624
 *
 
625
 * \param sess Struktura sesji
 
626
 * \param e Struktura zdarzenia
 
627
 * \param payload Treść pakietu
 
628
 * \param len Długość pakietu
 
629
 *
 
630
 * \return 0 jeśli się powiodło, -1 w przypadku błędu
 
631
 */
 
632
int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
 
633
{
 
634
        const struct gg_dcc7_id_reply *p = payload;
 
635
        struct gg_dcc7 *tmp;
 
636
 
 
637
        gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_id(%p, %p, %p, %d)\n", sess, e, payload, len);
 
638
 
 
639
        for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
 
640
                gg_debug_session(sess, GG_DEBUG_MISC, "// checking dcc %p, state %d, type %d\n", tmp, tmp->state, tmp->dcc_type);
 
641
 
 
642
                if (tmp->state != GG_STATE_REQUESTING_ID || tmp->dcc_type != gg_fix32(p->type))
 
643
                        continue;
 
644
                
 
645
                tmp->cid = p->id;
 
646
 
 
647
                switch (tmp->dcc_type) {
 
648
                        case GG_DCC7_TYPE_FILE:
 
649
                        {
 
650
                                struct gg_dcc7_new s;
 
651
 
 
652
                                memset(&s, 0, sizeof(s));
 
653
                                s.id = tmp->cid;
 
654
                                s.type = gg_fix32(GG_DCC7_TYPE_FILE);
 
655
                                s.uin_from = gg_fix32(tmp->uin);
 
656
                                s.uin_to = gg_fix32(tmp->peer_uin);
 
657
                                s.size = gg_fix32(tmp->size);
 
658
 
 
659
                                memcpy((char*) s.filename, (char*) tmp->filename, GG_DCC7_FILENAME_LEN);
 
660
 
 
661
                                tmp->state = GG_STATE_WAITING_FOR_ACCEPT;
 
662
                                tmp->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
 
663
 
 
664
                                return gg_send_packet(sess, GG_DCC7_NEW, &s, sizeof(s), NULL);
 
665
                        }
 
666
                }
 
667
        }
 
668
 
 
669
        return 0;
 
670
}
 
671
 
 
672
/**
 
673
 * \internal Obsługuje pakiet akceptacji połączenia bezpośredniego.
 
674
 *
 
675
 * \param sess Struktura sesji
 
676
 * \param e Struktura zdarzenia
 
677
 * \param payload Treść pakietu
 
678
 * \param len Długość pakietu
 
679
 *
 
680
 * \return 0 jeśli się powiodło, -1 w przypadku błędu
 
681
 */
 
682
int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
 
683
{
 
684
        const struct gg_dcc7_accept *p = payload;
 
685
        struct gg_dcc7 *dcc;
 
686
 
 
687
        gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_accept(%p, %p, %p, %d)\n", sess, e, payload, len);
 
688
 
 
689
        if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
 
690
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() unknown dcc session\n");
 
691
                // XXX wysłać reject?
 
692
                e->type = GG_EVENT_DCC7_ERROR;
 
693
                e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 
694
                return 0;
 
695
        }
 
696
 
 
697
        if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
 
698
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() invalid state\n");
 
699
                e->type = GG_EVENT_DCC7_ERROR;
 
700
                e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 
701
                return 0;
 
702
        }
 
703
        
 
704
        // XXX czy dla odwrotnego połączenia powinniśmy wywołać już zdarzenie GG_DCC7_ACCEPT?
 
705
        
 
706
        dcc->offset = gg_fix32(p->offset);
 
707
        dcc->state = GG_STATE_WAITING_FOR_INFO;
 
708
 
 
709
        return 0;
 
710
}
 
711
 
 
712
/**
 
713
 * \internal Obsługuje pakiet informacji o połączeniu bezpośrednim.
 
714
 *
 
715
 * \param sess Struktura sesji
 
716
 * \param e Struktura zdarzenia
 
717
 * \param payload Treść pakietu
 
718
 * \param len Długość pakietu
 
719
 *
 
720
 * \return 0 jeśli się powiodło, -1 w przypadku błędu
 
721
 */
 
722
int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
 
723
{
 
724
        const struct gg_dcc7_info *p = payload;
 
725
        struct gg_dcc7 *dcc;
 
726
        char *tmp;
 
727
 
 
728
        gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_info(%p, %p, %p, %d)\n", sess, e, payload, len);
 
729
        gg_debug_session(sess, GG_DEBUG_FUNCTION, "// gg_dcc7_handle_info() received address: %s, hash: %s\n", p->info, p->hash);
 
730
 
 
731
        if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
 
732
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown dcc session\n");
 
733
                return 0;
 
734
        }
 
735
        
 
736
        if (dcc->state == GG_STATE_CONNECTED) {
 
737
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() state is already connected\n");
 
738
                return 0;
 
739
        }
 
740
 
 
741
        switch (p->type)
 
742
        {
 
743
        case GG_DCC7_TYPE_P2P:
 
744
                if ((dcc->remote_addr = inet_addr(p->info)) == INADDR_NONE) {
 
745
                        gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP address\n");
 
746
                        e->type = GG_EVENT_DCC7_ERROR;
 
747
                        e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 
748
                        return 0;
 
749
                }
 
750
 
 
751
                if (!(tmp = strchr(p->info, ' ')) || !(dcc->remote_port = atoi(tmp + 1))) {
 
752
                        gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP port\n");
 
753
                        e->type = GG_EVENT_DCC7_ERROR;
 
754
                        e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 
755
                        return 0;
 
756
                }
 
757
 
 
758
                if (dcc->state == GG_STATE_WAITING_FOR_INFO) {
 
759
                        gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() waiting for info so send one\n");
 
760
                        gg_dcc7_listen_and_send_info(dcc);
 
761
                        e->type = GG_EVENT_DCC7_PENDING;
 
762
                        e->event.dcc7_pending.dcc7 = dcc;
 
763
                        return 0;
 
764
                }
 
765
 
 
766
                break;
 
767
 
 
768
        case GG_DCC7_TYPE_SERVER:
 
769
                if (!(tmp = strstr(p->info, "GG"))) {
 
770
                        gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown info packet\n");
 
771
                        e->type = GG_EVENT_DCC7_ERROR;
 
772
                        e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 
773
                        return 0;
 
774
                }
 
775
 
 
776
#if defined(HAVE_UINT64_T) && defined(HAVE_STRTOULL)
 
777
                {
 
778
                        uint64_t cid;
 
779
 
 
780
                        cid = strtoull(tmp + 2, NULL, 0);
 
781
 
 
782
                        gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() info.str=%s, info.id=%llu, sess.id=%llu\n", tmp + 2, cid, *((unsigned long long*) &dcc->cid));
 
783
 
 
784
                        cid = gg_fix64(cid);
 
785
 
 
786
                        if (memcmp(&dcc->cid, &cid, sizeof(cid)) != 0) {
 
787
                                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid session id\n");
 
788
                                e->type = GG_EVENT_DCC7_ERROR;
 
789
                                e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 
790
                                return 0;
 
791
                        }
 
792
                }
 
793
#endif
 
794
 
 
795
                if (gg_dcc7_get_relay_addr(dcc) == -1) {
 
796
                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unable to retrieve relay address\n");
 
797
                        e->type = GG_EVENT_DCC7_ERROR;
 
798
                        e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
 
799
                        return 0;
 
800
                }
 
801
 
 
802
                // XXX wysyłać dopiero jeśli uda się połączyć z serwerem?
 
803
 
 
804
                gg_send_packet(dcc->sess, GG_DCC7_INFO, payload, len, NULL);
 
805
 
 
806
                return 0;
 
807
 
 
808
        default:
 
809
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unhandled transfer type (%d)\n", p->type);
 
810
                e->type = GG_EVENT_DCC7_ERROR;
 
811
                e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 
812
                return 0;
 
813
        }
 
814
 
 
815
        // jeśli nadal czekamy na połączenie przychodzące, a druga strona nie
 
816
        // daje rady i oferuje namiary na siebie, bierzemy co dają.
 
817
 
 
818
//      if (dcc->state != GG_STATE_WAITING_FOR_INFO && (dcc->state != GG_STATE_LISTENING || dcc->reverse)) {
 
819
//              gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid state\n");
 
820
//              e->type = GG_EVENT_DCC7_ERROR;
 
821
//              e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 
822
//              return 0;
 
823
//      }
 
824
 
 
825
        if (dcc->state == GG_STATE_LISTENING) {
 
826
                close(dcc->fd);
 
827
                dcc->fd = -1;
 
828
                dcc->reverse = 1;
 
829
        }
 
830
        
 
831
        if (dcc->type == GG_SESSION_DCC7_SEND) {
 
832
                e->type = GG_EVENT_DCC7_ACCEPT;
 
833
                e->event.dcc7_accept.dcc7 = dcc;
 
834
                e->event.dcc7_accept.type = gg_fix32(p->type);
 
835
                e->event.dcc7_accept.remote_ip = dcc->remote_addr;
 
836
                e->event.dcc7_accept.remote_port = dcc->remote_port;
 
837
        } else {
 
838
                e->type = GG_EVENT_DCC7_PENDING;
 
839
                e->event.dcc7_pending.dcc7 = dcc;
 
840
        }
 
841
 
 
842
        if (gg_dcc7_connect(dcc) == -1) {
 
843
                if (gg_dcc7_reverse_connect(dcc) == -1) {
 
844
                        e->type = GG_EVENT_DCC7_ERROR;
 
845
                        e->event.dcc7_error = GG_ERROR_DCC7_NET;
 
846
                        return 0;
 
847
                }
 
848
        }
 
849
 
 
850
        return 0;
 
851
}
 
852
 
 
853
/**
 
854
 * \internal Obsługuje pakiet odrzucenia połączenia bezpośredniego.
 
855
 *
 
856
 * \param sess Struktura sesji
 
857
 * \param e Struktura zdarzenia
 
858
 * \param payload Treść pakietu
 
859
 * \param len Długość pakietu
 
860
 *
 
861
 * \return 0 jeśli się powiodło, -1 w przypadku błędu
 
862
 */
 
863
int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
 
864
{
 
865
        const struct gg_dcc7_reject *p = payload;
 
866
        struct gg_dcc7 *dcc;
 
867
 
 
868
        gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_reject(%p, %p, %p, %d)\n", sess, e, payload, len);
 
869
 
 
870
        if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
 
871
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() unknown dcc session\n");
 
872
                return 0;
 
873
        }
 
874
        
 
875
        if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
 
876
                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() invalid state\n");
 
877
                e->type = GG_EVENT_DCC7_ERROR;
 
878
                e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 
879
                return 0;
 
880
        }
 
881
 
 
882
        e->type = GG_EVENT_DCC7_REJECT;
 
883
        e->event.dcc7_reject.dcc7 = dcc;
 
884
        e->event.dcc7_reject.reason = gg_fix32(p->reason);
 
885
 
 
886
        // XXX ustawić state na rejected?
 
887
 
 
888
        return 0;
 
889
}
 
890
 
 
891
/**
 
892
 * \internal Obsługuje pakiet nowego połączenia bezpośredniego.
 
893
 *
 
894
 * \param sess Struktura sesji
 
895
 * \param e Struktura zdarzenia
 
896
 * \param payload Treść pakietu
 
897
 * \param len Długość pakietu
 
898
 *
 
899
 * \return 0 jeśli się powiodło, -1 w przypadku błędu
 
900
 */
 
901
int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
 
902
{
 
903
        const struct gg_dcc7_new *p = payload;
 
904
        struct gg_dcc7 *dcc;
 
905
 
 
906
        gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_new(%p, %p, %p, %d)\n", sess, e, payload, len);
 
907
 
 
908
        switch (gg_fix32(p->type)) {
 
909
                case GG_DCC7_TYPE_FILE:
 
910
                        if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
 
911
                                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() not enough memory\n");
 
912
                                return -1;
 
913
                        }
 
914
                        
 
915
                        memset(dcc, 0, sizeof(struct gg_dcc7));
 
916
                        dcc->type = GG_SESSION_DCC7_GET;
 
917
                        dcc->dcc_type = GG_DCC7_TYPE_FILE;
 
918
                        dcc->fd = -1;
 
919
                        dcc->file_fd = -1;
 
920
                        dcc->uin = sess->uin;
 
921
                        dcc->peer_uin = gg_fix32(p->uin_from);
 
922
                        dcc->cid = p->id;
 
923
                        dcc->sess = sess;
 
924
 
 
925
                        if (gg_dcc7_session_add(sess, dcc) == -1) {
 
926
                                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n");
 
927
                                gg_dcc7_free(dcc);
 
928
                                return -1;
 
929
                        }
 
930
 
 
931
                        dcc->size = gg_fix32(p->size);
 
932
                        strncpy((char*) dcc->filename, (char*) p->filename, GG_DCC7_FILENAME_LEN - 1);
 
933
                        dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
 
934
                        memcpy(dcc->hash, p->hash, GG_DCC7_HASH_LEN);
 
935
 
 
936
                        e->type = GG_EVENT_DCC7_NEW;
 
937
                        e->event.dcc7_new = dcc;
 
938
 
 
939
                        break;
 
940
 
 
941
                case GG_DCC7_TYPE_VOICE:
 
942
                        if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
 
943
                                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_packet() not enough memory\n");
 
944
                                return -1;
 
945
                        }
 
946
                        
 
947
                        memset(dcc, 0, sizeof(struct gg_dcc7));
 
948
 
 
949
                        dcc->type = GG_SESSION_DCC7_VOICE;
 
950
                        dcc->dcc_type = GG_DCC7_TYPE_VOICE;
 
951
                        dcc->fd = -1;
 
952
                        dcc->file_fd = -1;
 
953
                        dcc->uin = sess->uin;
 
954
                        dcc->peer_uin = gg_fix32(p->uin_from);
 
955
                        dcc->cid = p->id;
 
956
                        dcc->sess = sess;
 
957
 
 
958
                        if (gg_dcc7_session_add(sess, dcc) == -1) {
 
959
                                gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n");
 
960
                                gg_dcc7_free(dcc);
 
961
                                return -1;
 
962
                        }
 
963
 
 
964
                        e->type = GG_EVENT_DCC7_NEW;
 
965
                        e->event.dcc7_new = dcc;
 
966
 
 
967
                        break;
 
968
 
 
969
                default:
 
970
                        gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unknown dcc type (%d) from %ld\n", gg_fix32(p->type), gg_fix32(p->uin_from));
 
971
 
 
972
                        break;
 
973
        }
 
974
 
 
975
        return 0;
 
976
}
 
977
 
 
978
/**
 
979
 * \internal Ustawia odpowiednie stany wewnętrzne w zależności od rodzaju
 
980
 * połączenia.
 
981
 * 
 
982
 * \param dcc Struktura połączenia
 
983
 *
 
984
 * \return 0 jeśli się powiodło, -1 w przypadku błędu.
 
985
 */
 
986
static int gg_dcc7_postauth_fixup(struct gg_dcc7 *dcc)
 
987
{
 
988
        gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_postauth_fixup(%p)\n", dcc);
 
989
 
 
990
        if (!dcc) {
 
991
                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_postauth_fixup() invalid parameters\n");
 
992
                errno = EINVAL;
 
993
                return -1;
 
994
        }
 
995
 
 
996
        switch (dcc->type) {
 
997
                case GG_SESSION_DCC7_GET:
 
998
                        dcc->state = GG_STATE_GETTING_FILE;
 
999
                        dcc->check = GG_CHECK_READ;
 
1000
                        return 0;
 
1001
 
 
1002
                case GG_SESSION_DCC7_SEND:
 
1003
                        dcc->state = GG_STATE_SENDING_FILE;
 
1004
                        dcc->check = GG_CHECK_WRITE;
 
1005
                        return 0;
 
1006
 
 
1007
                case GG_SESSION_DCC7_VOICE:
 
1008
                        dcc->state = GG_STATE_READING_VOICE_DATA;
 
1009
                        dcc->check = GG_CHECK_READ;
 
1010
                        return 0;
 
1011
        }
 
1012
 
 
1013
        errno = EINVAL;
 
1014
 
 
1015
        return -1;
 
1016
}
 
1017
 
 
1018
/**
 
1019
 * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
 
1020
 *
 
1021
 * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
 
1022
 * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
 
1023
 * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free().
 
1024
 *
 
1025
 * \param dcc Struktura połączenia
 
1026
 *
 
1027
 * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
 
1028
 *
 
1029
 * \ingroup dcc7
 
1030
 */
 
1031
struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
 
1032
{
 
1033
        struct gg_event *e;
 
1034
 
 
1035
        gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_watch_fd(%p)\n", dcc);
 
1036
 
 
1037
        if (!dcc || (dcc->type != GG_SESSION_DCC7_SEND && dcc->type != GG_SESSION_DCC7_GET && dcc->type != GG_SESSION_DCC7_VOICE)) {
 
1038
                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n");
 
1039
                errno = EINVAL;
 
1040
                return NULL;
 
1041
        }
 
1042
 
 
1043
        if (!(e = malloc(sizeof(struct gg_event)))) {
 
1044
                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n");
 
1045
                return NULL;
 
1046
        }
 
1047
 
 
1048
        memset(e, 0, sizeof(struct gg_event));
 
1049
        e->type = GG_EVENT_NONE;
 
1050
 
 
1051
        switch (dcc->state) {
 
1052
                case GG_STATE_LISTENING:
 
1053
                {
 
1054
                        struct sockaddr_in sin;
 
1055
                        int fd, one = 1;
 
1056
                        socklen_t sin_len = sizeof(sin);
 
1057
 
 
1058
                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n");
 
1059
 
 
1060
                        if ((fd = accept(dcc->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
 
1061
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno));
 
1062
                                return e;
 
1063
                        }
 
1064
 
 
1065
                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));
 
1066
 
 
1067
#ifdef FIONBIO
 
1068
                        if (ioctl(fd, FIONBIO, &one) == -1) {
 
1069
#else
 
1070
                        if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
 
1071
#endif
 
1072
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() can't set nonblocking (%s)\n", strerror(errno));
 
1073
                                close(fd);
 
1074
                                e->type = GG_EVENT_DCC7_ERROR;
 
1075
                                e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
 
1076
                                return e;
 
1077
                        }
 
1078
 
 
1079
                        close(dcc->fd);
 
1080
                        dcc->fd = fd;
 
1081
 
 
1082
                        dcc->state = GG_STATE_READING_ID;
 
1083
                        dcc->check = GG_CHECK_READ;
 
1084
                        dcc->timeout = GG_DEFAULT_TIMEOUT;
 
1085
                        dcc->incoming = 1;
 
1086
 
 
1087
                        dcc->remote_port = ntohs(sin.sin_port);
 
1088
                        dcc->remote_addr = sin.sin_addr.s_addr;
 
1089
 
 
1090
                        e->type = GG_EVENT_DCC7_CONNECTED;
 
1091
                        e->event.dcc7_connected.dcc7 = dcc;
 
1092
 
 
1093
                        return e;
 
1094
                }
 
1095
 
 
1096
                case GG_STATE_CONNECTING:
 
1097
                {
 
1098
                        int res = 0, error = 0;
 
1099
                        unsigned int error_size = sizeof(error);
 
1100
 
 
1101
                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n");
 
1102
 
 
1103
                        dcc->soft_timeout = 0;
 
1104
 
 
1105
                        if (dcc->timeout == 0)
 
1106
                                error = ETIMEDOUT;
 
1107
 
 
1108
                        if (error || (res = getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &error, &error_size)) == -1 || error != 0) {
 
1109
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error));
 
1110
 
 
1111
                                if (dcc->relay) {
 
1112
                                        for (dcc->relay_index++; dcc->relay_index < dcc->relay_count; dcc->relay_index++) {
 
1113
                                                dcc->remote_addr = dcc->relay_list[dcc->relay_index].addr;
 
1114
                                                dcc->remote_port = dcc->relay_list[dcc->relay_index].port;
 
1115
 
 
1116
                                                if (gg_dcc7_connect(dcc) == 0)
 
1117
                                                        break;
 
1118
                                        }
 
1119
 
 
1120
                                        if (dcc->relay_index >= dcc->relay_count) {
 
1121
                                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available");
 
1122
                                                e->type = GG_EVENT_DCC7_ERROR;
 
1123
                                                e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 
1124
                                                return e;
 
1125
                                        }
 
1126
                                } else {
 
1127
                                        if (gg_dcc7_reverse_connect(dcc) != -1) {
 
1128
                                                e->type = GG_EVENT_DCC7_PENDING;
 
1129
                                                e->event.dcc7_pending.dcc7 = dcc;
 
1130
                                        } else {
 
1131
                                                e->type = GG_EVENT_DCC7_ERROR;
 
1132
                                                e->event.dcc_error = GG_ERROR_DCC7_NET;
 
1133
                                        }
 
1134
 
 
1135
                                        return e;
 
1136
                                }
 
1137
                        }
 
1138
 
 
1139
                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connected, sending id\n");
 
1140
 
 
1141
                        dcc->state = GG_STATE_SENDING_ID;
 
1142
                        dcc->check = GG_CHECK_WRITE;
 
1143
                        dcc->timeout = GG_DEFAULT_TIMEOUT;
 
1144
                        dcc->incoming = 0;
 
1145
 
 
1146
                        return e;
 
1147
                }
 
1148
 
 
1149
                case GG_STATE_READING_ID:
 
1150
                {
 
1151
                        int res;
 
1152
 
 
1153
                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n");
 
1154
 
 
1155
                        if (!dcc->relay) {
 
1156
                                struct gg_dcc7_welcome_p2p welcome, welcome_ok;
 
1157
                                welcome_ok.id = dcc->cid;
 
1158
 
 
1159
                                if ((res = read(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
 
1160
                                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
 
1161
                                        e->type = GG_EVENT_DCC7_ERROR;
 
1162
                                        e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
 
1163
                                        return e;
 
1164
                                }
 
1165
 
 
1166
                                if (memcmp(&welcome, &welcome_ok, sizeof(welcome))) {
 
1167
                                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
 
1168
                                        e->type = GG_EVENT_DCC7_ERROR;
 
1169
                                        e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
 
1170
                                        return e;
 
1171
                                }
 
1172
                        } else {
 
1173
                                struct gg_dcc7_welcome_server welcome, welcome_ok;
 
1174
                                welcome_ok.magic = GG_DCC7_WELCOME_SERVER;
 
1175
                                welcome_ok.id = dcc->cid;
 
1176
 
 
1177
                                if ((res = read(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
 
1178
                                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
 
1179
                                        e->type = GG_EVENT_DCC7_ERROR;
 
1180
                                        e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
 
1181
                                        return e;
 
1182
                                }
 
1183
 
 
1184
                                if (memcmp(&welcome, &welcome_ok, sizeof(welcome)) != 0) {
 
1185
                                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
 
1186
                                        e->type = GG_EVENT_DCC7_ERROR;
 
1187
                                        e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
 
1188
                                        return e;
 
1189
                                }
 
1190
                        }
 
1191
 
 
1192
                        if (dcc->incoming) {
 
1193
                                dcc->state = GG_STATE_SENDING_ID;
 
1194
                                dcc->check = GG_CHECK_WRITE;
 
1195
                                dcc->timeout = GG_DEFAULT_TIMEOUT;
 
1196
                        } else {
 
1197
                                gg_dcc7_postauth_fixup(dcc);
 
1198
                                dcc->timeout = GG_DEFAULT_TIMEOUT;
 
1199
                        }
 
1200
 
 
1201
                        return e;
 
1202
                }
 
1203
 
 
1204
                case GG_STATE_SENDING_ID:
 
1205
                {
 
1206
                        int res;
 
1207
 
 
1208
                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n");
 
1209
 
 
1210
                        if (!dcc->relay) {
 
1211
                                struct gg_dcc7_welcome_p2p welcome;
 
1212
 
 
1213
                                welcome.id = dcc->cid;
 
1214
 
 
1215
                                if ((res = write(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
 
1216
                                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)\n", res, strerror(errno));
 
1217
                                        e->type = GG_EVENT_DCC7_ERROR;
 
1218
                                        e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
 
1219
                                        return e;
 
1220
                                }
 
1221
                        } else {
 
1222
                                struct gg_dcc7_welcome_server welcome;
 
1223
 
 
1224
                                welcome.magic = gg_fix32(GG_DCC7_WELCOME_SERVER);
 
1225
                                welcome.id = dcc->cid;
 
1226
 
 
1227
                                if ((res = write(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
 
1228
                                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)\n", res, strerror(errno));
 
1229
                                        e->type = GG_EVENT_DCC7_ERROR;
 
1230
                                        e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
 
1231
                                        return e;
 
1232
                                }
 
1233
                        }
 
1234
 
 
1235
                        if (dcc->incoming) {
 
1236
                                gg_dcc7_postauth_fixup(dcc);
 
1237
                                dcc->timeout = GG_DEFAULT_TIMEOUT;
 
1238
                        } else {
 
1239
                                dcc->state = GG_STATE_READING_ID;
 
1240
                                dcc->check = GG_CHECK_READ;
 
1241
                                dcc->timeout = GG_DEFAULT_TIMEOUT;
 
1242
                        }
 
1243
 
 
1244
                        return e;
 
1245
                }
 
1246
 
 
1247
                case GG_STATE_SENDING_FILE:
 
1248
                {
 
1249
                        char buf[1024];
 
1250
                        int chunk, res;
 
1251
 
 
1252
                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_SENDING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
 
1253
 
 
1254
                        if (dcc->offset >= dcc->size) {
 
1255
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n");
 
1256
                                e->type = GG_EVENT_DCC7_DONE;
 
1257
                                e->event.dcc7_done.dcc7 = dcc;
 
1258
                                return e;
 
1259
                        }
 
1260
 
 
1261
                        if (dcc->seek && lseek(dcc->file_fd, dcc->offset, SEEK_SET) == (off_t) -1) {
 
1262
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() lseek() failed (%s)\n", strerror(errno));
 
1263
                                e->type = GG_EVENT_DCC7_ERROR;
 
1264
                                e->event.dcc_error = GG_ERROR_DCC7_FILE;
 
1265
                                return e;
 
1266
                        }
 
1267
 
 
1268
                        if ((chunk = dcc->size - dcc->offset) > sizeof(buf))
 
1269
                                chunk = sizeof(buf);
 
1270
 
 
1271
                        if ((res = read(dcc->file_fd, buf, chunk)) < 1) {
 
1272
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (res=%d, %s)\n", res, strerror(errno));
 
1273
                                e->type = GG_EVENT_DCC7_ERROR;
 
1274
                                e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF;
 
1275
                                return e;
 
1276
                        }
 
1277
 
 
1278
                        if ((res = write(dcc->fd, buf, res)) == -1) {
 
1279
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%s)\n", strerror(errno));
 
1280
                                e->type = GG_EVENT_DCC7_ERROR;
 
1281
                                e->event.dcc_error = GG_ERROR_DCC7_NET;
 
1282
                                return e;
 
1283
                        }
 
1284
 
 
1285
                        dcc->offset += res;
 
1286
 
 
1287
                        if (dcc->offset >= dcc->size) {
 
1288
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
 
1289
                                e->type = GG_EVENT_DCC7_DONE;
 
1290
                                e->event.dcc7_done.dcc7 = dcc;
 
1291
                                return e;
 
1292
                        }
 
1293
 
 
1294
                        dcc->state = GG_STATE_SENDING_FILE;
 
1295
                        dcc->check = GG_CHECK_WRITE;
 
1296
                        dcc->timeout = GG_DCC7_TIMEOUT_SEND;
 
1297
 
 
1298
                        return e;
 
1299
                }
 
1300
 
 
1301
                case GG_STATE_GETTING_FILE:
 
1302
                {
 
1303
                        char buf[1024];
 
1304
                        int res, wres;
 
1305
 
 
1306
                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_GETTING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
 
1307
 
 
1308
                        if (dcc->offset >= dcc->size) {
 
1309
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
 
1310
                                e->type = GG_EVENT_DCC7_DONE;
 
1311
                                e->event.dcc7_done.dcc7 = dcc;
 
1312
                                return e;
 
1313
                        }
 
1314
 
 
1315
                        if ((res = read(dcc->fd, buf, sizeof(buf))) < 1) {
 
1316
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (fd=%d, res=%d, %s)\n", dcc->fd, res, strerror(errno));
 
1317
                                e->type = GG_EVENT_DCC7_ERROR;
 
1318
                                e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_NET : GG_ERROR_DCC7_EOF;
 
1319
                                return e;
 
1320
                        }
 
1321
 
 
1322
                        // XXX zapisywać do skutku?
 
1323
 
 
1324
                        if ((wres = write(dcc->file_fd, buf, res)) < res) {
 
1325
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (fd=%d, res=%d, %s)\n", dcc->file_fd, wres, strerror(errno));
 
1326
                                e->type = GG_EVENT_DCC7_ERROR;
 
1327
                                e->event.dcc_error = GG_ERROR_DCC7_FILE;
 
1328
                                return e;
 
1329
                        }
 
1330
 
 
1331
                        dcc->offset += res;
 
1332
 
 
1333
                        if (dcc->offset >= dcc->size) {
 
1334
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
 
1335
                                e->type = GG_EVENT_DCC7_DONE;
 
1336
                                e->event.dcc7_done.dcc7 = dcc;
 
1337
                                return e;
 
1338
                        }
 
1339
 
 
1340
                        dcc->state = GG_STATE_GETTING_FILE;
 
1341
                        dcc->check = GG_CHECK_READ;
 
1342
                        dcc->timeout = GG_DCC7_TIMEOUT_GET;
 
1343
 
 
1344
                        return e;
 
1345
                }
 
1346
 
 
1347
                case GG_STATE_RESOLVING_RELAY:
 
1348
                {
 
1349
                        struct in_addr addr;
 
1350
 
 
1351
                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_RESOLVING_RELAY\n");
 
1352
 
 
1353
                        if (read(dcc->fd, &addr, sizeof(addr)) < sizeof(addr) || addr.s_addr == INADDR_NONE) {
 
1354
                                int errno_save = errno;
 
1355
 
 
1356
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolving failed\n");
 
1357
                                close(dcc->fd);
 
1358
                                dcc->fd = -1;
 
1359
                                dcc->sess->resolver_cleanup(&dcc->resolver, 0);
 
1360
                                errno = errno_save;
 
1361
                                e->type = GG_EVENT_DCC7_ERROR;
 
1362
                                e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 
1363
                                return e;
 
1364
                        }
 
1365
 
 
1366
                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), GG_RELAY_PORT);
 
1367
 
 
1368
                        if ((dcc->fd = gg_connect(&addr, GG_RELAY_PORT, 1)) == -1) {
 
1369
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
 
1370
                                e->type = GG_EVENT_DCC7_ERROR;
 
1371
                                e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 
1372
                                return e;
 
1373
                        }
 
1374
                        
 
1375
                        dcc->state = GG_STATE_CONNECTING_RELAY;
 
1376
                        dcc->check = GG_CHECK_WRITE;
 
1377
                        dcc->timeout = GG_DEFAULT_TIMEOUT;
 
1378
 
 
1379
                        e->type = GG_EVENT_DCC7_PENDING;
 
1380
                        e->event.dcc7_pending.dcc7 = dcc;
 
1381
 
 
1382
                        return e;
 
1383
                }
 
1384
 
 
1385
                case GG_STATE_CONNECTING_RELAY:
 
1386
                {
 
1387
                        int res;
 
1388
                        unsigned int res_size = sizeof(res);
 
1389
                        struct gg_dcc7_relay_req pkt;
 
1390
 
 
1391
                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING_RELAY\n");
 
1392
                        
 
1393
                        if (getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) != 0 || res != 0) {
 
1394
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
 
1395
                                e->type = GG_EVENT_DCC7_ERROR;
 
1396
                                e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 
1397
                                return e;
 
1398
                        }
 
1399
 
 
1400
                        memset(&pkt, 0, sizeof(pkt));
 
1401
                        pkt.magic = gg_fix32(GG_DCC7_RELAY_REQUEST);
 
1402
                        pkt.len = gg_fix32(sizeof(pkt));
 
1403
                        pkt.id = dcc->cid;
 
1404
                        pkt.type = gg_fix16(GG_DCC7_RELAY_TYPE_SERVER);
 
1405
                        pkt.dunno1 = gg_fix16(GG_DCC7_RELAY_DUNNO1);
 
1406
 
 
1407
                        gg_debug_dcc(dcc, GG_DEBUG_DUMP, "// gg_dcc7_watch_fd() send pkt(0x%.2x)\n", gg_fix32(pkt.magic));
 
1408
                        gg_debug_dump_dcc(dcc, GG_DEBUG_DUMP, (const char*) &pkt, sizeof(pkt));
 
1409
 
 
1410
                        if ((res = write(dcc->fd, &pkt, sizeof(pkt))) != sizeof(pkt)) {
 
1411
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() sending failed\n");
 
1412
                                e->type = GG_EVENT_DCC7_ERROR;
 
1413
                                e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 
1414
                                return e;
 
1415
                        }
 
1416
 
 
1417
                        dcc->state = GG_STATE_READING_RELAY;
 
1418
                        dcc->check = GG_CHECK_READ;
 
1419
                        dcc->timeout = GG_DEFAULT_TIMEOUT;
 
1420
 
 
1421
                        return e;
 
1422
                }
 
1423
 
 
1424
                case GG_STATE_READING_RELAY:
 
1425
                {
 
1426
                        char buf[256];
 
1427
                        struct gg_dcc7_relay_reply *pkt;
 
1428
                        struct gg_dcc7_relay_reply_server srv;
 
1429
                        int res;
 
1430
                        int i;
 
1431
 
 
1432
                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_RELAY\n");
 
1433
 
 
1434
                        if ((res = read(dcc->fd, buf, sizeof(buf))) < sizeof(*pkt)) {
 
1435
                                if (res == 0)
 
1436
                                        errno = ECONNRESET;
 
1437
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
 
1438
                                e->type = GG_EVENT_DCC7_ERROR;
 
1439
                                e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 
1440
                                return e;
 
1441
                        }
 
1442
 
 
1443
                        pkt = (struct gg_dcc7_relay_reply*) buf;
 
1444
 
 
1445
                        if (gg_fix32(pkt->magic) != GG_DCC7_RELAY_REPLY || gg_fix32(pkt->rcount) < 1 || gg_fix32(pkt->rcount > 256) || gg_fix32(pkt->len) < sizeof(*pkt) + gg_fix32(pkt->rcount) * sizeof(srv)) {
 
1446
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_wathc_fd() invalid reply\n");
 
1447
                                errno = EINVAL;
 
1448
                                e->type = GG_EVENT_DCC7_ERROR;
 
1449
                                e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 
1450
                                return e;
 
1451
                        }
 
1452
 
 
1453
                        gg_debug_dcc(dcc, GG_DEBUG_DUMP, "// gg_dcc7_get_relay() read pkt(0x%.2x)\n", gg_fix32(pkt->magic));
 
1454
                        gg_debug_dump_dcc(dcc, GG_DEBUG_DUMP, buf, res);
 
1455
 
 
1456
                        free(dcc->relay_list);
 
1457
 
 
1458
                        dcc->relay_index = 0;
 
1459
                        dcc->relay_count = gg_fix32(pkt->rcount);
 
1460
                        dcc->relay_list = malloc(dcc->relay_count * sizeof(gg_dcc7_relay_t));
 
1461
 
 
1462
                        if (dcc->relay_list == NULL) {
 
1463
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory");
 
1464
                                dcc->relay_count = 0;
 
1465
                                free(e);
 
1466
                                return NULL;
 
1467
                        }
 
1468
 
 
1469
                        for (i = 0; i < dcc->relay_count; i++) {
 
1470
                                struct in_addr addr;
 
1471
 
 
1472
                                memcpy(&srv, buf + sizeof(*pkt) + i * sizeof(srv), sizeof(srv));
 
1473
                                dcc->relay_list[i].addr = srv.addr;
 
1474
                                dcc->relay_list[i].port = gg_fix16(srv.port);
 
1475
                                dcc->relay_list[i].family = srv.family;
 
1476
 
 
1477
                                addr.s_addr = srv.addr;
 
1478
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "//    %s %d %d\n", inet_ntoa(addr), gg_fix16(srv.port), srv.family);
 
1479
                        }
 
1480
                        
 
1481
                        dcc->relay = 1;
 
1482
 
 
1483
                        for (; dcc->relay_index < dcc->relay_count; dcc->relay_index++) {
 
1484
                                dcc->remote_addr = dcc->relay_list[dcc->relay_index].addr;
 
1485
                                dcc->remote_port = dcc->relay_list[dcc->relay_index].port;
 
1486
 
 
1487
                                if (gg_dcc7_connect(dcc) == 0)
 
1488
                                        break;
 
1489
                        }
 
1490
 
 
1491
                        if (dcc->relay_index >= dcc->relay_count) {
 
1492
                                gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available");
 
1493
                                e->type = GG_EVENT_DCC7_ERROR;
 
1494
                                e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 
1495
                                return e;
 
1496
                        }
 
1497
 
 
1498
                        return e;
 
1499
                }
 
1500
 
 
1501
                default:
 
1502
                {
 
1503
                        gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n");
 
1504
                        e->type = GG_EVENT_DCC7_ERROR;
 
1505
                        e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
 
1506
 
 
1507
                        return e;
 
1508
                }
 
1509
        }
 
1510
 
 
1511
        return e;
 
1512
}
 
1513
 
 
1514
/**
 
1515
 * Zwalnia zasoby używane przez połączenie bezpośrednie.
 
1516
 *
 
1517
 * \param dcc Struktura połączenia
 
1518
 *
 
1519
 * \ingroup dcc7
 
1520
 */
 
1521
void gg_dcc7_free(struct gg_dcc7 *dcc)
 
1522
{
 
1523
        gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc);
 
1524
 
 
1525
        if (!dcc)
 
1526
                return;
 
1527
 
 
1528
        if (dcc->fd != -1)
 
1529
                close(dcc->fd);
 
1530
 
 
1531
        if (dcc->file_fd != -1)
 
1532
                close(dcc->file_fd);
 
1533
 
 
1534
        if (dcc->sess)
 
1535
                gg_dcc7_session_remove(dcc->sess, dcc);
 
1536
 
 
1537
        free(dcc->relay_list);
 
1538
 
 
1539
        free(dcc);
 
1540
}
 
1541