Commit message: Weryfikacja nazwy hosta w OpenSSL. Ignorujemy subjectAltname póki co.
CVE: CVE-2013-4488
CWE: CWE-310
Vulnerability description: libgadu before 1.12.0 does not verify X.509 certificates from SSL servers, which allows man-in-the-middle attackers to spoof servers.

Project: libgadu
File: events.c

static gg_action_t gg_handle_reading_proxy_gg(struct gg_session *sess, struct gg_event *e, enum gg_state_t next_state, enum gg_state_t alt_state, enum gg_state_t alt2_state)
{
	char buf[256];
	int res;
	int reply;
	char *body;

	res = recv(sess->fd, buf, sizeof(buf), 0);

	gg_debug_session(sess, GG_DEBUG_MISC, "recv() = %d\n", res);

	if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() non-critical recv error (errno=%d, %s)\n", errno, strerror(errno));
		return GG_ACTION_WAIT;
	}

	if (res == -1) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() recv error (errno=%d, %s)\n", errno, strerror(errno));
		e->event.failure = GG_FAILURE_CONNECTING;
		return GG_ACTION_FAIL;
	}

	if (res != 0) {
		char *tmp;

		tmp = realloc(sess->recv_buf, sess->recv_done + res + 1);

		if (tmp == NULL) {
			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for http reply\n");
			return GG_ACTION_FAIL;
		}

		sess->recv_buf = tmp;
		memcpy(sess->recv_buf + sess->recv_done, buf, res);
		sess->recv_done += res;
		sess->recv_buf[sess->recv_done] = 0;
	}

	if (res == 0 && sess->recv_buf == NULL) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection closed\n");
		e->event.failure = GG_FAILURE_CONNECTING;
		return GG_ACTION_FAIL;
	}

	/* szukamy początku treści */
	body = strstr(sess->recv_buf, "\r\n\r\n");
	
	if (body == NULL) {
		body = strstr(sess->recv_buf, "\n\n");

		if (body == NULL) {
			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't find body\n");
			e->event.failure = GG_FAILURE_CONNECTING;
			return GG_ACTION_FAIL;
		} else {
			body += 2;
		}
	} else {
		body += 4;
	}

	if (res != 0 && body == NULL)
		return GG_ACTION_WAIT;

	gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// received proxy reply:\n%s\n", sess->recv_buf);

	res = sscanf(sess->recv_buf, "HTTP/1.%*d %3d ", &reply);

	gg_debug_session(sess, GG_DEBUG_MISC, "res = %d, reply = %d\n", res, reply);

	/* sprawdzamy, czy wszystko w porządku. */
	if (res != 1 || reply != 200) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
		e->event.failure = GG_FAILURE_CONNECTING;
		return GG_ACTION_FAIL;
	}

	if (body == NULL) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't find body\n");
		e->event.failure = GG_FAILURE_CONNECTING;
		return GG_ACTION_FAIL;
	}

	gg_debug_session(sess, GG_DEBUG_MISC, "// found body!\n");

	if (sess->ssl_flag != GG_SSL_DISABLED) {
		if (gg_session_init_ssl(sess) == -1) {
			e->event.failure = GG_FAILURE_TLS;
			return GG_ACTION_FAIL;
		}

		/* Teoretycznie SSL jest inicjowany przez klienta, więc serwer
		 * nie powinien niczego wysłać. */
		if (sess->recv_buf + sess->recv_done > body) {
			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() unexpected SSL data\n");
			e->event.failure = GG_FAILURE_TLS;
			return GG_ACTION_FAIL;
		}

		free(sess->recv_buf);
		sess->recv_buf = NULL;
		sess->recv_done = 0;

		sess->state = alt_state;
		sess->check = GG_CHECK_WRITE;
		sess->timeout = GG_DEFAULT_TIMEOUT;

		return GG_ACTION_WAIT;
	}

	sess->state = next_state;
	sess->check = GG_CHECK_READ;
	sess->timeout = GG_DEFAULT_TIMEOUT;	/* Pierwszy pakiet musi przyjść */

	// Jeśli zbuforowaliśmy za dużo, przeanalizuj

	if (sess->recv_buf + sess->recv_done > body) {
		sess->recv_done = sess->recv_done - (body - sess->recv_buf);
		memmove(sess->recv_buf, body, sess->recv_done);
		sess->state = alt2_state;
		return GG_ACTION_NEXT;
	} else {
		free(sess->recv_buf);
		sess->recv_buf = NULL;
		sess->recv_done = 0;
	}

	return GG_ACTION_WAIT;
}

