Example #1
0
File: peer.c Project: dylex/pidgin
/**
 * This is the watcher callback for any listening socket that is
 * waiting for a peer to connect.  When a peer connects we set the
 * input watcher to start reading data from the peer.
 *
 * To make sure that the connection is with the intended person and
 * not with a malicious middle man, we don't send anything until we've
 * received a peer frame from the remote user and have verified that
 * the cookie in the peer frame matches the cookie that was exchanged
 * in the channel 2 ICBM.
 */
void
peer_connection_listen_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	PeerConnection *conn;
	struct sockaddr addr;
	socklen_t addrlen = sizeof(addr);

	conn = data;

	purple_debug_info("oscar", "Accepting connection on listener socket.\n");

	conn->fd = accept(conn->listenerfd, &addr, &addrlen);
	if (conn->fd < 0)
	{
		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
			/* No connection yet--no worries */
			/* TODO: Hmm, but they SHOULD be connected if we're here, right? */
			return;

		peer_connection_trynext(conn);
		return;
	}

	if ((addr.sa_family != PF_INET) && (addr.sa_family != PF_INET6))
	{
		/* Invalid connection type?!  Continue waiting. */
		close(conn->fd);
		return;
	}

	_purple_network_set_common_socket_flags(conn->fd);

	purple_input_remove(conn->watcher_incoming);

	peer_connection_finalize_connection(conn);
}
Example #2
0
/**
 * Someone else wants to establish a peer connection with us.
 */
