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