Project: libgadu
File: events.c

static gg_action_t gg_handle_resolving(struct gg_session *sess, struct gg_event *e, enum gg_state_t next_state, enum gg_state_t alt_state, enum gg_state_t alt2_state)
{
	char buf[256];
	int count = -1;
	int res;
	unsigned int i;

	res = gg_resolver_recv(sess->fd, buf, sizeof(buf));

	if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() non-critical error (errno=%d, %s)\n", errno, strerror(errno));
		return GG_ACTION_WAIT;
	}

	sess->resolver_cleanup(&sess->resolver, 0);

	if (res == -1) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() read error (errno=%d, %s)\n", errno, strerror(errno));
		e->event.failure = GG_FAILURE_RESOLVING;
		return GG_ACTION_FAIL;
	}

	if (res > 0) {
		char *tmp;

		tmp = realloc(sess->recv_buf, sess->recv_done + res);

		if (tmp == NULL)
			return GG_ACTION_FAIL;

		sess->recv_buf = tmp;
		memcpy(sess->recv_buf + sess->recv_done, buf, res);
		sess->recv_done += res;
	}

	/* Sprawdź, czy mamy listę zakończoną INADDR_NONE */

	for (i = 0; i < sess->recv_done / sizeof(struct in_addr); i++) {
		if (((struct in_addr*) sess->recv_buf)[i].s_addr == INADDR_NONE) {
			count = i;
			break;
		}
	}

	/* Nie znaleziono hosta */

	if (count == 0) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() host not found\n");
		e->event.failure = GG_FAILURE_RESOLVING;
		return GG_ACTION_FAIL;
	}

	/* Nie mamy pełnej listy, ale połączenie zerwane */

	if (res == 0 && count == -1) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection broken\n");
		e->event.failure = GG_FAILURE_RESOLVING;
		return GG_ACTION_FAIL;
	}

	/* Nie mamy pełnej listy, normalna sytuacja */

	if (count == -1)
		return GG_ACTION_WAIT;

#ifndef GG_DEBUG_DISABLE
	if ((gg_debug_level & GG_DEBUG_DUMP) && (count > 0)) {
		char *list;
		size_t len;

		len = 0;

		for (i = 0; i < (unsigned int) count; i++) {
			if (i > 0)
				len += 2;

			len += strlen(inet_ntoa(((struct in_addr*) sess->recv_buf)[i]));
		}

		list = malloc(len + 1);

		if (list == NULL)
			return GG_ACTION_FAIL;

		list[0] = 0;

		for (i = 0; i < (unsigned int) count; i++) {
			if (i > 0)
				strcat(list, ", ");

			strcat(list, inet_ntoa(((struct in_addr*) sess->recv_buf)[i]));
		}

		gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() resolved: %s\n", list);

		free(list);
	}
#endif

	close(sess->fd);
	sess->fd = -1;

	sess->state = next_state;
	sess->resolver_result = (struct in_addr*) sess->recv_buf;
	sess->resolver_count = count;
	sess->resolver_index = 0;
	sess->recv_buf = NULL;
	sess->recv_done = 0;
	
	return GG_ACTION_NEXT;
}

Project: libgadu
File: events.c

static gg_action_t gg_handle_resolve_sync(struct gg_session *sess, struct gg_event *e, enum gg_state_t next_state, enum gg_state_t alt_state, enum gg_state_t alt2_state)
{
	struct in_addr addr;

	addr.s_addr = inet_addr(sess->resolver_host);
	
	if (addr.s_addr == INADDR_NONE) {
		struct in_addr *addr_list = NULL;
		unsigned int addr_count;

		if (gg_gethostbyname_real(sess->resolver_host, &addr_list, &addr_count, 0) == -1) {
			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() host %s not found\n", sess->resolver_host);
			e->event.failure = GG_FAILURE_RESOLVING;
			free(addr_list);
			return GG_ACTION_FAIL;
		}

		sess->resolver_result = addr_list;
		sess->resolver_count = addr_count;
		sess->resolver_index = 0;
	} else {
		sess->resolver_result = malloc(sizeof(struct in_addr));

		if (sess->resolver_result == NULL) {
			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory\n");
			return GG_ACTION_FAIL;
		}

		sess->resolver_result[0].s_addr = addr.s_addr;
		sess->resolver_count = 1;
		sess->resolver_index = 0;
	}

	sess->state = next_state;

	return GG_ACTION_NEXT;
}

Project: libgadu
File: events.c