void
peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar *message, IcbmArgsCh2 *args)
{
	PurpleConnection *gc;
	PurpleAccount *account;
	PeerConnection *conn;
	gchar *buf;

	gc = od->gc;
	account = purple_connection_get_account(gc);

	/*
	 * If we have a connection with this same cookie then they are
	 * probably just telling us they weren't able to connect to us
	 * and we should try connecting to them, instead.  Or they want
	 * to go through a proxy.
	 */
	conn = peer_connection_find_by_cookie(od, bn, args->cookie);
	if ((conn != NULL) && (conn->type == args->type))
	{
		purple_debug_info("oscar", "Remote user wants to try a "
				"different connection method\n");
		g_free(conn->proxyip);
		g_free(conn->clientip);
		g_free(conn->verifiedip);
		if (args->use_proxy)
			conn->proxyip = g_strdup(args->proxyip);
		else
			conn->proxyip = NULL;
		conn->verifiedip = g_strdup(args->verifiedip);
		conn->clientip = g_strdup(args->clientip);
		conn->port = args->port;
		conn->use_proxy |= args->use_proxy;
		conn->lastrequestnumber++;
		peer_connection_trynext(conn);
		return;
	}

	/* If this is a direct IM, then close any existing session */
	if (args->type == OSCAR_CAPABILITY_DIRECTIM)
	{
		conn = peer_connection_find_by_type(od, bn, args->type);
		if (conn != NULL)
		{
			/* Close the old direct IM and start a new one */
			purple_debug_info("oscar", "Received new direct IM request "
				"from %s.  Destroying old connection.\n", bn);
			peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
		}
	}

	/* Check for proper arguments */
	if (args->type == OSCAR_CAPABILITY_SENDFILE)
	{
		if ((args->info.sendfile.filename == NULL) ||
			(args->info.sendfile.totsize == 0) ||
			(args->info.sendfile.totfiles == 0))
		{
			purple_debug_warning("oscar",
					"%s tried to send you a file with incomplete "
					"information.\n", bn);
			return;
		}
	}

	conn = peer_connection_new(od, args->type, bn);
	memcpy(conn->cookie, args->cookie, 8);
	if (args->use_proxy)
		conn->proxyip = g_strdup(args->proxyip);
	conn->clientip = g_strdup(args->clientip);
	conn->verifiedip = g_strdup(args->verifiedip);
	conn->port = args->port;
	conn->use_proxy |= args->use_proxy;
	conn->lastrequestnumber++;

	if (args->type == OSCAR_CAPABILITY_DIRECTIM)
	{
		buf = g_strdup_printf(_("%s has just asked to directly connect to %s"),
				bn, purple_account_get_username(account));

		purple_request_action(conn, NULL, buf,
						_("This requires a direct connection between "
						  "the two computers and is necessary for IM "
						  "Images.  Because your IP address will be "
						  "revealed, this may be considered a privacy "
						  "risk."),
						PURPLE_DEFAULT_ACTION_NONE,
						account, bn, NULL,
						conn, 2,
						_("C_onnect"), G_CALLBACK(peer_connection_got_proposition_yes_cb),
						_("Cancel"), G_CALLBACK(peer_connection_got_proposition_no_cb));
	}
	else if (args->type == OSCAR_CAPABILITY_SENDFILE)
	{
		gchar *filename;

		conn->xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, bn);
		if (conn->xfer)
		{
			conn->xfer->data = conn;
			purple_xfer_ref(conn->xfer);
			purple_xfer_set_size(conn->xfer, args->info.sendfile.totsize);

			/* Set the file name */
			if (g_utf8_validate(args->info.sendfile.filename, -1, NULL))
				filename = g_strdup(args->info.sendfile.filename);
			else
				filename = purple_utf8_salvage(args->info.sendfile.filename);

			if (args->info.sendfile.subtype == AIM_OFT_SUBTYPE_SEND_DIR)
			{
				/*
				 * If they are sending us a directory then the last character
				 * of the file name will be an asterisk.  We don't want to
				 * save stuff to a directory named "*" so we remove the
				 * asterisk from the file name.
				 */
				char *tmp = strrchr(filename, '\\');
				if ((tmp != NULL) && (tmp[1] == '*'))
					tmp[0] = '\0';
			}
			purple_xfer_set_filename(conn->xfer, filename);
			g_free(filename);

			/*
			 * Set the message, unless this is the dummy message from an
			 * ICQ client or an empty message from an AIM client.
			 * TODO: Maybe we should strip HTML and then see if strlen>0?
			 */
			if ((message != NULL) &&
				(g_ascii_strncasecmp(message, "<ICQ_COOL_FT>", 13) != 0) &&
				(g_ascii_strcasecmp(message, "<HTML>") != 0))
			{
				purple_xfer_set_message(conn->xfer, message);
			}

			/* Setup our I/O op functions */
			purple_xfer_set_init_fnc(conn->xfer, peer_oft_recvcb_init);
			purple_xfer_set_end_fnc(conn->xfer, peer_oft_recvcb_end);
			purple_xfer_set_request_denied_fnc(conn->xfer, peer_oft_cb_generic_cancel);
			purple_xfer_set_cancel_recv_fnc(conn->xfer, peer_oft_cb_generic_cancel);
			purple_xfer_set_ack_fnc(conn->xfer, peer_oft_recvcb_ack_recv);

			/* Now perform the request */
			purple_xfer_request(conn->xfer);
		}
	}
}
Example #3
0
/**
 * We've just opened a listener socket, so we send the remote
 * user an ICBM and ask them to connect to us.
 */
