/** * 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); }
/** * 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_dcc(dcc, 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_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n"); errno = EINVAL; return NULL; } if (!(e = malloc(sizeof(struct gg_event)))) { gg_debug_dcc(dcc, 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; int fd, one = 1; socklen_t sin_len = sizeof(sin); gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n"); if ((fd = accept(dcc->fd, (struct sockaddr*) &sin, &sin_len)) == -1) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno)); return e; } gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port)); #ifdef FIONBIO if (ioctl(fd, FIONBIO, &one) == -1) { #else if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { #endif gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() can't set nonblocking (%s)\n", strerror(errno)); close(fd); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; return e; } 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_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n"); dcc->soft_timeout = 0; if (dcc->timeout == 0) error = ETIMEDOUT; if (error || (res = getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &error, &error_size)) == -1 || error != 0) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error)); if (gg_dcc7_reverse_connect(dcc) != -1) { e->type = GG_EVENT_DCC7_PENDING; } else { e->type = GG_EVENT_DCC7_ERROR; e->event.dcc_error = GG_ERROR_DCC7_NET; } return e; } gg_debug_dcc(dcc, 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: { gg_dcc7_id_t id; int res; gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n"); if ((res = read(dcc->fd, &id, sizeof(id))) != sizeof(id)) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; return e; } if (memcmp(&id, &dcc->cid, sizeof(id))) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n"); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; return e; } if (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_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n"); if ((res = write(dcc->fd, &dcc->cid, sizeof(dcc->cid))) != sizeof(dcc->cid)) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)", res, strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; return e; } if (dcc->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_dcc(dcc, 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_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n"); e->type = GG_EVENT_DCC7_DONE; return e; } if (dcc->seek && lseek(dcc->file_fd, dcc->offset, SEEK_SET) == (off_t) -1) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() lseek() failed (%s)\n", strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc_error = GG_ERROR_DCC7_FILE; return e; } if ((chunk = dcc->size - dcc->offset) > sizeof(buf)) chunk = sizeof(buf); if ((res = read(dcc->file_fd, buf, chunk)) < 1) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (res=%d, %s)\n", res, strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF; return e; } if ((res = write(dcc->fd, buf, res)) == -1) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%s)\n", strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc_error = GG_ERROR_DCC7_NET; return e; } dcc->offset += res; if (dcc->offset >= dcc->size) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n"); e->type = GG_EVENT_DCC7_DONE; 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_dcc(dcc, 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_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n"); e->type = GG_EVENT_DCC7_DONE; return e; } if ((res = read(dcc->fd, buf, sizeof(buf))) < 1) { gg_debug_dcc(dcc, 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.dcc_error = (res == -1) ? GG_ERROR_DCC7_NET : GG_ERROR_DCC7_EOF; return e; } // XXX zapisywać do skutku? if ((wres = write(dcc->file_fd, buf, res)) < res) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (fd=%d, res=%d, %s)\n", dcc->file_fd, wres, strerror(errno)); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc_error = GG_ERROR_DCC7_FILE; return e; } dcc->offset += res; if (dcc->offset >= dcc->size) { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n"); e->type = GG_EVENT_DCC7_DONE; return e; } dcc->state = GG_STATE_GETTING_FILE; dcc->check = GG_CHECK_READ; dcc->timeout = GG_DCC7_TIMEOUT_GET; return e; } default: { gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n"); e->type = GG_EVENT_DCC7_ERROR; e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; 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_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc); if (!dcc) return; if (dcc->fd != -1) close(dcc->fd); if (dcc->file_fd != -1) close(dcc->file_fd); if (dcc->sess) gg_dcc7_session_remove(dcc->sess, dcc); free(dcc); }