static gg_action_t gg_handle_resolve_async(struct gg_session *sess, struct gg_event *e, enum gg_state_t next_state, enum gg_state_t alt_state, enum gg_state_t alt2_state)
{
	if (sess->resolver_start(&sess->fd, &sess->resolver, sess->resolver_host) == -1) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
		e->event.failure = GG_FAILURE_RESOLVING;
		return GG_ACTION_FAIL;
	}

	sess->state = next_state;
	sess->check = GG_CHECK_READ;
	sess->timeout = GG_DEFAULT_TIMEOUT;

	return GG_ACTION_WAIT;
}

Project: libgadu
File: events.c

static gg_action_t gg_handle_sending_hub_proxy(struct gg_session *sess, struct gg_event *e, enum gg_state_t next_state, enum gg_state_t alt_state, enum gg_state_t alt2_state)
{
	if (gg_send_queued_data(sess) == -1) {
		e->event.failure = GG_FAILURE_WRITING;
		return GG_ACTION_FAIL;
	}

	if (sess->send_left > 0)
		return GG_ACTION_WAIT;

	sess->state = next_state;
	sess->check = GG_CHECK_READ;
	sess->timeout = GG_DEFAULT_TIMEOUT;

	return GG_ACTION_WAIT;
}

Project: libgadu
File: events.c

static int gg_send_queued_data(struct gg_session *sess)
{
	int res;

	if (sess->send_buf == NULL || sess->send_left == 0)
		return 0;
		
	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending %d bytes of queued data\n", sess->send_left);

	res = send(sess->fd, sess->send_buf, sess->send_left, 0);

	if (res == -1) {
		if (errno == EAGAIN || errno == EINTR) {
			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() non-critical send error (errno=%d, %s)\n", errno, strerror(errno));

			return 0;
		}

		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() send() failed (errno=%d, %s)\n", errno, strerror(errno));

		return -1;
	}

	if (res == sess->send_left) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent all queued data\n");
		free(sess->send_buf);
		sess->send_buf = NULL;
		sess->send_left = 0;
	} else if (res > 0) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent %d bytes of queued data, %d bytes left\n", res, sess->send_left - res);

		memmove(sess->send_buf, sess->send_buf + res, sess->send_left - res);
		sess->send_left -= res;
	}

	return 0;
}

Project: libgadu
File: events.c

static gg_action_t gg_handle_connecting_gg(struct gg_session *sess, struct gg_event *e, enum gg_state_t next_state, enum gg_state_t alt_state, enum gg_state_t alt2_state)
{
	int res;

	sess->soft_timeout = 0;

	/* jeśli wystąpił błąd podczas łączenia się... */
	if (gg_async_connect_failed(sess, &res)) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
		close(sess->fd);
		sess->fd = -1;
		sess->connect_index++;
		sess->state = alt_state;
		return GG_ACTION_NEXT;
	}

	free(sess->resolver_result);
	sess->resolver_result = NULL;

	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n");

	if (sess->ssl_flag != GG_SSL_DISABLED) {
		if (gg_session_init_ssl(sess) == -1) {
			e->event.failure = GG_FAILURE_TLS;
			return GG_ACTION_FAIL;
		}

		sess->state = alt2_state;
		sess->check = GG_CHECK_WRITE;
		sess->timeout = GG_DEFAULT_TIMEOUT;

		return GG_ACTION_NEXT;
	} else {
		sess->state = next_state;
		sess->check = GG_CHECK_READ;
		sess->timeout = GG_DEFAULT_TIMEOUT;

		return GG_ACTION_WAIT;
	}
}

Project: libgadu
File: events.c

int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq)
{
	if (!s || !q) {
		errno = EFAULT;
		return -1;
	}

	if (s->images == q)
		s->images = q->next;
	else {
		struct gg_image_queue *qq;

		for (qq = s->images; qq; qq = qq->next) {
			if (qq->next == q) {
				qq->next = q->next;
				break;
			}
		}
	}

	if (freeq) {
		free(q->image);
		free(q->filename);
		free(q);
	}

	return 0;
}

Project: libgadu
File: events.c