static void
peer_connection_establish_listener_cb(int listenerfd, gpointer data)
{
	PeerConnection *conn;
	OscarData *od;
	PurpleConnection *gc;
	PurpleAccount *account;
	PurpleConversation *conv;
	char *tmp;
	FlapConnection *bos_conn;
	const char *listener_ip;
	const guchar *ip_atoi;
	unsigned short listener_port;

	conn = data;
	conn->listen_data = NULL;

	if (listenerfd < 0)
	{
		/* Could not open listener socket */
		peer_connection_trynext(conn);
		return;
	}

	od = conn->od;
	gc = od->gc;
	account = purple_connection_get_account(gc);
	conn->listenerfd = listenerfd;

	/* Watch for new connections on our listener socket */
	conn->watcher_incoming = purple_input_add(conn->listenerfd,
			PURPLE_INPUT_READ, peer_connection_listen_cb, conn);

	/* Send the "please connect to me!" ICBM */
	bos_conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
	if (bos_conn == NULL)
	{
		/* Not good */
		peer_connection_trynext(conn);
		return;
	}

	if (bos_conn->gsc)
		listener_ip = purple_network_get_my_ip(bos_conn->gsc->fd);
	else
		listener_ip = purple_network_get_my_ip(bos_conn->fd);

	ip_atoi = purple_network_ip_atoi(listener_ip);
	if (ip_atoi == NULL) {
		/* Could not convert IP to 4 byte array--weird, but this does
		   happen for some users (#4829, Adium #15839).  Maybe they're
		   connecting with IPv6...?  Maybe through a proxy? */
		purple_debug_error("oscar", "Can't ask peer to connect to us "
				"because purple_network_ip_atoi(%s) returned NULL. "
				"fd=%d. is_ssl=%d\n",
				listener_ip ? listener_ip : "(null)",
				bos_conn->gsc ? bos_conn->gsc->fd : bos_conn->fd,
				bos_conn->gsc ? 1 : 0);
		peer_connection_trynext(conn);
		return;
	}

	listener_port = purple_network_get_port_from_fd(conn->listenerfd);

	if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
	{
		aim_im_sendch2_odc_requestdirect(od,
				conn->cookie, conn->bn, ip_atoi,
				listener_port, ++conn->lastrequestnumber);

		/* Print a message to a local conversation window */
		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
		tmp = g_strdup_printf(_("Asking %s to connect to us at %s:%hu for "
				"Direct IM."), conn->bn, listener_ip, listener_port);
		purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
		g_free(tmp);
	}
	else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
	{
		aim_im_sendch2_sendfile_requestdirect(od,
				conn->cookie, conn->bn,
				ip_atoi,
				listener_port, ++conn->lastrequestnumber,
				(const gchar *)conn->xferdata.name,
				conn->xferdata.size, conn->xferdata.totfiles);
	}
}
Example #4
0
static void
send_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	PeerConnection *conn;
	gsize writelen;
	gssize wrotelen;

	conn = data;
	writelen = purple_circ_buffer_get_max_read(conn->buffer_outgoing);

	if (writelen == 0)
	{
		purple_input_remove(conn->watcher_outgoing);
		conn->watcher_outgoing = 0;
		/*
		 * The buffer is currently empty, so reset the current input
		 * and output positions to the start of the buffer.  We do
		 * this so that the next chunk of data that we put into the
		 * buffer can be read back out of the buffer in one fell swoop.
		 * Otherwise it gets fragmented and we have to read from the
		 * second half of the buffer than go back and read the rest of
		 * the chunk from the first half.
		 *
		 * We're using TCP, which is a stream based protocol, so this
		 * isn't supposed to matter.  However, experience has shown
		 * that at least the proxy file transfer code in AIM 6.1.41.2
		 * requires that the entire OFT frame arrive all at once.  If
		 * the frame is fragmented then AIM freaks out and aborts the
		 * file transfer.  Somebody should teach those guys how to
		 * write good TCP code.
		 */
		conn->buffer_outgoing->inptr = conn->buffer_outgoing->buffer;
		conn->buffer_outgoing->outptr = conn->buffer_outgoing->buffer;
		return;
	}

	wrotelen = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
	if (wrotelen <= 0)
	{
		if (wrotelen < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
			/* No worries */
			return;

		if (conn->ready)
		{
			purple_input_remove(conn->watcher_outgoing);
			conn->watcher_outgoing = 0;
			close(conn->fd);
			conn->fd = -1;
			peer_connection_schedule_destroy(conn,
					OSCAR_DISCONNECT_LOST_CONNECTION, NULL);
		}
		else
		{
			/*
			 * This could happen when unable to send a negotiation
			 * frame to a peer proxy server.
			 */
			peer_connection_trynext(conn);
		}
		return;
	}

	purple_circ_buffer_mark_read(conn->buffer_outgoing, wrotelen);
	conn->lastactivity = time(NULL);
}
Example #5
0
static void
peer_proxy_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	PeerConnection *conn;
	gssize read;
	ProxyFrame *frame;

	conn = data;
	frame = conn->frame;

	/* Start reading a new proxy frame */
	if (frame == NULL)
	{
		/* Read the first 12 bytes (frame length and header) */
		read = recv(conn->fd, conn->proxy_header + conn->proxy_header_received,
				12 - conn->proxy_header_received, 0);

		/* Check if the proxy server closed the connection */
		if (read == 0)
		{
			purple_debug_info("oscar", "Peer proxy server closed connection\n");
			peer_connection_trynext(conn);
			return;
		}

		/* If there was an error then close the connection */
		if (read < 0)
		{
			if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
				/* No worries */
				return;

			purple_debug_info("oscar", "Lost connection with peer proxy server\n");
			peer_connection_trynext(conn);
			return;
		}

		conn->lastactivity = time(NULL);

		/* If we don't even have the first 12 bytes then do nothing */
		conn->proxy_header_received += read;
		if (conn->proxy_header_received < 12)
			return;

		/* We only support a specific version of the proxy protocol */
		if (aimutil_get16(&conn->proxy_header[2]) != PEER_PROXY_PACKET_VERSION)
		{
			purple_debug_warning("oscar", "Expected peer proxy protocol "
				"version %u but received version %u.  Closing "
				"connection.\n", PEER_PROXY_PACKET_VERSION,
				aimutil_get16(&conn->proxy_header[2]));
			peer_connection_trynext(conn);
			return;
		}

		/* Initialize a new temporary ProxyFrame for incoming data */
		frame = g_new0(ProxyFrame, 1);
		frame->payload.len = aimutil_get16(&conn->proxy_header[0]) - 10;
		frame->version = aimutil_get16(&conn->proxy_header[2]);
		frame->type = aimutil_get16(&conn->proxy_header[4]);
		frame->unknown = aimutil_get16(&conn->proxy_header[6]);
		frame->flags = aimutil_get16(&conn->proxy_header[10]);
		if (frame->payload.len > 0)
			frame->payload.data = g_new(guint8, frame->payload.len);
		conn->frame = frame;
	}

	/* If this frame has a payload then attempt to read it */
	if (frame->payload.len - frame->payload.offset > 0)
	{
		/* Read data into the temporary buffer until it is complete */
		read = recv(conn->fd,
					&frame->payload.data[frame->payload.offset],
					frame->payload.len - frame->payload.offset,
					0);

		/* Check if the proxy server closed the connection */
		if (read == 0)
		{
			purple_debug_info("oscar", "Peer proxy server closed connection\n");
			g_free(frame->payload.data);
			g_free(frame);
			conn->frame = NULL;
			peer_connection_trynext(conn);
			return;
		}

		/* If there was an error then close the connection */
		if (read < 0)
		{
			if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
				/* No worries */
				return;

			purple_debug_info("oscar", "Lost connection with peer proxy server\n");
			g_free(frame->payload.data);
			g_free(frame);
			conn->frame = NULL;
			peer_connection_trynext(conn);
			return;
		}

		frame->payload.offset += read;
	}

	conn->lastactivity = time(NULL);
	if (frame->payload.offset < frame->payload.len)
		/* Waiting for more data to arrive */
		return;

	/* We have a complete proxy frame!  Handle it and continue reading */
	conn->frame = NULL;
	byte_stream_rewind(&frame->payload);
	peer_proxy_recv_frame(conn, frame);

	g_free(frame->payload.data);
	g_free(frame);

	conn->proxy_header_received = 0;
}
Example #6
0
/**
 * Handle an incoming peer proxy negotiation frame.
 */
