/** * \internal Obsługuje pakiet przerwania żądania połączenia bezpośredniego. * * \param sess Struktura sesji * \param e Struktura zdarzenia * \param payload Treść pakietu * \param len Długość pakietu * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ int gg_dcc7_handle_abort(struct gg_session *sess, struct gg_event *e, void *payload, int len) { struct gg_dcc7_aborted *p = payload; struct gg_dcc7 *dcc; gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_abort(%p, %p, %p, %d)\n", sess, e, payload, len); if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(0)))) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_abort() unknown dcc session\n"); return 0; } gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_abort() state %d\n", dcc->state); if (dcc->state != GG_STATE_IDLE) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_abort() invalid state\n"); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; e->event.dcc7_error_ex.dcc7 = dcc; return 0; } e->type = GG_EVENT_DCC7_REJECT; e->event.dcc7_reject.dcc7 = dcc; e->event.dcc7_reject.reason = gg_fix32(GG_DCC7_REJECT_USER); // XXX ustawić state na rejected? return 0; }
/** * \internal Obsługuje pakiet odrzucenia połączenia bezpośredniego. * * \param sess Struktura sesji * \param e Struktura zdarzenia * \param payload Treść pakietu * \param len Długość pakietu * * \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, const void *payload, int len) { 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); if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() unknown dcc session\n"); return 0; } if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() invalid state\n"); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; return 0; } e->type = GG_EVENT_DCC7_REJECT; e->event.dcc7_reject.dcc7 = dcc; e->event.dcc7_reject.reason = gg_fix32(p->reason); // XXX ustawić state na rejected? return 0; }
/** * \internal Obsługuje pakiet akceptacji połączenia bezpośredniego. * * \param sess Struktura sesji * \param e Struktura zdarzenia * \param payload Treść pakietu * \param len Długość pakietu * * \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) { 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); if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() unknown dcc session\n"); // XXX wysłać reject? e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; e->event.dcc7_error_ex.dcc7 = dcc; return 0; } if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() invalid state\n"); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; e->event.dcc7_error_ex.dcc7 = dcc; return 0; } // XXX czy dla odwrotnego połączenia powinniśmy wywołać już zdarzenie GG_DCC7_ACCEPT? dcc->offset = gg_fix32(p->offset); dcc->state = GG_STATE_WAITING_FOR_INFO; return 0; }
/* * gg_pubdir50() * * wysy³a zapytanie katalogu publicznego do serwera. * * - sess - sesja, * - req - zapytanie. * * numer sekwencyjny wyszukiwania lub 0 w przypadku b³êdu. */ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req) { int i, size = 5; uint32_t res; char *buf, *p; struct gg_pubdir50_request *r; gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req); if (!sess || !req) { gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n"); errno = EFAULT; return 0; } if (sess->state != GG_STATE_CONNECTED) { gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() not connected\n"); errno = ENOTCONN; return 0; } for (i = 0; i < req->entries_count; i++) { /* wyszukiwanie bierze tylko pierwszy wpis */ if (req->entries[i].num) continue; size += strlen(req->entries[i].field) + 1; size += strlen(req->entries[i].value) + 1; } if (!(buf = malloc(size))) { gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size); return 0; } r = (struct gg_pubdir50_request*) buf; res = time(NULL); r->type = req->type; r->seq = (req->seq) ? gg_fix32(req->seq) : gg_fix32(time(NULL)); req->seq = gg_fix32(r->seq); for (i = 0, p = buf + 5; i < req->entries_count; i++) { if (req->entries[i].num) continue; strcpy(p, req->entries[i].field); p += strlen(p) + 1; strcpy(p, req->entries[i].value); p += strlen(p) + 1; } if (gg_send_packet(sess, GG_PUBDIR50_REQUEST, buf, size, NULL, 0) == -1) res = 0; free(buf); return res; }
/** * \internal Tworzy gniazdo nasłuchujące i wysyła jego parametry * * \param dcc Struktura połączenia * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc) { struct gg_dcc7_info pkt; uint16_t external_port; uint32_t external_addr; struct in_addr addr; gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc); if (gg_dcc7_listen(dcc, dcc->sess->client_addr, dcc->sess->client_port) == -1) return -1; if (dcc->sess->external_port != 0) external_port = dcc->sess->external_port; else external_port = dcc->local_port; if (dcc->sess->external_addr != 0) external_addr = dcc->sess->external_addr; else external_addr = dcc->local_addr; addr.s_addr = external_addr; gg_debug_dcc(dcc, GG_DEBUG_MISC, "// dcc7_listen_and_send_info() sending IP address %s and port %d\n", inet_ntoa(addr), external_port); 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(addr), external_port); snprintf((char*) pkt.hash, sizeof(pkt.hash), "%u", external_addr + external_port * rand()); return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL); }
/** * \internal Wysyła do serwera żądanie nadania identyfikatora sesji * * \param sess Struktura sesji * \param type Rodzaj połączenia (\c GG_DCC7_TYPE_FILE lub \c GG_DCC7_TYPE_VOICE) * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ static int gg_dcc7_request_id(struct gg_session *sess, uint32_t type) { struct gg_dcc7_id_request pkt; gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_request_id(%p, %d)\n", sess, type); if (!sess) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid parameters\n"); errno = EFAULT; return -1; } if (sess->state != GG_STATE_CONNECTED) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() not connected\n"); errno = ENOTCONN; return -1; } if (type != GG_DCC7_TYPE_VOICE && type != GG_DCC7_TYPE_FILE) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid transfer type (%d)\n", type); errno = EINVAL; return -1; } memset(&pkt, 0, sizeof(pkt)); pkt.type = gg_fix32(type); return gg_send_packet(sess, GG_DCC7_ID_REQUEST, &pkt, sizeof(pkt), NULL); }
/** * Wysyła ramkę danych połączenia głosowego. * * \param d Struktura połączenia * \param buf Bufor z danymi * \param length Długość bufora z danymi * * \return 0 jeśli się powiodło, -1 w przypadku błędu * * \ingroup dcc6 */ int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length) { struct packet_s { uint8_t type; uint32_t length; } GG_PACKED; struct packet_s packet; gg_debug(GG_DEBUG_FUNCTION, "++ gg_dcc_voice_send(%p, %p, %d);\n", d, buf, length); if (!d || !buf || length < 0 || d->type != GG_SESSION_DCC_VOICE) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() invalid argument\n"); errno = EINVAL; return -1; } packet.type = 0x03; /* XXX */ packet.length = gg_fix32(length); if (send(d->fd, &packet, sizeof(packet), 0) < (signed)sizeof(packet)) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() send() failed\n"); return -1; } gg_dcc_debug_data("write", d->fd, &packet, sizeof(packet)); if (send(d->fd, buf, length, 0) < length) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() send() failed\n"); return -1; } gg_dcc_debug_data("write", d->fd, buf, length); return 0; }
/** * Przerwanie żądania przesłania pliku. * * \param dcc Struktura połączenia * * \return 0 jeśli się powiodło, -1 w przypadku błędu * * \ingroup dcc7 */ int gg_dcc7_abort(struct gg_dcc7 *dcc) { struct gg_dcc7_abort pkt; gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_abort(%p)\n", dcc); if (!dcc || !dcc->sess) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_abort() invalid parameters\n"); errno = EFAULT; return -1; } memset(&pkt, 0, sizeof(pkt)); pkt.id = dcc->cid; pkt.uin_from = gg_fix32(dcc->uin); pkt.uin_to = gg_fix32(dcc->peer_uin); return gg_send_packet(dcc->sess, GG_DCC7_ABORT, &pkt, sizeof(pkt), NULL); }
/** * Odrzuca próbę przesłania pliku. * * \param dcc Struktura połączenia * \param reason Powód odrzucenia * * \return 0 jeśli się powiodło, -1 w przypadku błędu * * \ingroup dcc7 */ int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason) { struct gg_dcc7_reject pkt; gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_reject(%p, %d)\n", dcc, reason); if (!dcc || !dcc->sess) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_reject() invalid parameters\n"); errno = EFAULT; return -1; } memset(&pkt, 0, sizeof(pkt)); pkt.uin = gg_fix32(dcc->peer_uin); pkt.id = dcc->cid; pkt.reason = gg_fix32(reason); return gg_send_packet(dcc->sess, GG_DCC7_REJECT, &pkt, sizeof(pkt), NULL); }
static void test_gg_fix32(void) { const char *source = "\xee\xdd\xcc\xbb"; uint32_t value; memcpy(&value, source, sizeof(value)); if (gg_fix32(value) != 0xbbccddee) { fprintf(stderr, "gg_fix32 failed\n"); exit(1); } }
/** * \internal Obsługuje pakiet identyfikatora połączenia bezpośredniego. * * \param sess Struktura sesji * \param e Struktura zdarzenia * \param payload Treść pakietu * \param len Długość pakietu * * \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) { 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); for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) { gg_debug_session(sess, GG_DEBUG_MISC, "// checking dcc %p, state %d, type %d\n", tmp, tmp->state, tmp->dcc_type); if (tmp->state != GG_STATE_REQUESTING_ID || tmp->dcc_type != (int)gg_fix32(p->type)) continue; tmp->cid = p->id; switch (tmp->dcc_type) { case GG_DCC7_TYPE_FILE: { struct gg_dcc7_new s; memset(&s, 0, sizeof(s)); s.id = tmp->cid; s.type = gg_fix32(GG_DCC7_TYPE_FILE); s.uin_from = gg_fix32(tmp->uin); s.uin_to = gg_fix32(tmp->peer_uin); s.size = gg_fix32(tmp->size); strncpy((char*) s.filename, (char*) tmp->filename, GG_DCC7_FILENAME_LEN); tmp->state = GG_STATE_WAITING_FOR_ACCEPT; tmp->timeout = GG_DCC7_TIMEOUT_FILE_ACK; return gg_send_packet(sess, GG_DCC7_NEW, &s, sizeof(s), NULL); } } } return 0; }
/** * Potwierdza chęć odebrania pliku. * * \param dcc Struktura połączenia * \param offset Początkowy offset przy wznawianiu przesyłania pliku * * \note Biblioteka nie zmienia położenia w odbieranych plikach. Jeśli offset * początkowy jest różny od zera, należy ustawić go funkcją \c lseek() lub * podobną. * * \return 0 jeśli się powiodło, -1 w przypadku błędu * * \ingroup dcc7 */ int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset) { struct gg_dcc7_accept pkt; gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_accept(%p, %d)\n", dcc, offset); if (!dcc || !dcc->sess) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_accept() invalid parameters\n"); errno = EFAULT; return -1; } memset(&pkt, 0, sizeof(pkt)); pkt.uin = gg_fix32(dcc->peer_uin); pkt.id = dcc->cid; pkt.offset = gg_fix32(offset); if (gg_send_packet(dcc->sess, GG_DCC7_ACCEPT, &pkt, sizeof(pkt), NULL) == -1) return -1; dcc->offset = offset; return gg_dcc7_listen_and_send_info(dcc); }
/** * \internal Tworzy gniazdo nasłuchujące i wysyła jego parametry * * \param dcc Struktura połączenia * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ 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; uint32_t count; gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc); if (!dcc->sess->client_port) local_port = dcc->sess->external_port; else local_port = dcc->sess->client_port; 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 (!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_session((dcc) ? (dcc)->sess : NULL, 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)), 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"); count = dcc->local_addr + external_port * rand(); mir_snprintf(pkt.hash, sizeof(pkt.hash), "%d", count); return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL); }
/** * \internal Tworzy gniazdo nasłuchujące i wysyła jego parametry * * \param dcc Struktura połączenia * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc) { struct gg_dcc7_info pkt; gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc); // XXX dać możliwość konfiguracji? dcc->local_addr = dcc->sess->client_addr; if (gg_dcc7_listen(dcc, 0) == -1) return -1; 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); return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL); }
/** * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku. * * \param d Struktura połączenia * \param filename Nazwa pliku zapisywana w strukturze * \param local_filename Nazwa pliku w lokalnym systemie plików * * \return 0 jeśli się powiodło, -1 w przypadku błędu * * \ingroup dcc6 */ int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename) { struct stat st; const char *name, *ext, *p; unsigned char *q; int i, j; gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_fill_file_info2(%p, \"%s\", \"%s\");\n", d, filename, local_filename); if (!d || d->type != GG_SESSION_DCC_SEND) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() invalid arguments\n"); errno = EINVAL; return -1; } if (stat(local_filename, &st) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() stat() failed (%s)\n", strerror(errno)); return -1; } if ((st.st_mode & S_IFDIR)) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() that's a directory\n"); errno = EINVAL; return -1; } if ((d->file_fd = open(local_filename, O_RDONLY)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() open() failed (%s)\n", strerror(errno)); return -1; } memset(&d->file_info, 0, sizeof(d->file_info)); if (!(st.st_mode & S_IWUSR)) d->file_info.mode |= gg_fix32(GG_DCC_FILEATTR_READONLY); gg_dcc_fill_filetime(st.st_atime, d->file_info.atime); gg_dcc_fill_filetime(st.st_mtime, d->file_info.mtime); gg_dcc_fill_filetime(st.st_ctime, d->file_info.ctime); d->file_info.size = gg_fix32(st.st_size); d->file_info.mode = gg_fix32(0x20); /* FILE_ATTRIBUTE_ARCHIVE */ if (!(name = strrchr(filename, '/'))) name = filename; else name++; if (!(ext = strrchr(name, '.'))) ext = name + strlen(name); for (i = 0, p = name; i < 8 && p < ext; i++, p++) d->file_info.short_filename[i] = toupper(name[i]); if (i == 8 && p < ext) { d->file_info.short_filename[6] = '~'; d->file_info.short_filename[7] = '1'; } if (strlen(ext) > 0) { for (j = 0; *ext && j < 4; j++, p++) d->file_info.short_filename[i + j] = toupper(ext[j]); } for (q = d->file_info.short_filename; *q; q++) { if (*q == 185) { *q = 165; } else if (*q == 230) { *q = 198; } else if (*q == 234) { *q = 202; } else if (*q == 179) { *q = 163; } else if (*q == 241) { *q = 209; } else if (*q == 243) { *q = 211; } else if (*q == 156) { *q = 140; } else if (*q == 159) { *q = 143; } else if (*q == 191) { *q = 175; } } gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\", dos name \"%s\"\n", name, d->file_info.short_filename); strncpy((char*) d->file_info.filename, name, sizeof(d->file_info.filename) - 1); return 0; }
/* * gg_watch_fd() * * funkcja, któr± nale¿y wywo³aæ, gdy co¶ siê stanie z obserwowanym * deskryptorem. zwraca klientowi informacjê o tym, co siê dzieje. * * - sess - opis sesji * * wska¼nik do struktury gg_event, któr± trzeba zwolniæ pó¼niej * za pomoc± gg_event_free(). jesli rodzaj zdarzenia jest równy * GG_EVENT_NONE, nale¿y je zignorowaæ. je¶li zwróci³o NULL, * sta³o siê co¶ niedobrego -- albo zabrak³o pamiêci albo zerwa³o * po³±czenie. */ struct gg_event *gg_watch_fd(struct gg_session *sess) { struct gg_event *e; int res = 0; int port = 0; int errno2 = 0; gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess); if (!sess) { errno = EFAULT; return NULL; } if (!(e = (void*) calloc(1, sizeof(*e)))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n"); return NULL; } e->type = GG_EVENT_NONE; switch (sess->state) { case GG_STATE_RESOLVING: { struct in_addr addr; int failed = 0; gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n"); if (read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n"); failed = 1; errno2 = errno; } close(sess->fd); sess->fd = -1; #ifndef __GG_LIBGADU_HAVE_PTHREAD waitpid(sess->pid, NULL, 0); sess->pid = -1; #else if (sess->resolver) { gg_resolve_pthread_cleanup(sess->resolver, 0); sess->resolver = NULL; } #endif if (failed) { errno = errno2; goto fail_resolving; } /* je¶li jeste¶my w resolverze i mamy ustawiony port * proxy, znaczy, ¿e resolvowali¶my proxy. zatem * wpiszmy jego adres. */ if (sess->proxy_port) sess->proxy_addr = addr.s_addr; /* zapiszmy sobie adres huba i adres serwera (do * bezpo¶redniego po³±czenia, je¶li hub le¿y) * z resolvera. */ if (sess->proxy_addr && sess->proxy_port) port = sess->proxy_port; else { sess->server_addr = sess->hub_addr = addr.s_addr; port = GG_APPMSG_PORT; } gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port); /* ³±czymy siê albo z hubem, albo z proxy, zale¿nie * od tego, co resolvowali¶my. */ if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) { /* je¶li w trybie asynchronicznym gg_connect() * zwróci b³±d, nie ma sensu próbowaæ dalej. */ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); goto fail_connecting; } /* je¶li podano serwer i ³±czmy siê przez proxy, * jest to bezpo¶rednie po³±czenie, inaczej jest * do huba. */ sess->state = (sess->proxy_addr && sess->proxy_port && sess->server_addr) ? GG_STATE_CONNECTING_GG : GG_STATE_CONNECTING_HUB; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; break; } case GG_STATE_CONNECTING_HUB: { char buf[1024], *client, *auth; int res = 0, res_size = sizeof(res); const char *host, *appmsg; gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n"); /* je¶li asynchroniczne, sprawdzamy, czy nie wyst±pi³ * przypadkiem jaki¶ b³±d. */ if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { /* no tak, nie uda³o siê po³±czyæ z proxy. nawet * nie próbujemy dalej. */ if (sess->proxy_addr && sess->proxy_port) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); goto fail_connecting; } gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s), trying direct connection\n", res, strerror(res)); close(sess->fd); if ((sess->fd = gg_connect(&sess->hub_addr, GG_DEFAULT_PORT, sess->async)) == -1) { /* przy asynchronicznych, gg_connect() * zwraca -1 przy b³êdach socket(), * ioctl(), braku routingu itd. dlatego * nawet nie próbujemy dalej. */ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() direct connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); goto fail_connecting; } sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; break; } gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n"); if (!(client = gg_urlencode((sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n"); goto fail_connecting; } if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) host = "http://" GG_APPMSG_HOST; else host = ""; #ifdef __GG_LIBGADU_HAVE_OPENSSL if (sess->ssl) appmsg = "appmsg3.asp"; else #endif appmsg = "appmsg2.asp"; auth = gg_proxy_auth(); snprintf(buf, sizeof(buf) - 1, "GET %s/appsvc/%s?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n" "Host: " GG_APPMSG_HOST "\r\n" "User-Agent: " GG_HTTP_USERAGENT "\r\n" "Pragma: no-cache\r\n" "%s" "\r\n", host, appmsg, sess->uin, client, sess->last_sysmsg, (auth) ? auth : ""); if (auth) free(auth); free(client); /* zwolnij pamiêæ po wersji klienta. */ if (sess->client_version) { free(sess->client_version); sess->client_version = NULL; } gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf); /* zapytanie jest krótkie, wiêc zawsze zmie¶ci siê * do bufora gniazda. je¶li write() zwróci mniej, * sta³o siê co¶ z³ego. */ if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n"); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_WRITING; sess->state = GG_STATE_IDLE; close(sess->fd); sess->fd = -1; break; } sess->state = GG_STATE_READING_DATA; sess->check = GG_CHECK_READ; sess->timeout = GG_DEFAULT_TIMEOUT; break; } case GG_STATE_READING_DATA: { char buf[1024], *tmp, *host; int port = GG_DEFAULT_PORT; struct in_addr addr; gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n"); /* czytamy liniê z gniazda i obcinamy \r\n. */ gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf); /* sprawdzamy, czy wszystko w porz±dku. */ if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() that's not what we've expected, trying direct connection\n"); close(sess->fd); /* je¶li otrzymali¶my jakie¶ dziwne informacje, * próbujemy siê ³±czyæ z pominiêciem huba. */ if (sess->proxy_addr && sess->proxy_port) { if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { /* trudno. nie wysz³o. */ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; break; } sess->port = GG_DEFAULT_PORT; /* ³±czymy siê na port 8074 huba. */ if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); sess->port = GG_HTTPS_PORT; /* ³±czymy siê na port 443. */ if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } } sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; break; } /* ignorujemy resztê nag³ówka. */ while (strcmp(buf, "\r\n") && strcmp(buf, "")) gg_read_line(sess->fd, buf, sizeof(buf) - 1); /* czytamy pierwsz± liniê danych. */ gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); /* je¶li pierwsza liczba w linii nie jest równa zeru, * oznacza to, ¿e mamy wiadomo¶æ systemow±. */ if (atoi(buf)) { char tmp[1024], *foo, *sysmsg_buf = NULL; int len = 0; while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) { if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n"); break; } sysmsg_buf = foo; if (!len) strcpy(sysmsg_buf, tmp); else strcat(sysmsg_buf, tmp); len += strlen(tmp); } e->type = GG_EVENT_MSG; e->event.msg.msgclass = atoi(buf); e->event.msg.sender = 0; e->event.msg.message = sysmsg_buf; } close(sess->fd); gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf); /* analizujemy otrzymane dane. */ tmp = buf; while (*tmp && *tmp != ' ') tmp++; while (*tmp && *tmp == ' ') tmp++; host = tmp; while (*tmp && *tmp != ' ') tmp++; *tmp = 0; if ((tmp = strchr(host, ':'))) { *tmp = 0; port = atoi(tmp + 1); } if (!strcmp(host, "notoperating")) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno)); sess->fd = -1; goto fail_unavailable; } addr.s_addr = inet_addr(host); sess->server_addr = addr.s_addr; if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) { /* je¶li mamy proxy, ³±czymy siê z nim. */ if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { /* nie wysz³o? trudno. */ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; break; } sess->port = port; /* ³±czymy siê z w³a¶ciwym serwerem. */ if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); sess->port = GG_HTTPS_PORT; /* nie wysz³o? próbujemy portu 443. */ if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) { /* ostatnia deska ratunku zawiod³a? * w takim razie zwijamy manatki. */ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } } sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; break; } case GG_STATE_CONNECTING_GG: { int res = 0, res_size = sizeof(res); gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n"); /* je¶li wyst±pi³ b³±d podczas ³±czenia siê... */ if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { /* je¶li nie uda³o siê po³±czenie z proxy, * nie mamy czego próbowaæ wiêcej. */ if (sess->proxy_addr && sess->proxy_port) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); goto fail_connecting; } close(sess->fd); sess->fd = -1; #ifdef ETIMEDOUT if (sess->timeout == 0) errno = ETIMEDOUT; #endif #ifdef __GG_LIBGADU_HAVE_OPENSSL /* je¶li logujemy siê po TLS, nie próbujemy * siê ³±czyæ ju¿ z niczym innym w przypadku * b³êdu. nie do¶æ, ¿e nie ma sensu, to i * trzeba by siê bawiæ w tworzenie na nowo * SSL i SSL_CTX. */ if (sess->ssl) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); goto fail_connecting; } #endif gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res)); sess->port = GG_HTTPS_PORT; /* próbujemy na port 443. */ if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } } gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected\n"); if (gg_proxy_http_only) sess->proxy_port = 0; /* je¶li mamy proxy, wy¶lijmy zapytanie. */ if (sess->proxy_addr && sess->proxy_port) { char buf[100], *auth = gg_proxy_auth(); struct in_addr addr; if (sess->server_addr) addr.s_addr = sess->server_addr; else addr.s_addr = sess->hub_addr; snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port); gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n// %s", buf); /* wysy³amy zapytanie. jest ono na tyle krótkie, * ¿e musi siê zmie¶ciæ w buforze gniazda. je¶li * write() zawiedzie, sta³o siê co¶ z³ego. */ if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); if (auth) free(auth); goto fail_connecting; } if (auth) { gg_debug(GG_DEBUG_MISC, "// %s", auth); if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); free(auth); goto fail_connecting; } free(auth); } if (write(sess->fd, "\r\n", 2) < 2) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); goto fail_connecting; } } #ifdef __GG_LIBGADU_HAVE_OPENSSL if (sess->ssl) { SSL_set_fd(sess->ssl, sess->fd); sess->state = GG_STATE_TLS_NEGOTIATION; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; break; } #endif sess->state = GG_STATE_READING_KEY; sess->check = GG_CHECK_READ; sess->timeout = GG_DEFAULT_TIMEOUT; break; } #ifdef __GG_LIBGADU_HAVE_OPENSSL case GG_STATE_TLS_NEGOTIATION: { int res; X509 *peer; gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n"); if ((res = SSL_connect(sess->ssl)) <= 0) { int err = SSL_get_error(sess->ssl, res); if (res == 0) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n"); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_TLS; sess->state = GG_STATE_IDLE; close(sess->fd); sess->fd = -1; break; } if (err == SSL_ERROR_WANT_READ) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n"); sess->state = GG_STATE_TLS_NEGOTIATION; sess->check = GG_CHECK_READ; sess->timeout = GG_DEFAULT_TIMEOUT; break; } else if (err == SSL_ERROR_WANT_WRITE) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n"); sess->state = GG_STATE_TLS_NEGOTIATION; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; break; } else { char buf[1024]; ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_TLS; sess->state = GG_STATE_IDLE; close(sess->fd); sess->fd = -1; break; } } gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n// cipher: %s\n", SSL_get_cipher_name(sess->ssl)); peer = SSL_get_peer_certificate(sess->ssl); if (!peer) gg_debug(GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n"); else { char buf[1024]; X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf)); gg_debug(GG_DEBUG_MISC, "// cert subject: %s\n", buf); X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf)); gg_debug(GG_DEBUG_MISC, "// cert issuer: %s\n", buf); } sess->state = GG_STATE_READING_KEY; sess->check = GG_CHECK_READ; sess->timeout = GG_DEFAULT_TIMEOUT; break; } #endif case GG_STATE_READING_KEY: { struct gg_header *h; struct gg_welcome *w; struct gg_login60 l; unsigned int hash; unsigned char *password = sess->password; int ret; gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n"); memset(&l, 0, sizeof(l)); l.dunno2 = 0xbe; /* XXX bardzo, bardzo, bardzo g³upi pomys³ na pozbycie * siê tekstu wrzucanego przez proxy. */ if (sess->proxy_addr && sess->proxy_port) { char buf[100]; strcpy(buf, ""); gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n// %s\n", buf); while (strcmp(buf, "")) { gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); if (strcmp(buf, "")) gg_debug(GG_DEBUG_MISC, "// %s\n", buf); } /* XXX niech czeka jeszcze raz w tej samej * fazie. g³upio, ale dzia³a. */ sess->proxy_port = 0; break; } /* czytaj pierwszy pakiet. */ if (!(h = gg_recv_packet(sess))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_READING; sess->state = GG_STATE_IDLE; errno2 = errno; close(sess->fd); errno = errno2; sess->fd = -1; break; } if (h->type != GG_WELCOME) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n"); free(h); close(sess->fd); sess->fd = -1; errno = EINVAL; e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_INVALID; sess->state = GG_STATE_IDLE; break; } w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header)); w->key = gg_fix32(w->key); hash = gg_login_hash(password, w->key); gg_debug(GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> hash %.8x\n", w->key, hash); free(h); free(sess->password); sess->password = NULL; { struct in_addr dcc_ip; dcc_ip.s_addr = gg_dcc_ip; gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() gg_dcc_ip = %s\n", inet_ntoa(dcc_ip)); } if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) { struct sockaddr_in sin; int sin_len = sizeof(sin); gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n"); if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); l.local_ip = sin.sin_addr.s_addr; } else { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); l.local_ip = 0; } } else l.local_ip = gg_dcc_ip; l.uin = gg_fix32(sess->uin); l.hash = gg_fix32(hash); l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); l.version = gg_fix32(sess->protocol_version); l.local_port = gg_fix16(gg_dcc_port); l.image_size = sess->image_size; if (sess->external_addr && sess->external_port > 1023) { l.external_ip = sess->external_addr; l.external_port = gg_fix16(sess->external_port); } gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN60 packet\n"); ret = gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, NULL); free(sess->initial_descr); sess->initial_descr = NULL; if (ret == -1) { gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno)); errno2 = errno; close(sess->fd); errno = errno2; sess->fd = -1; e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_WRITING; sess->state = GG_STATE_IDLE; break; } sess->state = GG_STATE_READING_REPLY; break; } case GG_STATE_READING_REPLY: { struct gg_header *h; gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n"); if (!(h = gg_recv_packet(sess))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_READING; sess->state = GG_STATE_IDLE; errno2 = errno; close(sess->fd); errno = errno2; sess->fd = -1; break; } if (h->type == GG_LOGIN_OK || h->type == GG_NEED_EMAIL) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n"); e->type = GG_EVENT_CONN_SUCCESS; sess->state = GG_STATE_CONNECTED; sess->timeout = -1; sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL; free(h); break; } if (h->type == GG_LOGIN_FAILED) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login failed\n"); e->event.failure = GG_FAILURE_PASSWORD; errno = EACCES; } else if (h->type == GG_DISCONNECTING) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n"); e->event.failure = GG_FAILURE_INTRUDER; errno = EACCES; } else { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n"); e->event.failure = GG_FAILURE_INVALID; errno = EINVAL; } e->type = GG_EVENT_CONN_FAILED; sess->state = GG_STATE_IDLE; errno2 = errno; close(sess->fd); errno = errno2; sess->fd = -1; free(h); break; } case GG_STATE_CONNECTED: { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n"); sess->last_event = time(NULL); if ((res = gg_watch_fd_connected(sess, e)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno)); if (errno == EAGAIN) { e->type = GG_EVENT_NONE; res = 0; } else res = -1; } break; } } done: if (res == -1) { free(e); e = NULL; } return e; fail_connecting: if (sess->fd != -1) { errno2 = errno; close(sess->fd); errno = errno2; sess->fd = -1; } e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_CONNECTING; sess->state = GG_STATE_IDLE; goto done; fail_resolving: e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_RESOLVING; sess->state = GG_STATE_IDLE; goto done; fail_unavailable: e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_UNAVAILABLE; sess->state = GG_STATE_IDLE; goto done; }
/* * gg_watch_fd_connected() // funkcja wewnêtrzna * * patrzy na gniazdo, odbiera pakiet i wype³nia strukturê zdarzenia. * * - sess - struktura opisuj±ca sesjê * - e - opis zdarzenia * * 0, -1. */ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e) { struct gg_header *h = NULL; char *p; gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e); if (!sess) { errno = EFAULT; return -1; } if (!(h = gg_recv_packet(sess))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail; } p = (char*) h + sizeof(struct gg_header); switch (h->type) { case GG_RECV_MSG: { if (h->length >= sizeof(struct gg_recv_msg)) if (gg_handle_recv_msg(h, e, sess)) goto fail; break; } case GG_NOTIFY_REPLY: { struct gg_notify_reply *n = (void*) p; unsigned int count, i; char *tmp; gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); if (h->length < sizeof(*n)) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n"); errno = EINVAL; goto fail; } if (gg_fix32(n->status) == GG_STATUS_BUSY_DESCR || gg_fix32(n->status) == GG_STATUS_NOT_AVAIL_DESCR || gg_fix32(n->status) == GG_STATUS_AVAIL_DESCR) { e->type = GG_EVENT_NOTIFY_DESCR; if (!(e->event.notify_descr.notify = (void*) malloc(sizeof(*n) * 2))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } e->event.notify_descr.notify[1].uin = 0; memcpy(e->event.notify_descr.notify, p, sizeof(*n)); e->event.notify_descr.notify[0].uin = gg_fix32(e->event.notify_descr.notify[0].uin); e->event.notify_descr.notify[0].status = gg_fix32(e->event.notify_descr.notify[0].status); e->event.notify_descr.notify[0].remote_port = gg_fix16(e->event.notify_descr.notify[0].remote_port); count = h->length - sizeof(*n); if (!(tmp = malloc(count + 1))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } memcpy(tmp, p + sizeof(*n), count); tmp[count] = 0; e->event.notify_descr.descr = tmp; } else { e->type = GG_EVENT_NOTIFY; if (!(e->event.notify = (void*) malloc(h->length + 2 * sizeof(*n)))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } memcpy(e->event.notify, p, h->length); count = h->length / sizeof(*n); e->event.notify[count].uin = 0; for (i = 0; i < count; i++) { e->event.notify[i].uin = gg_fix32(e->event.notify[i].uin); e->event.notify[i].status = gg_fix32(e->event.notify[i].status); e->event.notify[i].remote_port = gg_fix16(e->event.notify[i].remote_port); } } break; } case GG_STATUS: { struct gg_status *s = (void*) p; gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); if (h->length >= sizeof(*s)) { e->type = GG_EVENT_STATUS; memcpy(&e->event.status, p, sizeof(*s)); e->event.status.uin = gg_fix32(e->event.status.uin); e->event.status.status = gg_fix32(e->event.status.status); if (h->length > sizeof(*s)) { int len = h->length - sizeof(*s); char *buf = malloc(len + 1); if (buf) { memcpy(buf, p + sizeof(*s), len); buf[len] = 0; } e->event.status.descr = buf; } else e->event.status.descr = NULL; } break; } case GG_NOTIFY_REPLY60: { struct gg_notify_reply60 *n = (void*) p; unsigned int length = h->length, i = 0; gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); e->type = GG_EVENT_NOTIFY60; e->event.notify60 = malloc(sizeof(*e->event.notify60)); if (!e->event.notify60) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } e->event.notify60[0].uin = 0; while (length >= sizeof(struct gg_notify_reply60)) { uin_t uin = gg_fix32(n->uin); char *tmp; e->event.notify60[i].uin = uin & 0x00ffffff; e->event.notify60[i].status = n->status; e->event.notify60[i].remote_ip = n->remote_ip; e->event.notify60[i].remote_port = gg_fix16(n->remote_port); e->event.notify60[i].version = n->version; e->event.notify60[i].image_size = n->image_size; e->event.notify60[i].descr = NULL; e->event.notify60[i].time = 0; if (uin & 0x40000000) e->event.notify60[i].version |= GG_HAS_AUDIO_MASK; if (uin & 0x08000000) e->event.notify60[i].version |= GG_ERA_OMNIX_MASK; if (GG_S_D(n->status)) { unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60)); if (descr_len < length) { if (!(e->event.notify60[i].descr = malloc(descr_len + 1))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } memcpy(e->event.notify60[i].descr, (char*) n + sizeof(struct gg_notify_reply60) + 1, descr_len); e->event.notify60[i].descr[descr_len] = 0; /* XXX czas */ } length -= sizeof(struct gg_notify_reply60) + descr_len + 1; n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1); } else { length -= sizeof(struct gg_notify_reply60); n = (void*) ((char*) n + sizeof(struct gg_notify_reply60)); } if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); free(e->event.notify60); goto fail; } e->event.notify60 = (void*) tmp; e->event.notify60[++i].uin = 0; } break; } case GG_STATUS60: { struct gg_status60 *s = (void*) p; uint32_t uin; gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); if (h->length < sizeof(*s)) break; uin = gg_fix32(s->uin); e->type = GG_EVENT_STATUS60; e->event.status60.uin = uin & 0x00ffffff; e->event.status60.status = s->status; e->event.status60.remote_ip = s->remote_ip; e->event.status60.remote_port = gg_fix16(s->remote_port); e->event.status60.version = s->version; e->event.status60.image_size = s->image_size; e->event.status60.descr = NULL; e->event.status60.time = 0; if (uin & 0x40000000) e->event.status60.version |= GG_HAS_AUDIO_MASK; if (uin & 0x08000000) e->event.status60.version |= GG_ERA_OMNIX_MASK; if (h->length > sizeof(*s)) { int len = h->length - sizeof(*s); char *buf = malloc(len + 1); if (buf) { memcpy(buf, (char*) p + sizeof(*s), len); buf[len] = 0; } e->event.status60.descr = buf; if (len > 4 && p[h->length - 5] == 0) { uint32_t t; memcpy(&t, p + h->length - 4, sizeof(uint32_t)); e->event.status60.time = gg_fix32(t); } } break; } case GG_SEND_MSG_ACK: { struct gg_send_msg_ack *s = (void*) p; gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n"); if (h->length < sizeof(*s)) break; e->type = GG_EVENT_ACK; e->event.ack.status = gg_fix32(s->status); e->event.ack.recipient = gg_fix32(s->recipient); e->event.ack.seq = gg_fix32(s->seq); break; } case GG_PONG: { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n"); e->type = GG_EVENT_PONG; sess->last_pong = time(NULL); break; } case GG_DISCONNECTING: { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n"); e->type = GG_EVENT_DISCONNECT; break; } case GG_PUBDIR50_REPLY: { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n"); if (gg_pubdir50_handle_reply(e, p, h->length) == -1) goto fail; break; } case GG_USERLIST_REPLY: { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n"); if (h->length < 1) break; /* je¶li odpowied¼ na eksport, wywo³aj zdarzenie tylko * gdy otrzymano wszystkie odpowiedzi */ if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) { if (--sess->userlist_blocks) break; p[0] = GG_USERLIST_PUT_REPLY; } if (h->length > 1) { char *tmp; unsigned int len = (sess->userlist_reply) ? strlen(sess->userlist_reply) : 0; gg_debug(GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len); if (!(tmp = realloc(sess->userlist_reply, len + h->length))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n"); free(sess->userlist_reply); sess->userlist_reply = NULL; goto fail; } sess->userlist_reply = tmp; sess->userlist_reply[len + h->length - 1] = 0; memcpy(sess->userlist_reply + len, p + 1, h->length - 1); } if (p[0] == GG_USERLIST_GET_MORE_REPLY) break; e->type = GG_EVENT_USERLIST; e->event.userlist.type = p[0]; e->event.userlist.reply = sess->userlist_reply; sess->userlist_reply = NULL; break; } default: gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type); } free(h); return 0; fail: free(h); return -1; }
/* * gg_handle_recv_msg() // funkcja wewnêtrzna * * obs³uguje pakiet z przychodz±c± wiadomo¶ci±, rozbijaj±c go na dodatkowe * struktury (konferencje, kolorki) w razie potrzeby. * * - h - nag³ówek pakietu * - e - opis zdarzenia * * 0, -1. */ static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess) { struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header)); char *p, *packet_end = (char*) r + h->length; gg_debug(GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e); if (!r->seq && !r->msgclass) { gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n"); e->type = GG_EVENT_NONE; return 0; } for (p = (char*) r + sizeof(*r); *p; p++) { if (*p == 0x02 && p == packet_end - 1) { gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n"); break; } if (p >= packet_end) { gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n"); goto malformed; } } p++; /* przeanalizuj dodatkowe opcje */ while (p < packet_end) { switch (*p) { case 0x01: /* konferencja */ { struct gg_msg_recipients *m = (void*) p; uint32_t i, count; p += sizeof(*m); if (p > packet_end) { gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n"); goto malformed; } count = gg_fix32(m->count); if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) { gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n"); goto malformed; } if (!(e->event.msg.recipients = (void*) malloc(count * sizeof(uin_t)))) { gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n"); goto fail; } for (i = 0; i < count; i++, p += sizeof(uint32_t)) { uint32_t u; memcpy(&u, p, sizeof(uint32_t)); e->event.msg.recipients[i] = gg_fix32(u); } e->event.msg.recipients_count = count; break; } case 0x02: /* richtext */ { uint16_t len; char *buf; if (p + 3 > packet_end) { gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n"); goto malformed; } memcpy(&len, p + 1, sizeof(uint16_t)); len = gg_fix16(len); if (!(buf = malloc(len))) { gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n"); goto fail; } p += 3; if (p + len > packet_end) { gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); free(buf); goto malformed; } memcpy(buf, p, len); e->event.msg.formats = buf; e->event.msg.formats_length = len; p += len; break; } case 0x04: /* image_request */ { struct gg_msg_image_request *i = (void*) p; if (p + sizeof(*i) > packet_end) { gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); goto malformed; } e->event.image_request.sender = gg_fix32(r->sender); e->event.image_request.size = gg_fix32(i->size); e->event.image_request.crc32 = gg_fix32(i->crc32); e->type = GG_EVENT_IMAGE_REQUEST; return 0; } case 0x05: /* image_reply */ case 0x06: { struct gg_msg_image_reply *rep = (void*) p; if (p + sizeof(struct gg_msg_image_reply) == packet_end) { /* pusta odpowied¼ - klient po drugiej stronie nie ma ¿±danego obrazka */ e->type = GG_EVENT_IMAGE_REPLY; e->event.image_reply.sender = gg_fix32(r->sender); e->event.image_reply.size = 0; e->event.image_reply.crc32 = gg_fix32(rep->crc32); e->event.image_reply.filename = NULL; e->event.image_reply.image = NULL; return 0; } else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) { gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n"); goto malformed; } rep->size = gg_fix32(rep->size); rep->crc32 = gg_fix32(rep->crc32); gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, gg_fix32(r->sender)); return 0; } default: { gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p); p = packet_end; } } } e->type = GG_EVENT_MSG; e->event.msg.msgclass = gg_fix32(r->msgclass); e->event.msg.sender = gg_fix32(r->sender); e->event.msg.time = gg_fix32(r->time); e->event.msg.message = strdup((char*) r + sizeof(*r)); return 0; malformed: e->type = GG_EVENT_NONE; free(e->event.msg.recipients); free(e->event.msg.formats); return 0; fail: free(e->event.msg.recipients); free(e->event.msg.formats); return -1; }
/** * \internal Obsługuje pakiet informacji o połączeniu bezpośrednim. * * \param sess Struktura sesji * \param e Struktura zdarzenia * \param payload Treść pakietu * \param len Długość pakietu * * \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) { 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); 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; 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; } 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; } // 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_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; e->event.dcc7_accept.type = gg_fix32(p->type); e->event.dcc7_accept.remote_ip = dcc->remote_addr; e->event.dcc7_accept.remote_port = dcc->remote_port; } else { e->type = GG_EVENT_DCC7_PENDING; } if (gg_dcc7_connect(sess, dcc) == -1) { if (gg_dcc7_reverse_connect(dcc) == -1) { e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_NET; return 0; } } return 0; }
/** * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. * * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania. * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free(). * * \param dcc Struktura połączenia * * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd * * \ingroup dcc7 */ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc) { struct gg_event *e; gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_watch_fd(%p)\n", dcc); if (!dcc || (dcc->type != GG_SESSION_DCC7_SEND && dcc->type != GG_SESSION_DCC7_GET && dcc->type != GG_SESSION_DCC7_VOICE)) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n"); errno = EINVAL; return NULL; } if (!(e = malloc(sizeof(struct gg_event)))) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n"); return NULL; } memset(e, 0, sizeof(struct gg_event)); e->type = GG_EVENT_NONE; switch (dcc->state) { case GG_STATE_LISTENING: { struct sockaddr_in sin; SOCKET fd; int one = 1; unsigned int sin_len = sizeof(sin); gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n"); if ((fd = accept(dcc->fd, (struct sockaddr*) &sin, &sin_len)) == -1) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno)); return e; } gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port)); #ifdef FIONBIO if (ioctl(fd, FIONBIO, &one) == -1) { #else if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { #endif gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() can't set nonblocking (%s)\n", strerror(errno)); gg_sock_close(fd); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; e->event.dcc7_error_ex.dcc7 = dcc; return e; } gg_sock_close(dcc->fd); dcc->fd = fd; dcc->state = GG_STATE_READING_ID; dcc->check = GG_CHECK_READ; dcc->timeout = GG_DEFAULT_TIMEOUT; dcc->incoming = 1; dcc->remote_port = ntohs(sin.sin_port); dcc->remote_addr = sin.sin_addr.s_addr; e->type = GG_EVENT_DCC7_CONNECTED; e->event.dcc7_connected.dcc7 = dcc; return e; } case GG_STATE_CONNECTING: { int res = 0, error = 0; unsigned int error_size = sizeof(error); gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n"); dcc->soft_timeout = 0; if (dcc->timeout == 0) error = ETIMEDOUT; if (error || (res = gg_getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &error, &error_size)) == -1 || error != 0) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error)); 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available"); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_RELAY; e->event.dcc7_error_ex.dcc7 = dcc; return e; } } else { 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.dcc7_error = GG_ERROR_DCC7_NET; e->event.dcc7_error_ex.dcc7 = dcc; } return e; } } gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connected, sending id\n"); dcc->state = GG_STATE_SENDING_ID; dcc->check = GG_CHECK_WRITE; dcc->timeout = GG_DEFAULT_TIMEOUT; dcc->incoming = 0; return e; } case GG_STATE_READING_ID: { int res; gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n"); if (!dcc->relay) { struct gg_dcc7_welcome_p2p welcome, welcome_ok; welcome_ok.id = dcc->cid; if ((res = gg_sock_read(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; e->event.dcc7_error_ex.dcc7 = dcc; return e; } if (memcmp(&welcome, &welcome_ok, sizeof(welcome))) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n"); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; e->event.dcc7_error_ex.dcc7 = dcc; 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 = gg_sock_read(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; e->event.dcc7_error_ex.dcc7 = dcc; return e; } if (memcmp(&welcome, &welcome_ok, sizeof(welcome)) != 0) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n"); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; e->event.dcc7_error_ex.dcc7 = dcc; return e; } } if (dcc->incoming) { dcc->state = GG_STATE_SENDING_ID; dcc->check = GG_CHECK_WRITE; dcc->timeout = GG_DEFAULT_TIMEOUT; } else { gg_dcc7_postauth_fixup(dcc); dcc->timeout = GG_DEFAULT_TIMEOUT; } return e; } case GG_STATE_SENDING_ID: { int res; gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n"); if (!dcc->relay) { struct gg_dcc7_welcome_p2p welcome; welcome.id = dcc->cid; if ((res = gg_sock_write(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)", res, strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; e->event.dcc7_error_ex.dcc7 = dcc; return e; } } else { struct gg_dcc7_welcome_server welcome; welcome.magic = gg_fix32(GG_DCC7_WELCOME_SERVER); welcome.id = dcc->cid; if ((res = gg_sock_write(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)\n", res, strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; e->event.dcc7_error_ex.dcc7 = dcc; return e; } } if (dcc->incoming) { gg_dcc7_postauth_fixup(dcc); dcc->timeout = GG_DEFAULT_TIMEOUT; } else { dcc->state = GG_STATE_READING_ID; dcc->check = GG_CHECK_READ; dcc->timeout = GG_DEFAULT_TIMEOUT; } return e; } case GG_STATE_SENDING_FILE: { char buf[1024]; int chunk, res; gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_SENDING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size); if (dcc->offset >= dcc->size) { gg_debug_session((dcc) ? (dcc)->sess : NULL, 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; } if (dcc->seek && lseek(dcc->file_fd, dcc->offset, SEEK_SET) == (off_t) -1) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() lseek() failed (%s)\n", strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_FILE; e->event.dcc7_error_ex.dcc7 = dcc; return e; } if ((chunk = dcc->size - dcc->offset) > sizeof(buf)) chunk = sizeof(buf); if ((res = read(dcc->file_fd, buf, chunk)) < 1) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (res=%d, %s)\n", res, strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF; e->event.dcc7_error_ex.dcc7 = dcc; return e; } if ((res = gg_sock_write(dcc->fd, buf, res)) == -1) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%s)\n", strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_NET; e->event.dcc7_error_ex.dcc7 = dcc; return e; } dcc->offset += res; if (dcc->offset >= dcc->size) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n"); e->type = GG_EVENT_DCC7_DONE; e->event.dcc7_done.dcc7 = dcc; return e; } dcc->state = GG_STATE_SENDING_FILE; dcc->check = GG_CHECK_WRITE; dcc->timeout = GG_DCC7_TIMEOUT_SEND; return e; } case GG_STATE_GETTING_FILE: { char buf[1024]; int res, wres; gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_GETTING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size); if (dcc->offset >= dcc->size) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n"); e->type = GG_EVENT_DCC7_DONE; e->event.dcc7_done.dcc7 = dcc; return e; } if ((res = gg_sock_read(dcc->fd, buf, sizeof(buf))) < 1) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (fd=%d, res=%d, %s)\n", dcc->fd, res, strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = (res == -1) ? GG_ERROR_DCC7_NET : GG_ERROR_DCC7_EOF; e->event.dcc7_error_ex.dcc7 = dcc; return e; } // XXX zapisywać do skutku? if ((wres = write(dcc->file_fd, buf, res)) < res) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (fd=%d, res=%d, %s)\n", dcc->file_fd, wres, strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_FILE; e->event.dcc7_error_ex.dcc7 = dcc; return e; } dcc->offset += res; if (dcc->offset >= dcc->size) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n"); e->type = GG_EVENT_DCC7_DONE; e->event.dcc7_done.dcc7 = dcc; return e; } dcc->state = GG_STATE_GETTING_FILE; dcc->check = GG_CHECK_READ; dcc->timeout = GG_DCC7_TIMEOUT_GET; return e; } case GG_STATE_RESOLVING_RELAY: { struct in_addr addr; gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_RESOLVING_RELAY\n"); if (gg_sock_read(dcc->fd, &addr, sizeof(addr)) < sizeof(addr) || addr.s_addr == INADDR_NONE) { int errno_save = errno; gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolving failed\n"); gg_sock_close(dcc->fd); dcc->fd = -1; dcc->sess->resolver_cleanup(&dcc->resolver, 0); errno = errno_save; e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_RELAY; e->event.dcc7_error_ex.dcc7 = dcc; return e; } gg_debug_session((dcc) ? (dcc)->sess : NULL, 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_session((dcc) ? (dcc)->sess : NULL, 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.dcc7_error = GG_ERROR_DCC7_RELAY; e->event.dcc7_error_ex.dcc7 = dcc; 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING_RELAY\n"); if (gg_getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) != 0 || res != 0) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_RELAY; e->event.dcc7_error_ex.dcc7 = dcc; 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_dump_session((dcc) ? (dcc)->sess : NULL, &pkt, sizeof(pkt), "// gg_dcc7_watch_fd() send pkt(0x%.2x)\n", gg_fix32(pkt.magic)); if ((res = gg_sock_write(dcc->fd, &pkt, sizeof(pkt))) != sizeof(pkt)) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() sending failed\n"); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_RELAY; e->event.dcc7_error_ex.dcc7 = dcc; 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_RELAY\n"); if ((res = gg_sock_read(dcc->fd, buf, sizeof(buf))) < sizeof(*pkt)) { if (res == 0) errno = ECONNRESET; gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_RELAY; e->event.dcc7_error_ex.dcc7 = dcc; 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_wathc_fd() invalid reply\n"); errno = EINVAL; e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_RELAY; e->event.dcc7_error_ex.dcc7 = dcc; return e; } gg_debug_dump_session((dcc) ? (dcc)->sess : NULL, buf, res, "// gg_dcc7_get_relay() read pkt(0x%.2x)\n", gg_fix32(pkt->magic)); 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_session((dcc) ? (dcc)->sess : NULL, 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_session((dcc) ? (dcc)->sess : NULL, 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available"); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_RELAY; e->event.dcc7_error_ex.dcc7 = dcc; return e; } return e; } default: { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n"); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; e->event.dcc7_error_ex.dcc7 = dcc; return e; } } return e; } /** * Zwalnia zasoby używane przez połączenie bezpośrednie. * * \param dcc Struktura połączenia * * \ingroup dcc7 */ void gg_dcc7_free(struct gg_dcc7 *dcc) { gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc); if (!dcc) return; if (dcc->fd != -1) gg_sock_close(dcc->fd); if (dcc->file_fd != -1) close(dcc->file_fd); if (dcc->sess) gg_dcc7_session_remove(dcc->sess, dcc); free(dcc->relay_list); free(dcc); }
/** * Wysyła zapytanie katalogu publicznego do serwera. * * \param sess Struktura sesji * \param req Zapytanie * * \return Numer sekwencyjny zapytania lub 0 w przypadku błędu * * \ingroup pubdir50 */ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req) { size_t size = 5; int i; uint32_t res; char *buf, *p; struct gg_pubdir50_request *r; gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req); if (!sess || !req) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n"); errno = EFAULT; return 0; } if (sess->state != GG_STATE_CONNECTED) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() not connected\n"); errno = ENOTCONN; return 0; } for (i = 0; i < req->entries_count; i++) { /* wyszukiwanie bierze tylko pierwszy wpis */ if (req->entries[i].num) continue; if (sess->encoding == GG_ENCODING_CP1250) { size += strlen(req->entries[i].field) + 1; size += strlen(req->entries[i].value) + 1; } else { char *tmp; tmp = gg_utf8_to_cp(req->entries[i].field); if (tmp == NULL) return -1; size += strlen(tmp) + 1; free(tmp); tmp = gg_utf8_to_cp(req->entries[i].value); if (tmp == NULL) return -1; size += strlen(tmp) + 1; free(tmp); } } if (!(buf = (char*)malloc(size))) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size); return 0; } if (!req->seq) req->seq = (uint32_t)time(NULL); res = req->seq; r = (struct gg_pubdir50_request*) buf; r->type = req->type; r->seq = gg_fix32(req->seq); for (i = 0, p = buf + 5; i < req->entries_count; i++) { if (req->entries[i].num) continue; if (sess->encoding == GG_ENCODING_CP1250) { strcpy(p, req->entries[i].field); p += strlen(p) + 1; strcpy(p, req->entries[i].value); p += strlen(p) + 1; } else { char *tmp; tmp = gg_utf8_to_cp(req->entries[i].field); if (tmp == NULL) { free(buf); return -1; } strcpy(p, tmp); p += strlen(tmp) + 1; free(tmp); tmp = gg_utf8_to_cp(req->entries[i].value); if (tmp == NULL) { free(buf); return -1; } strcpy(p, tmp); p += strlen(tmp) + 1; free(tmp); } } if (gg_send_packet(sess, GG_PUBDIR50_REQUEST, buf, size, NULL, 0) == -1) res = 0; free(buf); return res; }
/** * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. * * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania. * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free. * * \param h Struktura połączenia * * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd * * \ingroup dcc6 */ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h) { struct gg_event *e; int foo; gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_watch_fd(%p);\n", h); if (!h || (h->type != GG_SESSION_DCC && h->type != GG_SESSION_DCC_SOCKET && h->type != GG_SESSION_DCC_SEND && h->type != GG_SESSION_DCC_GET && h->type != GG_SESSION_DCC_VOICE)) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid argument\n"); errno = EINVAL; return NULL; } if (!(e = (void*) calloc(1, sizeof(*e)))) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory\n"); return NULL; } e->type = GG_EVENT_NONE; if (h->type == GG_SESSION_DCC_SOCKET) { struct sockaddr_in sin; struct gg_dcc *c; int fd; #ifdef FIONBIO int one = 1; #endif socklen_t sin_len = sizeof(sin); if ((fd = accept(h->fd, (struct sockaddr*) &sin, &sin_len)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't accept() new connection (errno=%d, %s)\n", errno, strerror(errno)); return e; } gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() new direct connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port)); #ifdef FIONBIO if (ioctl(fd, FIONBIO, &one) == -1) { #else if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { #endif gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't set nonblocking (errno=%d, %s)\n", errno, strerror(errno)); close(fd); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; return e; } if (!(c = (void*) calloc(1, sizeof(*c)))) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory for client data\n"); free(e); close(fd); return NULL; } c->fd = fd; c->check = GG_CHECK_READ; c->state = GG_STATE_READING_UIN_1; c->type = GG_SESSION_DCC; c->timeout = GG_DEFAULT_TIMEOUT; c->file_fd = -1; c->remote_addr = sin.sin_addr.s_addr; c->remote_port = ntohs(sin.sin_port); e->type = GG_EVENT_DCC_NEW; e->event.dcc_new = c; return e; } else { struct gg_dcc_tiny_packet tiny_pkt; struct gg_dcc_small_packet small_pkt; struct gg_dcc_big_packet big_pkt; int size, tmp, res; unsigned int utmp; socklen_t res_size = sizeof(res); char buf[1024], ack[] = "UDAG"; struct gg_dcc_file_info_packet { struct gg_dcc_big_packet big; struct gg_file_info file_info; } GG_PACKED; struct gg_dcc_file_info_packet file_info_packet; switch (h->state) { case GG_STATE_READING_UIN_1: case GG_STATE_READING_UIN_2: { uin_t uin; gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_READING_UIN_%d\n", (h->state == GG_STATE_READING_UIN_1) ? 1 : 2); gg_dcc_read(h->fd, &uin, sizeof(uin)); if (h->state == GG_STATE_READING_UIN_1) { h->state = GG_STATE_READING_UIN_2; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; h->peer_uin = gg_fix32(uin); } else { h->state = GG_STATE_SENDING_ACK; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; h->uin = gg_fix32(uin); e->type = GG_EVENT_DCC_CLIENT_ACCEPT; } return e; } case GG_STATE_SENDING_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n"); gg_dcc_write(h->fd, ack, 4); h->state = GG_STATE_READING_TYPE; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; return e; case GG_STATE_READING_TYPE: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n"); gg_dcc_read(h->fd, &small_pkt, sizeof(small_pkt)); small_pkt.type = gg_fix32(small_pkt.type); switch (small_pkt.type) { case 0x0003: /* XXX */ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() callback\n"); h->type = GG_SESSION_DCC_SEND; h->state = GG_STATE_SENDING_FILE_INFO; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; e->type = GG_EVENT_DCC_CALLBACK; break; case 0x0002: /* XXX */ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() dialin\n"); h->type = GG_SESSION_DCC_GET; h->state = GG_STATE_READING_REQUEST; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; h->incoming = 1; break; default: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc type (%.4x) from %ld\n", small_pkt.type, h->peer_uin); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; } return e; case GG_STATE_READING_REQUEST: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n"); gg_dcc_read(h->fd, &small_pkt, sizeof(small_pkt)); small_pkt.type = gg_fix32(small_pkt.type); switch (small_pkt.type) { case 0x0001: /* XXX */ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() file transfer request\n"); h->state = GG_STATE_READING_FILE_INFO; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; break; case 0x0003: /* XXX */ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() voice chat request\n"); h->state = GG_STATE_SENDING_VOICE_ACK; h->check = GG_CHECK_WRITE; h->timeout = GG_DCC_TIMEOUT_VOICE_ACK; h->type = GG_SESSION_DCC_VOICE; e->type = GG_EVENT_DCC_NEED_VOICE_ACK; break; default: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc request (%.4x) from %ld\n", small_pkt.type, h->peer_uin); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; } return e; case GG_STATE_READING_FILE_INFO: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_INFO\n"); gg_dcc_read(h->fd, &file_info_packet, sizeof(file_info_packet)); memcpy(&h->file_info, &file_info_packet.file_info, sizeof(h->file_info)); h->file_info.mode = gg_fix32(h->file_info.mode); h->file_info.size = gg_fix32(h->file_info.size); h->state = GG_STATE_SENDING_FILE_ACK; h->check = GG_CHECK_WRITE; h->timeout = GG_DCC_TIMEOUT_FILE_ACK; e->type = GG_EVENT_DCC_NEED_FILE_ACK; return e; case GG_STATE_SENDING_FILE_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_ACK\n"); big_pkt.type = gg_fix32(0x0006); /* XXX */ big_pkt.dunno1 = gg_fix32(h->offset); big_pkt.dunno2 = 0; gg_dcc_write(h->fd, &big_pkt, sizeof(big_pkt)); h->state = GG_STATE_READING_FILE_HEADER; h->chunk_size = sizeof(big_pkt); h->chunk_offset = 0; if (!(h->chunk_buf = malloc(sizeof(big_pkt)))) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n"); free(e); return NULL; } h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; return e; case GG_STATE_SENDING_VOICE_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_ACK\n"); tiny_pkt.type = 0x01; /* XXX */ gg_dcc_write(h->fd, &tiny_pkt, sizeof(tiny_pkt)); h->state = GG_STATE_READING_VOICE_HEADER; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; h->offset = 0; return e; case GG_STATE_READING_FILE_HEADER: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_HEADER\n"); tmp = recv(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset, 0); if (tmp == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; return e; } gg_dcc_debug_data("read", h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); h->chunk_offset += tmp; if (h->chunk_offset < h->chunk_size) return e; memcpy(&big_pkt, h->chunk_buf, sizeof(big_pkt)); free(h->chunk_buf); h->chunk_buf = NULL; big_pkt.type = gg_fix32(big_pkt.type); h->chunk_size = gg_fix32(big_pkt.dunno1); h->chunk_offset = 0; if (big_pkt.type == 0x0005) { /* XXX */ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() transfer refused\n"); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_REFUSED; return e; } if (h->chunk_size == 0) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() empty chunk, EOF\n"); e->type = GG_EVENT_DCC_DONE; return e; } h->state = GG_STATE_GETTING_FILE; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; h->established = 1; return e; case GG_STATE_READING_VOICE_HEADER: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_HEADER\n"); gg_dcc_read(h->fd, &tiny_pkt, sizeof(tiny_pkt)); switch (tiny_pkt.type) { case 0x03: /* XXX */ h->state = GG_STATE_READING_VOICE_SIZE; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; h->established = 1; break; case 0x04: /* XXX */ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() peer breaking connection\n"); /* XXX zwracać odpowiedni event */ default: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown request (%.2x)\n", tiny_pkt.type); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; } return e; case GG_STATE_READING_VOICE_SIZE: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_SIZE\n"); gg_dcc_read(h->fd, &small_pkt, sizeof(small_pkt)); small_pkt.type = gg_fix32(small_pkt.type); if (small_pkt.type < 16 || small_pkt.type > sizeof(buf)) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid voice frame size (%d)\n", small_pkt.type); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; return e; } h->chunk_size = small_pkt.type; h->chunk_offset = 0; if (!(h->voice_buf = malloc(h->chunk_size))) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n"); free(e); return NULL; } h->state = GG_STATE_READING_VOICE_DATA; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; return e; case GG_STATE_READING_VOICE_DATA: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_DATA\n"); tmp = recv(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset, 0); if (tmp < 1) { if (tmp == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed (errno=%d, %s)\n", errno, strerror(errno)); } else { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed, connection broken\n"); } e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; return e; } gg_dcc_debug_data("read", h->fd, h->voice_buf + h->chunk_offset, tmp); h->chunk_offset += tmp; if (h->chunk_offset >= h->chunk_size) { e->type = GG_EVENT_DCC_VOICE_DATA; e->event.dcc_voice_data.data = (unsigned char*) h->voice_buf; e->event.dcc_voice_data.length = h->chunk_size; h->state = GG_STATE_READING_VOICE_HEADER; h->voice_buf = NULL; } h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; return e; case GG_STATE_CONNECTING: { uin_t uins[2]; gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_CONNECTING\n"); res = 0; if ((foo = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size)) || res) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connection failed (fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n", h->fd, errno, strerror(errno), foo, res, strerror(res)); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; return e; } gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connected, sending uins\n"); uins[0] = gg_fix32(h->uin); uins[1] = gg_fix32(h->peer_uin); gg_dcc_write(h->fd, uins, sizeof(uins)); h->state = GG_STATE_READING_ACK; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; return e; } case GG_STATE_READING_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_ACK\n"); gg_dcc_read(h->fd, buf, 4); if (strncmp(buf, ack, 4)) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n"); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; return e; } h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; h->state = GG_STATE_SENDING_REQUEST; return e; case GG_STATE_SENDING_VOICE_REQUEST: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_REQUEST\n"); small_pkt.type = gg_fix32(0x0003); gg_dcc_write(h->fd, &small_pkt, sizeof(small_pkt)); h->state = GG_STATE_READING_VOICE_ACK; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; return e; case GG_STATE_SENDING_REQUEST: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_REQUEST\n"); small_pkt.type = (h->type == GG_SESSION_DCC_GET) ? gg_fix32(0x0003) : gg_fix32(0x0002); /* XXX */ gg_dcc_write(h->fd, &small_pkt, sizeof(small_pkt)); switch (h->type) { case GG_SESSION_DCC_GET: h->state = GG_STATE_READING_REQUEST; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; break; case GG_SESSION_DCC_SEND: h->state = GG_STATE_SENDING_FILE_INFO; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; if (h->file_fd == -1) e->type = GG_EVENT_DCC_NEED_FILE_INFO; break; case GG_SESSION_DCC_VOICE: h->state = GG_STATE_SENDING_VOICE_REQUEST; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; break; } return e; case GG_STATE_SENDING_FILE_INFO: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n"); if (h->file_fd == -1) { e->type = GG_EVENT_DCC_NEED_FILE_INFO; return e; } small_pkt.type = gg_fix32(0x0001); /* XXX */ gg_dcc_write(h->fd, &small_pkt, sizeof(small_pkt)); file_info_packet.big.type = gg_fix32(0x0003); /* XXX */ file_info_packet.big.dunno1 = 0; file_info_packet.big.dunno2 = 0; memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info)); /* zostają teraz u nas, więc odwracamy z powrotem */ h->file_info.size = gg_fix32(h->file_info.size); h->file_info.mode = gg_fix32(h->file_info.mode); gg_dcc_write(h->fd, &file_info_packet, sizeof(file_info_packet)); h->state = GG_STATE_READING_FILE_ACK; h->check = GG_CHECK_READ; h->timeout = GG_DCC_TIMEOUT_FILE_ACK; return e; case GG_STATE_READING_FILE_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_ACK\n"); gg_dcc_read(h->fd, &big_pkt, sizeof(big_pkt)); /* XXX sprawdzać wynik */ h->offset = gg_fix32(big_pkt.dunno1); h->state = GG_STATE_SENDING_FILE_HEADER; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; e->type = GG_EVENT_DCC_ACK; return e; case GG_STATE_READING_VOICE_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n"); gg_dcc_read(h->fd, &tiny_pkt, sizeof(tiny_pkt)); if (tiny_pkt.type != 0x01) { gg_debug(GG_DEBUG_MISC, "// invalid reply (%.2x), connection refused\n", tiny_pkt.type); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_REFUSED; return e; } h->state = GG_STATE_READING_VOICE_HEADER; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; e->type = GG_EVENT_DCC_ACK; return e; case GG_STATE_SENDING_FILE_HEADER: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_HEADER\n"); h->chunk_offset = 0; if ((h->chunk_size = h->file_info.size - h->offset) > 4096) { h->chunk_size = 4096; big_pkt.type = gg_fix32(0x0003); /* XXX */ } else big_pkt.type = gg_fix32(0x0002); /* XXX */ big_pkt.dunno1 = gg_fix32(h->chunk_size); big_pkt.dunno2 = 0; gg_dcc_write(h->fd, &big_pkt, sizeof(big_pkt)); h->state = GG_STATE_SENDING_FILE; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; h->established = 1; return e; case GG_STATE_SENDING_FILE: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE\n"); if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf)) utmp = sizeof(buf); gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset=%d, size=%d\n", h->offset, h->file_info.size); /* koniec pliku? */ if (h->file_info.size == 0) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof on empty file\n"); e->type = GG_EVENT_DCC_DONE; return e; } if (h->offset >= h->file_info.size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n"); e->type = GG_EVENT_DCC_DONE; return e; } lseek(h->file_fd, h->offset, SEEK_SET); size = read(h->file_fd, buf, utmp); /* błąd */ if (size == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_FILE; return e; } /* koniec pliku? */ if (size == 0) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n"); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_EOF; return e; } /* jeśli wczytaliśmy więcej, utnijmy. */ if (h->offset + size > h->file_info.size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() too much (read=%d, ofs=%d, size=%d)\n", size, h->offset, h->file_info.size); size = h->file_info.size - h->offset; if (size < 1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() reached EOF after cutting\n"); e->type = GG_EVENT_DCC_DONE; return e; } } tmp = send(h->fd, buf, size, 0); if (tmp == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() send() failed (%s)\n", strerror(errno)); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; return e; } if (tmp == 0) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() send() failed (connection reset)\n"); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; return e; } h->offset += tmp; if (h->offset >= h->file_info.size) { e->type = GG_EVENT_DCC_DONE; return e; } h->chunk_offset += tmp; if (h->chunk_offset >= h->chunk_size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n"); h->state = GG_STATE_SENDING_FILE_HEADER; h->timeout = GG_DEFAULT_TIMEOUT; } else { h->state = GG_STATE_SENDING_FILE; h->timeout = GG_DCC_TIMEOUT_SEND; } h->check = GG_CHECK_WRITE; return e; case GG_STATE_GETTING_FILE: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_GETTING_FILE\n"); if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf)) utmp = sizeof(buf); if (h->offset >= h->file_info.size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n"); e->type = GG_EVENT_DCC_DONE; return e; } size = recv(h->fd, buf, utmp, 0); gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() ofs=%d, size=%d, recv()=%d\n", h->offset, h->file_info.size, size); /* błąd */ if (size == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed. (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; return e; } /* koniec? */ if (size == 0) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() reached eof\n"); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_EOF; return e; } tmp = write(h->file_fd, buf, size); if (tmp == -1 || tmp < size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d:fd=%d:res=%d:%s)\n", tmp, h->file_fd, size, strerror(errno)); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; return e; } h->offset += size; if (h->offset >= h->file_info.size) { e->type = GG_EVENT_DCC_DONE; return e; } h->chunk_offset += size; if (h->chunk_offset >= h->chunk_size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n"); h->state = GG_STATE_READING_FILE_HEADER; h->timeout = GG_DEFAULT_TIMEOUT; h->chunk_offset = 0; h->chunk_size = sizeof(big_pkt); if (!(h->chunk_buf = malloc(sizeof(big_pkt)))) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n"); free(e); return NULL; } } else { h->state = GG_STATE_GETTING_FILE; h->timeout = GG_DCC_TIMEOUT_GET; } h->check = GG_CHECK_READ; return e; default: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_???\n"); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; return e; } } return e; } /** * Zwalnia zasoby używane przez połączenie bezpośrednie. * * \param d Struktura połączenia * * \ingroup dcc6 */ void gg_dcc_free(struct gg_dcc *d) { gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d); if (!d) return; if (d->fd != -1) close(d->fd); if (d->file_fd != -1) gg_file_close(d->file_fd); free(d->chunk_buf); free(d); }
/** * \internal Obsługuje pakiet nowego połączenia bezpośredniego. * * \param sess Struktura sesji * \param e Struktura zdarzenia * \param payload Treść pakietu * \param len Długość pakietu * * \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) { 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); switch (gg_fix32(p->type)) { case GG_DCC7_TYPE_FILE: if (!(dcc = malloc(sizeof(struct gg_dcc7)))) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() not enough memory\n"); return -1; } memset(dcc, 0, sizeof(struct gg_dcc7)); dcc->type = GG_SESSION_DCC7_GET; dcc->dcc_type = GG_DCC7_TYPE_FILE; dcc->fd = -1; dcc->file_fd = -1; dcc->uin = sess->uin; dcc->peer_uin = gg_fix32(p->uin_from); dcc->cid = p->id; dcc->sess = sess; if (gg_dcc7_session_add(sess, dcc) == -1) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n"); gg_dcc7_free(dcc); return -1; } dcc->size = gg_fix32(p->size); strncpy((char*) dcc->filename, (char*) p->filename, GG_DCC7_FILENAME_LEN - 1); dcc->filename[GG_DCC7_FILENAME_LEN] = 0; memcpy(dcc->hash, p->hash, GG_DCC7_HASH_LEN); e->type = GG_EVENT_DCC7_NEW; e->event.dcc7_new = dcc; break; case GG_DCC7_TYPE_VOICE: if (!(dcc = malloc(sizeof(struct gg_dcc7)))) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_packet() not enough memory\n"); return -1; } memset(dcc, 0, sizeof(struct gg_dcc7)); dcc->type = GG_SESSION_DCC7_VOICE; dcc->dcc_type = GG_DCC7_TYPE_VOICE; dcc->fd = -1; dcc->file_fd = -1; dcc->uin = sess->uin; dcc->peer_uin = gg_fix32(p->uin_from); dcc->cid = p->id; dcc->sess = sess; if (gg_dcc7_session_add(sess, dcc) == -1) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n"); gg_dcc7_free(dcc); return -1; } e->type = GG_EVENT_DCC7_NEW; e->event.dcc7_new = dcc; break; default: gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unknown dcc type (%d) from %ld\n", gg_fix32(p->type), gg_fix32(p->uin_from)); break; } return 0; }
/* * \internal Analizuje przychodzący pakiet odpowiedzi i zapisuje wynik * w strukturze \c gg_event. * * \param sess Struktura sesji * \param e Struktura zdarzenia * \param packet Pakiet odpowiedzi * \param length Długość pakietu odpowiedzi * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length) { const char *end = packet + length, *p; struct gg_pubdir50_reply *r = (struct gg_pubdir50_reply*) packet; gg_pubdir50_t res; int num = 0; gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply_sess(%p, %p, %p, %d);\n", sess, e, packet, length); if (!sess || !e || !packet) { gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n"); errno = EFAULT; return -1; } if (length < 5) { gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() packet too short\n"); errno = EINVAL; return -1; } if (!(res = gg_pubdir50_new(r->type))) { gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() unable to allocate reply\n"); return -1; } e->event.pubdir50 = res; res->seq = gg_fix32(r->seq); switch (res->type) { case GG_PUBDIR50_READ: e->type = GG_EVENT_PUBDIR50_READ; break; case GG_PUBDIR50_WRITE: e->type = GG_EVENT_PUBDIR50_WRITE; break; default: e->type = GG_EVENT_PUBDIR50_SEARCH_REPLY; break; } /* brak wyników? */ if (length == 5) return 0; /* pomiń początek odpowiedzi */ p = packet + 5; while (p < end) { const char *field, *value; field = p; /* sprawdź, czy nie mamy podziału na kolejne pole */ if (!*field) { num++; field++; } value = NULL; for (p = field; p < end; p++) { /* jeśli mamy koniec tekstu... */ if (!*p) { /* ...i jeszcze nie mieliśmy wartości pola to * wiemy, że po tym zerze jest wartość... */ if (!value) value = p + 1; else /* ...w przeciwym wypadku koniec * wartości i możemy wychodzić * grzecznie z pętli */ break; } } /* sprawdźmy, czy pole nie wychodzi poza pakiet, żeby nie * mieć segfaultów, jeśli serwer przestanie zakańczać pakietów * przez \0 */ if (p == end) { gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() premature end of packet\n"); goto failure; } p++; /* jeśli dostaliśmy namier na następne wyniki, to znaczy że * mamy koniec wyników i nie jest to kolejna osoba. */ if (!strcasecmp(field, "nextstart")) { res->next = atoi(value); num--; } else { if (sess->encoding == GG_ENCODING_CP1250) { if (gg_pubdir50_add_n(res, num, field, value) == -1) goto failure; } else { char *tmp; tmp = gg_cp_to_utf8(value); if (tmp == NULL) goto failure; if (gg_pubdir50_add_n(res, num, field, tmp) == -1) { free(tmp); goto failure; } free(tmp); } } } res->count = num + 1; return 0; failure: gg_pubdir50_free(res); return -1; }
/** * \internal Obsługuje pakiet informacji o połączeniu bezpośrednim. * * \param sess Struktura sesji * \param e Struktura zdarzenia * \param payload Treść pakietu * \param len Długość pakietu * * \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) { 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 (dcc->state == GG_STATE_CONNECTED) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() state is already connected\n"); 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; e->event.dcc7_error_ex.dcc7 = dcc; 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; e->event.dcc7_error_ex.dcc7 = dcc; 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; e->event.dcc7_error_ex.dcc7 = dcc; return 0; } #if defined(GG_CONFIG_HAVE_UINT64_T) && defined(GG_CONFIG_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; e->event.dcc7_error_ex.dcc7 = dcc; return 0; } } #endif if (gg_dcc7_get_relay_addr(dcc) == -1) { gg_debug_session(sess, 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; e->event.dcc7_error_ex.dcc7 = dcc; 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; e->event.dcc7_error_ex.dcc7 = dcc; return 0; } // 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; // e->event.dcc7_error_ex.dcc7 = dcc; // return 0; // } if (dcc->state == GG_STATE_LISTENING) { gg_sock_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; e->event.dcc7_accept.type = gg_fix32(p->type); e->event.dcc7_accept.remote_ip = dcc->remote_addr; e->event.dcc7_accept.remote_port = dcc->remote_port; } else { e->type = GG_EVENT_DCC7_PENDING; e->event.dcc7_pending.dcc7 = dcc; } if (dcc->state == GG_STATE_RESOLVING_RELAY) return 0; 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; e->event.dcc7_error_ex.dcc7 = dcc; return 0; } } return 0; }