static gg_action_t gg_handle_reading_hub_proxy(struct gg_session *sess, struct gg_event *e, enum gg_state_t next_state, enum gg_state_t alt_state, enum gg_state_t alt2_state)
{
	char buf[1024], *tmp, host[128];
	int port = GG_DEFAULT_PORT;
	int reply;
	const char *body;
	struct in_addr addr;
	int res;

	res = recv(sess->fd, buf, sizeof(buf), 0);

	if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() non-critical recv error (errno=%d, %s)\n", errno, strerror(errno));
		return GG_ACTION_WAIT;
	}

	if (res == -1) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() recv error (errno=%d, %s)\n", errno, strerror(errno));
		e->event.failure = GG_FAILURE_CONNECTING;
		return GG_ACTION_FAIL;
	}

	if (res != 0) {
		tmp = realloc(sess->recv_buf, sess->recv_done + res + 1);

		if (tmp == NULL) {
			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for http reply\n");
			return GG_ACTION_FAIL;
		}

		sess->recv_buf = tmp;
		memcpy(sess->recv_buf + sess->recv_done, buf, res);
		sess->recv_done += res;
		sess->recv_buf[sess->recv_done] = 0;
	}

	if (res == 0 && sess->recv_buf == NULL) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection closed\n");
		e->event.failure = GG_FAILURE_CONNECTING;
		return GG_ACTION_FAIL;
	}

	if (res != 0)
		return GG_ACTION_WAIT;

	gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// received http reply:\n%s", sess->recv_buf);

	res = sscanf(sess->recv_buf, "HTTP/1.%*d %3d ", &reply);

	/* sprawdzamy, czy wszystko w porządku. */
	if (res != 1 || reply != 200) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
		e->event.failure = GG_FAILURE_CONNECTING;
		return GG_ACTION_FAIL;
	}

	/* szukamy początku treści */
	body = strstr(sess->recv_buf, "\r\n\r\n");
	
	if (body == NULL) {
		body = strstr(sess->recv_buf, "\n\n");

		if (body == NULL) {
			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't find body\n");
			e->event.failure = GG_FAILURE_CONNECTING;
			return GG_ACTION_FAIL;
		} else {
			body += 2;
		}
	} else {
		body += 4;
	}

	// 17591 0 91.197.13.71:8074 91.197.13.71
	res = sscanf(body, "%d %*d %128s", &reply, host);

	if (res != 2) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid hub reply, connection failed\n");
		e->event.failure = GG_FAILURE_CONNECTING;
		return GG_ACTION_FAIL;
	}

	gg_debug_session(sess, GG_DEBUG_MISC, "reply=%d, host=\"%s\"\n", reply, host);

	/* jeśli pierwsza liczba w linii nie jest równa zeru,
	 * oznacza to, że mamy wiadomość systemową. */
	if (reply != 0) {
		tmp = strchr(body, '\n');

		if (tmp != NULL) {
			e->type = GG_EVENT_MSG;
			e->event.msg.msgclass = reply;
			e->event.msg.sender = 0;
			e->event.msg.message = (unsigned char*) strdup(tmp + 1);

			if (e->event.msg.message == NULL) {
				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for system message\n");
				return GG_ACTION_FAIL;
			}
		}
	}

	close(sess->fd);
	sess->fd = -1;

	tmp = strchr(host, ':');

	if (tmp != NULL) {
		*tmp = 0;
		port = atoi(tmp + 1);
	}

	if (strcmp(host, "notoperating") == 0) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno));
		e->event.failure = GG_FAILURE_UNAVAILABLE;
		return GG_ACTION_FAIL;
	}

	addr.s_addr = inet_addr(host);
	sess->server_addr = addr.s_addr;

	free(sess->recv_buf);
	sess->recv_buf = NULL;
	sess->recv_done = 0;

	if (sess->state != GG_STATE_READING_PROXY_HUB) {
		if (sess->port == 0) {
			sess->connect_port[0] = port;
			sess->connect_port[1] = (port != GG_HTTPS_PORT) ? GG_HTTPS_PORT : 0;
		} else {
			sess->connect_port[0] = sess->port;
			sess->connect_port[1] = 0;
		}
	} else {
		sess->connect_port[0] = (sess->port == 0) ? GG_HTTPS_PORT : sess->port;
		sess->connect_port[1] = 0;
	}

	free(sess->connect_host);
	sess->connect_host = strdup(host);

	if (sess->connect_host == NULL) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory\n");
		return GG_ACTION_FAIL;
	}

	if (sess->state == GG_STATE_READING_HUB)
		sess->resolver_host = sess->connect_host;

	/* Jeśli łączymy się przez proxy, zacznijmy od początku listy */
	sess->resolver_index = 0;

	sess->state = (sess->async) ? next_state : alt_state;

	return GG_ACTION_NEXT;
}

Project: libgadu
File: events.c