static void
peer_proxy_recv_frame(PeerConnection *conn, ProxyFrame *frame)
{
	purple_debug_info("oscar", "Incoming peer proxy frame with "
			"type=0x%04hx, unknown=0x%08x, "
			"flags=0x%04hx, and payload length=%hd\n", frame->type,
			frame->unknown, frame->flags, frame->payload.len);

	if (frame->type == PEER_PROXY_TYPE_CREATED)
	{
		/*
		 * Read in 2 byte port then 4 byte IP and tell the
		 * remote user to connect to it by sending an ICBM.
		 */
		guint16 pin;
		int i;
		guint8 ip[4];

		pin = byte_stream_get16(&frame->payload);
		for (i = 0; i < 4; i++)
			ip[i] = byte_stream_get8(&frame->payload);
		if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
			aim_im_sendch2_odc_requestproxy(conn->od,
					conn->cookie,
					conn->bn, ip, pin, ++conn->lastrequestnumber);
		else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
		{
			aim_im_sendch2_sendfile_requestproxy(conn->od,
					conn->cookie, conn->bn,
					ip, pin, ++conn->lastrequestnumber,
					(const gchar *)conn->xferdata.name,
					conn->xferdata.size, conn->xferdata.totfiles);
		}
	}
	else if (frame->type == PEER_PROXY_TYPE_READY)
	{
		purple_input_remove(conn->watcher_incoming);
		conn->watcher_incoming = 0;

		peer_connection_finalize_connection(conn);
	}
	else if (frame->type == PEER_PROXY_TYPE_ERROR)
	{
		if (byte_stream_empty(&frame->payload) >= 2)
		{
			guint16 error;
			const char *msg;
			error = byte_stream_get16(&frame->payload);
			if (error == 0x000d)
				msg = "bad request";
			else if (error == 0x0010)
				msg = "initial request timed out";
			else if (error == 0x001a)
				msg ="accept period timed out";
			else
				msg = "unknown reason";
			purple_debug_info("oscar", "Proxy negotiation failed with "
					"error 0x%04hx: %s\n", error, msg);
		}
		else
		{
			purple_debug_warning("oscar", "Proxy negotiation failed with "
					"an unknown error\n");
		}
		peer_connection_trynext(conn);
	}
	else
	{
		purple_debug_warning("oscar", "Unknown peer proxy frame type 0x%04hx.\n",
				frame->type);
	}
}
Example #7
0
/**
 * We've just opened a listener socket, so we send the remote
 * user an ICBM and ask them to connect to us.
 */
