2 * Purple's oscar protocol plugin
3 * This file is the legal property of its developers.
4 * Please see the AUTHORS file distributed alongside this file.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
29 peer_proxy_send(PeerConnection
*conn
, ProxyFrame
*frame
)
34 purple_debug_info("oscar", "Outgoing peer proxy frame with "
35 "type=0x%04hx, unknown=0x%08x, flags=0x%04hx, and "
36 "payload length=%" G_GSIZE_FORMAT
"\n",
37 frame
->type
, frame
->unknown
,
38 frame
->flags
, frame
->payload
.len
);
40 length
= 12 + frame
->payload
.len
;
41 byte_stream_new(&bs
, length
);
42 byte_stream_put16(&bs
, length
- 2);
43 byte_stream_put16(&bs
, PEER_PROXY_PACKET_VERSION
);
44 byte_stream_put16(&bs
, frame
->type
);
45 byte_stream_put32(&bs
, frame
->unknown
);
46 byte_stream_put16(&bs
, frame
->flags
);
47 byte_stream_putraw(&bs
, frame
->payload
.data
, frame
->payload
.len
);
49 peer_connection_send(conn
, &bs
);
51 byte_stream_destroy(&bs
);
55 * Create a rendezvous "init send" packet and send it on its merry way.
56 * This is the first packet sent to the proxy server by the first client
57 * to indicate that this will be a proxied connection
59 * @param conn The peer connection.
62 peer_proxy_send_create_new_conn(PeerConnection
*conn
)
65 PurpleAccount
*account
;
69 memset(&frame
, 0, sizeof(ProxyFrame
));
70 frame
.type
= PEER_PROXY_TYPE_CREATE
;
73 account
= purple_connection_get_account(conn
->od
->gc
);
74 bn
= purple_account_get_username(account
);
75 bn_length
= strlen(bn
);
76 byte_stream_new(&frame
.payload
, 1 + bn_length
+ 8 + 20);
77 byte_stream_put8(&frame
.payload
, bn_length
);
78 byte_stream_putraw(&frame
.payload
, (const guint8
*)bn
, bn_length
);
79 byte_stream_putraw(&frame
.payload
, conn
->cookie
, 8);
81 byte_stream_put16(&frame
.payload
, 0x0001); /* Type */
82 byte_stream_put16(&frame
.payload
, 16); /* Length */
83 byte_stream_putcaps(&frame
.payload
, conn
->type
); /* Value */
85 peer_proxy_send(conn
, &frame
);
87 byte_stream_destroy(&frame
.payload
);
91 * Create a rendezvous "init recv" packet and send it on its merry way.
92 * This is the first packet sent to the proxy server by the second client
93 * involved in this rendezvous proxy session.
95 * @param conn The peer connection.
96 * @param pin The 2 byte PIN sent to us by the other user. This acts
97 * as our passcode when establishing the proxy session.
100 peer_proxy_send_join_existing_conn(PeerConnection
*conn
, guint16 pin
)
103 PurpleAccount
*account
;
107 memset(&frame
, 0, sizeof(ProxyFrame
));
108 frame
.type
= PEER_PROXY_TYPE_JOIN
;
109 frame
.flags
= 0x0000;
111 account
= purple_connection_get_account(conn
->od
->gc
);
112 bn
= purple_account_get_username(account
);
113 bn_length
= strlen(bn
);
114 byte_stream_new(&frame
.payload
, 1 + bn_length
+ 2 + 8 + 20);
115 byte_stream_put8(&frame
.payload
, bn_length
);
116 byte_stream_putraw(&frame
.payload
, (const guint8
*)bn
, bn_length
);
117 byte_stream_put16(&frame
.payload
, pin
);
118 byte_stream_putraw(&frame
.payload
, conn
->cookie
, 8);
120 byte_stream_put16(&frame
.payload
, 0x0001); /* Type */
121 byte_stream_put16(&frame
.payload
, 16); /* Length */
122 byte_stream_putcaps(&frame
.payload
, conn
->type
); /* Value */
124 peer_proxy_send(conn
, &frame
);
126 byte_stream_destroy(&frame
.payload
);
130 * Handle an incoming peer proxy negotiation frame.
133 peer_proxy_recv_frame(PeerConnection
*conn
, ProxyFrame
*frame
)
135 purple_debug_info("oscar", "Incoming peer proxy frame with "
136 "type=0x%04hx, unknown=0x%08x, flags=0x%04hx, and "
137 "payload length=%" G_GSIZE_FORMAT
"\n", frame
->type
,
138 frame
->unknown
, frame
->flags
, frame
->payload
.len
);
140 if (frame
->type
== PEER_PROXY_TYPE_CREATED
)
143 * Read in 2 byte port then 4 byte IP and tell the
144 * remote user to connect to it by sending an ICBM.
150 pin
= byte_stream_get16(&frame
->payload
);
151 for (i
= 0; i
< 4; i
++)
152 ip
[i
] = byte_stream_get8(&frame
->payload
);
153 if (conn
->type
== OSCAR_CAPABILITY_DIRECTIM
)
154 aim_im_sendch2_odc_requestproxy(conn
->od
,
156 conn
->bn
, ip
, pin
, ++conn
->lastrequestnumber
);
157 else if (conn
->type
== OSCAR_CAPABILITY_SENDFILE
)
159 aim_im_sendch2_sendfile_requestproxy(conn
->od
,
160 conn
->cookie
, conn
->bn
,
161 ip
, pin
, ++conn
->lastrequestnumber
,
162 (const gchar
*)conn
->xferdata
.name
,
163 conn
->xferdata
.size
, conn
->xferdata
.totfiles
);
166 else if (frame
->type
== PEER_PROXY_TYPE_READY
)
168 purple_input_remove(conn
->watcher_incoming
);
169 conn
->watcher_incoming
= 0;
171 peer_connection_finalize_connection(conn
);
173 else if (frame
->type
== PEER_PROXY_TYPE_ERROR
)
175 if (byte_stream_bytes_left(&frame
->payload
) >= 2)
179 error
= byte_stream_get16(&frame
->payload
);
182 else if (error
== 0x0010)
183 msg
= "initial request timed out";
184 else if (error
== 0x001a)
185 msg
="accept period timed out";
187 msg
= "unknown reason";
188 purple_debug_info("oscar", "Proxy negotiation failed with "
189 "error 0x%04hx: %s\n", error
, msg
);
193 purple_debug_warning("oscar", "Proxy negotiation failed with "
194 "an unknown error\n");
196 peer_connection_trynext(conn
);
200 purple_debug_warning("oscar", "Unknown peer proxy frame type 0x%04hx.\n",
206 peer_proxy_connection_recv_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
208 PeerConnection
*conn
;
215 /* Start reading a new proxy frame */
218 /* Read the first 12 bytes (frame length and header) */
219 read
= recv(conn
->fd
, conn
->proxy_header
+ conn
->proxy_header_received
,
220 12 - conn
->proxy_header_received
, 0);
222 /* Check if the proxy server closed the connection */
225 purple_debug_info("oscar", "Peer proxy server closed connection\n");
226 peer_connection_trynext(conn
);
230 /* If there was an error then close the connection */
233 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
237 purple_debug_info("oscar", "Lost connection with peer proxy server\n");
238 peer_connection_trynext(conn
);
242 conn
->lastactivity
= time(NULL
);
244 /* If we don't even have the first 12 bytes then do nothing */
245 conn
->proxy_header_received
+= read
;
246 if (conn
->proxy_header_received
< 12)
249 /* We only support a specific version of the proxy protocol */
250 if (aimutil_get16(&conn
->proxy_header
[2]) != PEER_PROXY_PACKET_VERSION
)
252 purple_debug_warning("oscar", "Expected peer proxy protocol "
253 "version %u but received version %u. Closing "
254 "connection.\n", PEER_PROXY_PACKET_VERSION
,
255 aimutil_get16(&conn
->proxy_header
[2]));
256 peer_connection_trynext(conn
);
260 /* Initialize a new temporary ProxyFrame for incoming data */
261 frame
= g_new0(ProxyFrame
, 1);
262 frame
->payload
.len
= aimutil_get16(&conn
->proxy_header
[0]) - 10;
263 frame
->version
= aimutil_get16(&conn
->proxy_header
[2]);
264 frame
->type
= aimutil_get16(&conn
->proxy_header
[4]);
265 frame
->unknown
= aimutil_get16(&conn
->proxy_header
[6]);
266 frame
->flags
= aimutil_get16(&conn
->proxy_header
[10]);
267 if (frame
->payload
.len
> 0)
268 frame
->payload
.data
= g_new(guint8
, frame
->payload
.len
);
272 /* If this frame has a payload then attempt to read it */
273 if (frame
->payload
.len
- frame
->payload
.offset
> 0)
275 /* Read data into the temporary buffer until it is complete */
276 read
= recv(conn
->fd
,
277 &frame
->payload
.data
[frame
->payload
.offset
],
278 frame
->payload
.len
- frame
->payload
.offset
,
281 /* Check if the proxy server closed the connection */
284 purple_debug_info("oscar", "Peer proxy server closed connection\n");
285 g_free(frame
->payload
.data
);
288 peer_connection_trynext(conn
);
292 /* If there was an error then close the connection */
295 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
299 purple_debug_info("oscar", "Lost connection with peer proxy server\n");
300 g_free(frame
->payload
.data
);
303 peer_connection_trynext(conn
);
307 frame
->payload
.offset
+= read
;
310 conn
->lastactivity
= time(NULL
);
311 if (frame
->payload
.offset
< frame
->payload
.len
)
312 /* Waiting for more data to arrive */
315 /* We have a complete proxy frame! Handle it and continue reading */
317 byte_stream_rewind(&frame
->payload
);
318 peer_proxy_recv_frame(conn
, frame
);
320 g_free(frame
->payload
.data
);
323 conn
->proxy_header_received
= 0;
327 * We tried to make an outgoing connection to a proxy server. It
328 * either connected or failed to connect.
331 peer_proxy_connection_established_cb(gpointer data
, gint source
, const gchar
*error_message
)
333 PeerConnection
*conn
;
337 conn
->verified_connect_data
= NULL
;
341 peer_connection_trynext(conn
);
346 conn
->watcher_incoming
= purple_input_add(conn
->fd
,
347 PURPLE_INPUT_READ
, peer_proxy_connection_recv_cb
, conn
);
349 if (conn
->proxyip
!= NULL
)
350 /* Connect to the session created by the remote user */
351 peer_proxy_send_join_existing_conn(conn
, conn
->port
);
353 /* Create a new session */
354 peer_proxy_send_create_new_conn(conn
);