static gg_action_t gg_handle_connecting(struct gg_session *sess, struct gg_event *e, enum gg_state_t next_state, enum gg_state_t alt_state, enum gg_state_t alt2_state)
{
	int res;

	sess->soft_timeout = 0;

	if (gg_async_connect_failed(sess, &res)) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
		close(sess->fd);
		sess->fd = -1;
		sess->resolver_index++;
		sess->state = alt_state;
	} else {
		/* Z proxy zwykle łączymy się dwa razy, więc nie zwalniamy
		 * adresów IP po pierwszym połączeniu. */
		if (sess->state != GG_STATE_CONNECTING_PROXY_HUB) {
			free(sess->resolver_result);
			sess->resolver_result = NULL;
		}

		sess->state = next_state;
	}

	return GG_ACTION_NEXT;
}

Project: libgadu
File: events.c

static gg_action_t gg_handle_send_hub(struct gg_session *sess, struct gg_event *e, enum gg_state_t next_state, enum gg_state_t alt_state, enum gg_state_t alt2_state)
{
	char *req, *client, *auth;
	const char *host;
	int res;
	int proxy;
	size_t req_len;

	if (sess->client_version != NULL && isdigit(sess->client_version[0]))
		client = gg_urlencode(sess->client_version);
	else
		client = gg_urlencode(GG_DEFAULT_CLIENT_VERSION);

	if (client == NULL) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
		return GG_ACTION_FAIL;
	}

	if (sess->proxy_addr && sess->proxy_port) {
		host = "http://" GG_APPMSG_HOST;
		proxy = 1;
	} else {
		host = "";
		proxy = 0;
	}

	auth = gg_proxy_auth();

	if (sess->ssl_flag != GG_SSL_DISABLED) {
		req = gg_saprintf
			("GET %s/appsvc/appmsg_ver10.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s&age=2&gender=1 HTTP/1.0\r\n"
			"Connection: close\r\n"
			"Host: " GG_APPMSG_HOST "\r\n"
			"%s"
			"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
	} else {
		req = gg_saprintf
			("GET %s/appsvc/appmsg_ver8.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s HTTP/1.0\r\n"
			"Host: " GG_APPMSG_HOST "\r\n"
			"%s"
			"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
	}

	if (req == NULL) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory\n");
		e->event.failure = GG_FAILURE_PROXY;
		return GG_ACTION_FAIL;
	}

	req_len = strlen(req);

	free(auth);
	free(client);

	gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// sending http query:\n%s", req);

	res = send(sess->fd, req, req_len, 0);

	free(req);
	
	if (res == -1 && errno != EINTR && errno != EAGAIN) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
		e->event.failure = (!proxy) ? GG_FAILURE_HUB : GG_FAILURE_PROXY;
		return GG_ACTION_FAIL;
	}

	if ((size_t) res < req_len) {
		sess->state = alt_state;
		sess->check = GG_CHECK_WRITE;
		sess->timeout = GG_DEFAULT_TIMEOUT;
	} else {
		sess->state = next_state;
		sess->check = GG_CHECK_READ;
		sess->timeout = GG_DEFAULT_TIMEOUT;
	}

	return GG_ACTION_WAIT;
}

Project: libgadu
File: events.c

static gg_action_t gg_handle_send_proxy_gg(struct gg_session *sess, struct gg_event *e, enum gg_state_t next_state, enum gg_state_t alt_state, enum gg_state_t alt2_state)
{
	char *req, *auth;
	size_t req_len;
	int res;

	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() %s\n", gg_debug_state(sess->state));

	if (sess->connect_index > 1 || sess->connect_port[sess->connect_index] == 0) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of connection candidates\n");
		e->event.failure = GG_FAILURE_CONNECTING;
		return GG_ACTION_FAIL;
	}

	auth = gg_proxy_auth();

	req = gg_saprintf("CONNECT %s:%d HTTP/1.0\r\n%s\r\n", sess->connect_host, sess->connect_port[sess->connect_index], (auth) ? auth : "");

	free(auth);

	sess->connect_index++;

	if (req == NULL) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory\n");
		e->event.failure = GG_FAILURE_PROXY;
		return GG_ACTION_FAIL;
	}

	req_len = strlen(req);

	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n%s", req);

	res = send(sess->fd, req, req_len, 0);

	free(req);

	if (res == -1 && errno != EINTR && errno != EAGAIN) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
		e->event.failure = GG_FAILURE_PROXY;
		return GG_ACTION_FAIL;
	}

	if ((size_t) res < req_len) {
		sess->state = alt_state;
		sess->check = GG_CHECK_WRITE;
		sess->timeout = GG_DEFAULT_TIMEOUT;
	} else {
		sess->state = next_state;
		sess->check = GG_CHECK_READ;
		sess->timeout = GG_DEFAULT_TIMEOUT;
	}

	return GG_ACTION_WAIT;
}

