Mercurial > pidgin.yaz
diff libpurple/protocols/gg/lib/dcc7.c @ 31860:93b08d43f684
matekm and kkszysiu collaborated on this patch to update our internal libgadu
to version 1.10.1.
author | John Bailey <rekkanoryo@rekkanoryo.org> |
---|---|
date | Thu, 24 Mar 2011 20:53:13 +0000 |
parents | a8cc50c2279f |
children | 3a90a59ddea2 |
line wrap: on
line diff
--- a/libpurple/protocols/gg/lib/dcc7.c Thu Mar 24 15:18:14 2011 +0000 +++ b/libpurple/protocols/gg/lib/dcc7.c Thu Mar 24 20:53:13 2011 +0000 @@ -1,9 +1,10 @@ -/* $Id: dcc7.c 711 2009-04-16 00:52:47Z darkjames $ */ +/* $Id: dcc7.c 1037 2010-12-17 22:18:08Z wojtekka $ */ /* - * (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka@irc.pl> + * (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl> * Tomasz Chiliński <chilek@chilan.com> * Adam Wysocki <gophi@ekg.chmurka.net> + * Bartłomiej Zimoń <uzi18@o2.pl> * * Thanks to Jakub Zawadzki <darkjames@darkjames.ath.cx> * @@ -29,6 +30,8 @@ */ #include "libgadu.h" +#include "libgadu-internal.h" +#include "libgadu-debug.h" #include <sys/types.h> #include <sys/stat.h> @@ -55,9 +58,14 @@ #include <unistd.h> #include "compat.h" +#include "protocol.h" +#include "resolver.h" -#define gg_debug_dcc(dcc, fmt...) \ - gg_debug_session((dcc) ? (dcc)->sess : NULL, fmt) +#define gg_debug_dcc(dcc, level, fmt...) \ + gg_debug_session(((dcc) != NULL) ? (dcc)->sess : NULL, level, fmt) + +#define gg_debug_dump_dcc(dcc, level, buf, len) \ + gg_debug_dump(((dcc) != NULL) ? (dcc)->sess : NULL, level, buf, len) /** * \internal Dodaje połączenie bezpośrednie do sesji. @@ -72,7 +80,7 @@ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_add(%p, %p)\n", sess, dcc); if (!sess || !dcc || dcc->next) { - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_add() invalid parameters\n"); errno = EINVAL; return -1; } @@ -97,7 +105,7 @@ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_remove(%p, %p)\n", sess, dcc); - if (!sess || !dcc) { + if (sess == NULL || dcc == NULL) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n"); errno = EINVAL; return -1; @@ -109,9 +117,9 @@ return 0; } - for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) { + for (tmp = sess->dcc7_list; tmp != NULL; tmp = tmp->next) { if (tmp->next == dcc) { - tmp = dcc->next; + tmp->next = dcc->next; dcc->next = NULL; return 0; } @@ -153,25 +161,53 @@ } /** - * \internal Nawiązuje połączenie bezpośrednie + * \internal Rozpoczyna proces pobierania adresu * - * \param sess Struktura sesji * \param dcc Struktura połączenia * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ -static int gg_dcc7_connect(struct gg_session *sess, struct gg_dcc7 *dcc) +static int gg_dcc7_get_relay_addr(struct gg_dcc7 *dcc) { - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p, %p)\n", sess, dcc); + gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_get_relay_addr(%p)\n", dcc); + + if (dcc == NULL || dcc->sess == NULL) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() invalid parameters\n"); + errno = EINVAL; + return -1; + } + + if (dcc->sess->resolver_start(&dcc->fd, &dcc->resolver, GG_RELAY_HOST) == -1) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() resolving failed (errno=%d, %s)\n", errno, strerror(errno)); + return -1; + } + + dcc->state = GG_STATE_RESOLVING_RELAY; + dcc->check = GG_CHECK_READ; + dcc->timeout = GG_DEFAULT_TIMEOUT; - if (!sess || !dcc) { - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_connect() invalid parameters\n"); + return 0; +} + +/** + * \internal Nawiązuje połączenie bezpośrednie + * + * \param dcc Struktura połączenia + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +static int gg_dcc7_connect(struct gg_dcc7 *dcc) +{ + gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p)\n", dcc); + + if (dcc == NULL) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_connect() invalid parameters\n"); errno = EINVAL; return -1; } if ((dcc->fd = gg_connect(&dcc->remote_addr, dcc->remote_port, 1)) == -1) { - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n"); + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n"); return -1; } @@ -260,21 +296,39 @@ static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc) { struct gg_dcc7_info pkt; + uint16_t external_port; + uint16_t local_port; gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc); - // XXX dać możliwość konfiguracji? + if (!dcc->sess->client_port) + local_port = dcc->sess->external_port; + else + local_port = dcc->sess->client_port; - dcc->local_addr = dcc->sess->client_addr; + if (gg_dcc7_listen(dcc, local_port) == -1) + return -1; + + if (!dcc->sess->external_port || dcc->local_port != local_port) + external_port = dcc->local_port; + else + external_port = dcc->sess->external_port; - if (gg_dcc7_listen(dcc, 0) == -1) - return -1; + if (!dcc->sess->external_addr || dcc->local_port != local_port) + dcc->local_addr = dcc->sess->client_addr; + else + dcc->local_addr = dcc->sess->external_addr; + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// dcc7_listen_and_send_info() sending IP address %s and port %d\n", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), external_port); memset(&pkt, 0, sizeof(pkt)); pkt.uin = gg_fix32(dcc->peer_uin); pkt.type = GG_DCC7_TYPE_P2P; pkt.id = dcc->cid; - snprintf((char*) pkt.info, sizeof(pkt.info), "%s %d", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), dcc->local_port); + snprintf((char*) pkt.info, sizeof(pkt.info), "%s %d", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), external_port); + // TODO: implement hash count + // we MUST fill hash to recive from server request for server connection + snprintf((char*) pkt.hash, sizeof(pkt.hash), "0"); return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL); } @@ -583,9 +637,9 @@ * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ -int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len) +int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, const void *payload, int len) { - struct gg_dcc7_id_reply *p = payload; + const struct gg_dcc7_id_reply *p = payload; struct gg_dcc7 *tmp; gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_id(%p, %p, %p, %d)\n", sess, e, payload, len); @@ -633,9 +687,9 @@ * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ -int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len) +int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, const void *payload, int len) { - struct gg_dcc7_accept *p = payload; + const struct gg_dcc7_accept *p = payload; struct gg_dcc7 *dcc; gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_accept(%p, %p, %p, %d)\n", sess, e, payload, len); @@ -673,35 +727,92 @@ * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ -int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len) +int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, const void *payload, int len) { - struct gg_dcc7_info *p = payload; + const struct gg_dcc7_info *p = payload; struct gg_dcc7 *dcc; char *tmp; gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_info(%p, %p, %p, %d)\n", sess, e, payload, len); + gg_debug_session(sess, GG_DEBUG_FUNCTION, "// gg_dcc7_handle_info() received address: %s, hash: %s\n", p->info, p->hash); if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown dcc session\n"); return 0; } - - if (p->type != GG_DCC7_TYPE_P2P) { - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unhandled transfer type (%d)\n", p->type); - e->type = GG_EVENT_DCC7_ERROR; - e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; + + if (dcc->state == GG_STATE_CONNECTED) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() state is already connected\n"); return 0; } - if ((dcc->remote_addr = inet_addr(p->info)) == INADDR_NONE) { - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP address\n"); - e->type = GG_EVENT_DCC7_ERROR; - e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; - return 0; - } + switch (p->type) + { + case GG_DCC7_TYPE_P2P: + if ((dcc->remote_addr = inet_addr(p->info)) == INADDR_NONE) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP address\n"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; + return 0; + } + + if (!(tmp = strchr(p->info, ' ')) || !(dcc->remote_port = atoi(tmp + 1))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP port\n"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; + return 0; + } + + if (dcc->state == GG_STATE_WAITING_FOR_INFO) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() wainting for info so send one\n"); + gg_dcc7_listen_and_send_info(dcc); + return 0; + } + + break; + + case GG_DCC7_TYPE_SERVER: + if (!(tmp = strstr(p->info, "GG"))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown info packet\n"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; + return 0; + } - if (!(tmp = strchr(p->info, ' ')) || !(dcc->remote_port = atoi(tmp + 1))) { - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP port\n"); +#if defined(HAVE_UINT64_T) && defined(HAVE_STRTOULL) + { + uint64_t cid; + + cid = strtoull(tmp + 2, NULL, 0); + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() info.str=%s, info.id=%llu, sess.id=%llu\n", tmp + 2, cid, *((unsigned long long*) &dcc->cid)); + + cid = gg_fix64(cid); + + if (memcmp(&dcc->cid, &cid, sizeof(cid)) != 0) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid session id\n"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; + return 0; + } + } +#endif + + if (gg_dcc7_get_relay_addr(dcc) == -1) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unable to retrieve relay address\n"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc7_error = GG_ERROR_DCC7_RELAY; + return 0; + } + + // XXX wysyłać dopiero jeśli uda się połączyć z serwerem? + + gg_send_packet(dcc->sess, GG_DCC7_INFO, payload, len, NULL); + + break; + + default: + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unhandled transfer type (%d)\n", p->type); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; return 0; @@ -710,19 +821,19 @@ // jeśli nadal czekamy na połączenie przychodzące, a druga strona nie // daje rady i oferuje namiary na siebie, bierzemy co dają. - if (dcc->state != GG_STATE_WAITING_FOR_INFO && (dcc->state != GG_STATE_LISTENING || dcc->reverse)) { - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid state\n"); - e->type = GG_EVENT_DCC7_ERROR; - e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; - return 0; - } +// if (dcc->state != GG_STATE_WAITING_FOR_INFO && (dcc->state != GG_STATE_LISTENING || dcc->reverse)) { +// gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid state\n"); +// e->type = GG_EVENT_DCC7_ERROR; +// e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; +// return 0; +// } if (dcc->state == GG_STATE_LISTENING) { close(dcc->fd); dcc->fd = -1; dcc->reverse = 1; } - + if (dcc->type == GG_SESSION_DCC7_SEND) { e->type = GG_EVENT_DCC7_ACCEPT; e->event.dcc7_accept.dcc7 = dcc; @@ -734,7 +845,7 @@ e->event.dcc7_pending.dcc7 = dcc; } - if (gg_dcc7_connect(sess, dcc) == -1) { + if (gg_dcc7_connect(dcc) == -1) { if (gg_dcc7_reverse_connect(dcc) == -1) { e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_NET; @@ -755,9 +866,9 @@ * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ -int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len) +int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, const void *payload, int len) { - struct gg_dcc7_reject *p = payload; + const struct gg_dcc7_reject *p = payload; struct gg_dcc7 *dcc; gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_reject(%p, %p, %p, %d)\n", sess, e, payload, len); @@ -793,9 +904,9 @@ * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ -int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len) +int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, const void *payload, int len) { - struct gg_dcc7_new *p = payload; + const struct gg_dcc7_new *p = payload; struct gg_dcc7 *dcc; gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_new(%p, %p, %p, %d)\n", sess, e, payload, len); @@ -1003,15 +1114,32 @@ if (error || (res = getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &error, &error_size)) == -1 || error != 0) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error)); - if (gg_dcc7_reverse_connect(dcc) != -1) { - e->type = GG_EVENT_DCC7_PENDING; - e->event.dcc7_pending.dcc7 = dcc; + if (dcc->relay) { + for (dcc->relay_index++; dcc->relay_index < dcc->relay_count; dcc->relay_index++) { + dcc->remote_addr = dcc->relay_list[dcc->relay_index].addr; + dcc->remote_port = dcc->relay_list[dcc->relay_index].port; + + if (gg_dcc7_connect(dcc) == 0) + break; + } + + if (dcc->relay_index >= dcc->relay_count) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_RELAY; + return e; + } } else { - e->type = GG_EVENT_DCC7_ERROR; - e->event.dcc_error = GG_ERROR_DCC7_NET; + if (gg_dcc7_reverse_connect(dcc) != -1) { + e->type = GG_EVENT_DCC7_PENDING; + e->event.dcc7_pending.dcc7 = dcc; + } else { + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_NET; + } + + return e; } - - return e; } gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connected, sending id\n"); @@ -1026,23 +1154,45 @@ case GG_STATE_READING_ID: { - gg_dcc7_id_t id; int res; gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n"); - if ((res = read(dcc->fd, &id, sizeof(id))) != sizeof(id)) { - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno)); - e->type = GG_EVENT_DCC7_ERROR; - e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; - return e; - } + if (!dcc->relay) { + struct gg_dcc7_welcome_p2p welcome, welcome_ok; + welcome_ok.id = dcc->cid; + + if ((res = read(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno)); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; + return e; + } - if (memcmp(&id, &dcc->cid, sizeof(id))) { - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n"); - e->type = GG_EVENT_DCC7_ERROR; - e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; - return e; + if (memcmp(&welcome, &welcome_ok, sizeof(welcome))) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; + return e; + } + } else { + struct gg_dcc7_welcome_server welcome, welcome_ok; + welcome_ok.magic = GG_DCC7_WELCOME_SERVER; + welcome_ok.id = dcc->cid; + + if ((res = read(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno)); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; + return e; + } + + if (memcmp(&welcome, &welcome_ok, sizeof(welcome)) != 0) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; + return e; + } } if (dcc->incoming) { @@ -1063,11 +1213,29 @@ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n"); - if ((res = write(dcc->fd, &dcc->cid, sizeof(dcc->cid))) != sizeof(dcc->cid)) { - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)", res, strerror(errno)); - e->type = GG_EVENT_DCC7_ERROR; - e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; - return e; + if (!dcc->relay) { + struct gg_dcc7_welcome_p2p welcome; + + welcome.id = dcc->cid; + + if ((res = write(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)\n", res, strerror(errno)); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; + return e; + } + } else { + struct gg_dcc7_welcome_server welcome; + + welcome.magic = gg_fix32(GG_DCC7_WELCOME_SERVER); + welcome.id = dcc->cid; + + if ((res = write(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)\n", res, strerror(errno)); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; + return e; + } } if (dcc->incoming) { @@ -1092,6 +1260,7 @@ if (dcc->offset >= dcc->size) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n"); e->type = GG_EVENT_DCC7_DONE; + e->event.dcc7_done.dcc7 = dcc; return e; } @@ -1124,6 +1293,7 @@ if (dcc->offset >= dcc->size) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n"); e->type = GG_EVENT_DCC7_DONE; + e->event.dcc7_done.dcc7 = dcc; return e; } @@ -1144,6 +1314,7 @@ if (dcc->offset >= dcc->size) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n"); e->type = GG_EVENT_DCC7_DONE; + e->event.dcc7_done.dcc7 = dcc; return e; } @@ -1168,6 +1339,7 @@ if (dcc->offset >= dcc->size) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n"); e->type = GG_EVENT_DCC7_DONE; + e->event.dcc7_done.dcc7 = dcc; return e; } @@ -1178,6 +1350,157 @@ return e; } + case GG_STATE_RESOLVING_RELAY: + { + struct in_addr addr; + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_RESOLVING_RELAY\n"); + + if (read(dcc->fd, &addr, sizeof(addr)) < sizeof(addr) || addr.s_addr == INADDR_NONE) { + int errno_save = errno; + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolving failed\n"); + close(dcc->fd); + dcc->fd = -1; + dcc->sess->resolver_cleanup(&dcc->resolver, 0); + errno = errno_save; + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_RELAY; + return e; + } + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), GG_RELAY_PORT); + + if ((dcc->fd = gg_connect(&addr, GG_RELAY_PORT, 1)) == -1) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_RELAY; + return e; + } + + dcc->state = GG_STATE_CONNECTING_RELAY; + dcc->check = GG_CHECK_WRITE; + dcc->timeout = GG_DEFAULT_TIMEOUT; + + return e; + } + + case GG_STATE_CONNECTING_RELAY: + { + int res; + unsigned int res_size = sizeof(res); + struct gg_dcc7_relay_req pkt; + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING_RELAY\n"); + + if (getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) != 0 || res != 0) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_RELAY; + return e; + } + + memset(&pkt, 0, sizeof(pkt)); + pkt.magic = gg_fix32(GG_DCC7_RELAY_REQUEST); + pkt.len = gg_fix32(sizeof(pkt)); + pkt.id = dcc->cid; + pkt.type = gg_fix16(GG_DCC7_RELAY_TYPE_SERVER); + pkt.dunno1 = gg_fix16(GG_DCC7_RELAY_DUNNO1); + + gg_debug_dcc(dcc, GG_DEBUG_DUMP, "// gg_dcc7_watch_fd() send pkt(0x%.2x)\n", gg_fix32(pkt.magic)); + gg_debug_dump_dcc(dcc, GG_DEBUG_DUMP, (const char*) &pkt, sizeof(pkt)); + + if ((res = write(dcc->fd, &pkt, sizeof(pkt))) != sizeof(pkt)) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() sending failed\n"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_RELAY; + return e; + } + + dcc->state = GG_STATE_READING_RELAY; + dcc->check = GG_CHECK_READ; + dcc->timeout = GG_DEFAULT_TIMEOUT; + + return e; + } + + case GG_STATE_READING_RELAY: + { + char buf[256]; + struct gg_dcc7_relay_reply *pkt; + struct gg_dcc7_relay_reply_server srv; + int res; + int i; + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_RELAY\n"); + + if ((res = read(dcc->fd, buf, sizeof(buf))) < sizeof(*pkt)) { + if (res == 0) + errno = ECONNRESET; + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno)); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_RELAY; + return e; + } + + pkt = (struct gg_dcc7_relay_reply*) buf; + + if (gg_fix32(pkt->magic) != GG_DCC7_RELAY_REPLY || gg_fix32(pkt->rcount) < 1 || gg_fix32(pkt->rcount > 256) || gg_fix32(pkt->len) < sizeof(*pkt) + gg_fix32(pkt->rcount) * sizeof(srv)) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_wathc_fd() invalid reply\n"); + errno = EINVAL; + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_RELAY; + return e; + } + + gg_debug_dcc(dcc, GG_DEBUG_DUMP, "// gg_dcc7_get_relay() read pkt(0x%.2x)\n", gg_fix32(pkt->magic)); + gg_debug_dump_dcc(dcc, GG_DEBUG_DUMP, buf, res); + + free(dcc->relay_list); + + dcc->relay_index = 0; + dcc->relay_count = gg_fix32(pkt->rcount); + dcc->relay_list = malloc(dcc->relay_count * sizeof(gg_dcc7_relay_t)); + + if (dcc->relay_list == NULL) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory"); + dcc->relay_count = 0; + free(e); + return NULL; + } + + for (i = 0; i < dcc->relay_count; i++) { + struct in_addr addr; + + memcpy(&srv, buf + sizeof(*pkt) + i * sizeof(srv), sizeof(srv)); + dcc->relay_list[i].addr = srv.addr; + dcc->relay_list[i].port = gg_fix16(srv.port); + dcc->relay_list[i].family = srv.family; + + addr.s_addr = srv.addr; + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// %s %d %d\n", inet_ntoa(addr), gg_fix16(srv.port), srv.family); + } + + dcc->relay = 1; + + for (; dcc->relay_index < dcc->relay_count; dcc->relay_index++) { + dcc->remote_addr = dcc->relay_list[dcc->relay_index].addr; + dcc->remote_port = dcc->relay_list[dcc->relay_index].port; + + if (gg_dcc7_connect(dcc) == 0) + break; + } + + if (dcc->relay_index >= dcc->relay_count) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_RELAY; + return e; + } + + return e; + } + default: { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n"); @@ -1214,6 +1537,8 @@ if (dcc->sess) gg_dcc7_session_remove(dcc->sess, dcc); + free(dcc->relay_list); + free(dcc); }