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