Project: libgadu
File: events.c

struct gg_event *gg_watch_fd(struct gg_session *sess)
{
	struct gg_event *ge;

	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);

	if (sess == NULL) {
		errno = EFAULT;
		return NULL;
	}

	ge = malloc(sizeof(struct gg_event));

	if (ge == NULL) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n");
		return NULL;
	}

	memset(ge, 0, sizeof(struct gg_event));

	ge->type = GG_EVENT_NONE;

	for (;;) {
		unsigned int i;
		gg_action_t res;

		res = GG_ACTION_FAIL;

		for (i = 0; i < sizeof(handlers) / sizeof(handlers[0]); i++) {
			if (handlers[i].state == (enum gg_state_t) sess->state) {
				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() %s\n", gg_debug_state(sess->state));
				res = (*handlers[i].handler)(sess, ge, handlers[i].next_state, handlers[i].alt_state, handlers[i].alt2_state);
				break;
			}
		}

		if (!sess->async && ge->type == GG_EVENT_NONE && res == GG_ACTION_WAIT)
			res = GG_ACTION_NEXT;

		switch (res) {
			case GG_ACTION_WAIT:
				return ge;

			case GG_ACTION_NEXT:
				continue;

			case GG_ACTION_FAIL:
				sess->state = GG_STATE_IDLE;

				if (sess->fd != -1) {
					int errno2;

					errno2 = errno;
					close(sess->fd);
					errno = errno2;
					sess->fd = -1;
				}

				if (ge->event.failure != 0) {
					ge->type = GG_EVENT_CONN_FAILED;
				} else {
					free(ge);
					ge = NULL;
				}

				return ge;

			/* Celowo nie ma default */
		}
	}
}

Project: libgadu
File: events.c

static gg_action_t gg_handle_connected(struct gg_session *sess, struct gg_event *e, enum gg_state_t next_state, enum gg_state_t alt_state, enum gg_state_t alt2_state)
{
#if 0
	char buf[1024];
	int res;

	if (gg_send_queued_data(sess) == -1)
		return GG_ACTION_FAIL;

	res = gg_read(sess, buf, sizeof(buf));

	if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() non-critical read error (errno=%d, %s)\n", errno, strerror(errno));
		return GG_ACTION_WAIT;
	}
	
	if (res == -1 || res == 0) {
		if (res == -1)
			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() read error (errno=%d, %s)\n", errno, strerror(errno));
		else
			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection closed\n");

		if (sess->state == GG_STATE_DISCONNECTING && res == 0) {
			e->type = GG_EVENT_DISCONNECT_ACK;
		} else if (sess->state == GG_STATE_READING_KEY) {
			e->event.failure = GG_FAILURE_INVALID;
			return GG_ACTION_FAIL;
		}

		return GG_ACTION_FAIL;
	}

	gg_debug_dump(sess, GG_DEBUG_DUMP, buf, res);

	if (gg_session_handle_data(sess, buf, res, e) == -1)
		return GG_ACTION_FAIL;

	if (sess->send_buf != NULL)
		sess->check |= GG_CHECK_WRITE;

	return GG_ACTION_WAIT;
#else
	struct gg_header *gh;

	if (gg_send_queued_data(sess) == -1)
		return GG_ACTION_FAIL;

	gh = gg_recv_packet(sess);

	if (gh == NULL) {
		if (sess->state == GG_STATE_DISCONNECTING) {
			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection broken expectedly\n");
			e->type = GG_EVENT_DISCONNECT_ACK;
			return GG_ACTION_WAIT;
		}

		if (errno != EAGAIN) {
			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno));
			return GG_ACTION_FAIL;
		}
	} else {
		if (gg_session_handle_packet(sess, gh->type, (const char *) gh + sizeof(struct gg_header), gh->length, e) == -1) {
			free(gh);
			return GG_ACTION_FAIL;
		}

		free(gh);
	}

	sess->check = GG_CHECK_READ;

	if (sess->send_buf != NULL)
		sess->check |= GG_CHECK_WRITE;

	return GG_ACTION_WAIT;
#endif
}

Project: libgadu
File: events.c