static void
peer_connection_establish_listener_cb(int listenerfd, gpointer data)
{
	PeerConnection *conn;
	OscarData *od;
	PurpleConnection *gc;
	PurpleAccount *account;
	PurpleConversation *conv;
	char *tmp;
	FlapConnection *bos_conn;
	const char *listener_ip;
	unsigned short listener_port;

	conn = data;
	conn->listen_data = NULL;

	if (listenerfd < 0)
	{
		/* Could not open listener socket */
		peer_connection_trynext(conn);
		return;
	}

	od = conn->od;
	gc = od->gc;
	account = purple_connection_get_account(gc);
	conn->listenerfd = listenerfd;

	/* Watch for new connections on our listener socket */
	conn->watcher_incoming = purple_input_add(conn->listenerfd,
			PURPLE_INPUT_READ, peer_connection_listen_cb, conn);

	/* Send the "please connect to me!" ICBM */
	bos_conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
	if (bos_conn == NULL)
	{
		/* Not good */
		peer_connection_trynext(conn);
		return;
	}

	if (bos_conn->gsc)
		listener_ip = purple_network_get_my_ip(bos_conn->gsc->fd);
	else
		listener_ip = purple_network_get_my_ip(bos_conn->fd);
	listener_port = purple_network_get_port_from_fd(conn->listenerfd);
	if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
	{
		aim_im_sendch2_odc_requestdirect(od,
				conn->cookie, conn->sn, purple_network_ip_atoi(listener_ip),
				listener_port, ++conn->lastrequestnumber);

		/* Print a message to a local conversation window */
		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->sn);
		tmp = g_strdup_printf(_("Asking %s to connect to us at %s:%hu for "
				"Direct IM."), conn->sn, listener_ip, listener_port);
		purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
		g_free(tmp);
	}
	else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
	{
		aim_im_sendch2_sendfile_requestdirect(od,
				conn->cookie, conn->sn,
				purple_network_ip_atoi(listener_ip),
				listener_port, ++conn->lastrequestnumber,
				(const gchar *)conn->xferdata.name,
				conn->xferdata.size, conn->xferdata.totfiles);
	}
}