示例#1
0
文件: dcc7.c 项目: 0xmono/miranda-ng
/**
 * 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);
}
示例#2
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_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);
}