static gg_action_t gg_handle_connect_gg(struct gg_session *sess, struct gg_event *e, enum gg_state_t next_state, enum gg_state_t alt_state, enum gg_state_t alt2_state)
{
	struct in_addr addr;
	uint16_t port;

	gg_debug_session(sess, GG_DEBUG_MISC, "resolver_index=%d, connect_index=%d, connect_port={%d,%d}\n", sess->resolver_index, sess->connect_index, sess->connect_port[0], sess->connect_port[1]);

	if ((unsigned int) sess->connect_index >= sizeof(sess->connect_port) / sizeof(sess->connect_port[0]) || sess->connect_port[sess->connect_index] == 0) {
		sess->connect_index = 0;
		sess->resolver_index++;
		if (sess->resolver_index >= sess->resolver_count) {
			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of addresses to connect to\n");
			e->event.failure = GG_FAILURE_CONNECTING;
			return GG_ACTION_FAIL;
		}
	}

	addr = sess->resolver_result[sess->resolver_index];
	port = sess->connect_port[sess->connect_index];

	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connecting to %s:%d\n", inet_ntoa(addr), port);

	sess->fd = gg_connect(&addr, port, sess->async);

	if (sess->fd == -1) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
		sess->connect_index++;
		return GG_ACTION_NEXT;
	}

	sess->state = next_state;
	sess->check = GG_CHECK_WRITE;
	sess->timeout = GG_DEFAULT_TIMEOUT;
	sess->soft_timeout = 1;

	return GG_ACTION_WAIT;
}

Project: libgadu
File: events.c

static gg_action_t gg_handle_connect(struct gg_session *sess, struct gg_event *e, enum gg_state_t next_state, enum gg_state_t alt_state, enum gg_state_t alt2_state)
{
	struct in_addr addr;
	int port;

	if (sess->resolver_index >= sess->resolver_count) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of addresses to connect to\n");
		e->event.failure = GG_FAILURE_CONNECTING;
		return GG_ACTION_FAIL;
	}

	addr = sess->resolver_result[sess->resolver_index];

	if (sess->state == GG_STATE_CONNECT_HUB) {
		port = GG_APPMSG_PORT;
	} else {
		sess->proxy_addr = addr.s_addr;
		port = sess->proxy_port;
	}

	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connecting to %s:%d\n", inet_ntoa(addr), port);

	sess->fd = gg_connect(&addr, port, sess->async);

	if (sess->fd == -1) {
		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
		sess->resolver_index++;
		return GG_ACTION_NEXT;
	}

	sess->state = next_state;
	sess->check = GG_CHECK_WRITE;
	sess->timeout = GG_DEFAULT_TIMEOUT;
	sess->soft_timeout = 1;

	return GG_ACTION_WAIT;
}

Project: libgadu
File: events.c

static int gg_async_connect_failed(struct gg_session *gs, int *res_ptr)
{
	int res = 0;
	socklen_t res_size = sizeof(res);

	if (!gs->async)
		return 0;

	if (gs->timeout == 0) {
		*res_ptr = ETIMEDOUT;
		return 1;
	}

	if (getsockopt(gs->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) == -1) {
		*res_ptr = errno;
		return 1;
	}

	if (res != 0) {
		*res_ptr = res;
		return 1;
	}

	*res_ptr = 0;

	return 0;
}

Project: libgadu
File: events.c

static int gg_session_init_ssl(struct gg_session *gs)
{
#ifdef GG_CONFIG_HAVE_GNUTLS
	gg_session_gnutls_t *tmp;

	tmp = (gg_session_gnutls_t*) gs->ssl;

	if (tmp == NULL) {
		tmp = malloc(sizeof(gg_session_gnutls_t));

		if (tmp == NULL) {
			gg_debug(GG_DEBUG_MISC, "// gg_session_connect() out of memory for GnuTLS session\n");
			return -1;
		}

		memset(tmp, 0, sizeof(gg_session_gnutls_t));

		gs->ssl = tmp;

		gnutls_global_init();
		gnutls_certificate_allocate_credentials(&tmp->xcred);
#ifdef GG_CONFIG_SSL_SYSTEM_TRUST
#ifdef HAVE_GNUTLS_CERTIFICATE_SET_X509_SYSTEM_TRUST
		gnutls_certificate_set_x509_system_trust(tmp->xcred);
#else
		gnutls_certificate_set_x509_trust_file(tmp->xcred, GG_CONFIG_GNUTLS_SYSTEM_TRUST_STORE, GNUTLS_X509_FMT_PEM);
#endif
#endif
	} else {
		gnutls_deinit(tmp->session);
	}

	gnutls_init(&tmp->session, GNUTLS_CLIENT);
	gnutls_set_default_priority(tmp->session);
	gnutls_credentials_set(tmp->session, GNUTLS_CRD_CERTIFICATE, tmp->xcred);
	gnutls_transport_set_ptr(tmp->session, (gnutls_transport_ptr_t) (long) gs->fd);
#endif

#ifdef GG_CONFIG_HAVE_OPENSSL
	char buf[1024];

	OpenSSL_add_ssl_algorithms();

	if (!RAND_status()) {
		char rdata[1024];
		struct {
			time_t time;
			void *ptr;
		} rstruct;

		time(&rstruct.time);
		rstruct.ptr = (void *) &rstruct;

		RAND_seed((void *) rdata, sizeof(rdata));
		RAND_seed((void *) &rstruct, sizeof(rstruct));
	}

	if (gs->ssl_ctx == NULL) {
		gs->ssl_ctx = SSL_CTX_new(SSLv3_client_method());

		if (gs->ssl_ctx == NULL) {
			ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
			gg_debug(GG_DEBUG_MISC, "// gg_session_connect() SSL_CTX_new() failed: %s\n", buf);
			return -1;
		}

		SSL_CTX_set_verify(gs->ssl_ctx, SSL_VERIFY_NONE, NULL);
#ifdef GG_CONFIG_SSL_SYSTEM_TRUST
		SSL_CTX_set_default_verify_paths(gs->ssl_ctx);
#endif
	}

	if (gs->ssl != NULL)
		SSL_free(gs->ssl);

	gs->ssl = SSL_new(gs->ssl_ctx);

	if (gs->ssl == NULL) {
		ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
		gg_debug(GG_DEBUG_MISC, "// gg_session_connect() SSL_new() failed: %s\n", buf);
		return -1;
	}

	SSL_set_fd(gs->ssl, gs->fd);
#endif

	return 0;
}

Project: libgadu
File: events.c

void gg_event_free(struct gg_event *e)
{
	gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e);

	if (!e)
		return;

	switch (e->type) {
		case GG_EVENT_MSG:
		case GG_EVENT_MULTILOGON_MSG:
			free(e->event.msg.message);
			free(e->event.msg.formats);
			free(e->event.msg.recipients);
			free(e->event.msg.xhtml_message);
			break;

		case GG_EVENT_NOTIFY:
			free(e->event.notify);
			break;

		case GG_EVENT_NOTIFY60:
		{
			int i;

			for (i = 0; e->event.notify60[i].uin; i++)
				free(e->event.notify60[i].descr);

			free(e->event.notify60);

			break;
		}

		case GG_EVENT_STATUS60:
			free(e->event.status60.descr);
			break;

		case GG_EVENT_STATUS:
			free(e->event.status.descr);
			break;

		case GG_EVENT_NOTIFY_DESCR:
			free(e->event.notify_descr.notify);
			free(e->event.notify_descr.descr);
			break;

		case GG_EVENT_DCC_VOICE_DATA:
			free(e->event.dcc_voice_data.data);
			break;

		case GG_EVENT_PUBDIR50_SEARCH_REPLY:
		case GG_EVENT_PUBDIR50_READ:
		case GG_EVENT_PUBDIR50_WRITE:
			gg_pubdir50_free(e->event.pubdir50);
			break;

		case GG_EVENT_USERLIST:
			free(e->event.userlist.reply);
			break;

		case GG_EVENT_IMAGE_REPLY:
			free(e->event.image_reply.filename);
			free(e->event.image_reply.image);
			break;

		case GG_EVENT_XML_EVENT:
			free(e->event.xml_event.data);
			break;

		case GG_EVENT_JSON_EVENT:
			free(e->event.json_event.data);
			free(e->event.json_event.type);
			break;

		case GG_EVENT_USER_DATA:
		{
			unsigned int i, j;

			for (i = 0; i < e->event.user_data.user_count; i++) {
				for (j = 0; j < e->event.user_data.users[i].attr_count; j++) {
					free(e->event.user_data.users[i].attrs[j].key);
					free(e->event.user_data.users[i].attrs[j].value);
				}

				free(e->event.user_data.users[i].attrs);
			}

			free(e->event.user_data.users);

			break;
		}
	
		case GG_EVENT_MULTILOGON_INFO:
		{
			int i;

			for (i = 0; i < e->event.multilogon_info.count; i++)
				free(e->event.multilogon_info.sessions[i].name);

			free(e->event.multilogon_info.sessions);

			break;
		}

		case GG_EVENT_USERLIST100_REPLY:
			free(e->event.userlist100_reply.reply);
			break;

		case GG_EVENT_IMTOKEN:
			free(e->event.imtoken.imtoken);
			break;

		case GG_EVENT_CHAT_INFO:
			free(e->event.chat_info.participants);
			break;
	}

	free(e);
}