| |
1 /* |
| |
2 * Gaim's oscar protocol plugin |
| |
3 * This file is the legal property of its developers. |
| |
4 * Please see the AUTHORS file distributed alongside this file. |
| |
5 * |
| |
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. |
| |
10 * |
| |
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. |
| |
15 * |
| |
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
19 */ |
| |
20 |
| |
21 #ifdef HAVE_CONFIG_H |
| |
22 #include <config.h> |
| |
23 #endif |
| |
24 |
| |
25 #include "oscar.h" |
| |
26 #include "peer.h" |
| |
27 |
| |
28 static void |
| |
29 peer_proxy_send(PeerConnection *conn, ProxyFrame *frame) |
| |
30 { |
| |
31 size_t length; |
| |
32 ByteStream bs; |
| |
33 |
| |
34 gaim_debug_info("oscar", "Outgoing peer proxy frame with " |
| |
35 "type=0x%04hx, unknown=0x%08x, " |
| |
36 "flags=0x%04hx, and payload length=%hd\n", |
| |
37 frame->type, frame->unknown, |
| |
38 frame->flags, frame->payload.len); |
| |
39 |
| |
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); |
| |
48 |
| |
49 peer_connection_send(conn, &bs); |
| |
50 |
| |
51 g_free(bs.data); |
| |
52 } |
| |
53 |
| |
54 /** |
| |
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 |
| |
58 * |
| |
59 * @param conn The peer connection. |
| |
60 */ |
| |
61 static void |
| |
62 peer_proxy_send_create_new_conn(PeerConnection *conn) |
| |
63 { |
| |
64 ProxyFrame frame; |
| |
65 GaimAccount *account; |
| |
66 const gchar *sn; |
| |
67 guint8 sn_length; |
| |
68 |
| |
69 memset(&frame, 0, sizeof(ProxyFrame)); |
| |
70 frame.type = PEER_PROXY_TYPE_CREATE; |
| |
71 frame.flags = 0x0000; |
| |
72 |
| |
73 account = gaim_connection_get_account(conn->od->gc); |
| |
74 sn = gaim_account_get_username(account); |
| |
75 sn_length = strlen(sn); |
| |
76 byte_stream_new(&frame.payload, 1 + sn_length + 8 + 20); |
| |
77 byte_stream_put8(&frame.payload, sn_length); |
| |
78 byte_stream_putraw(&frame.payload, (const guint8 *)sn, sn_length); |
| |
79 byte_stream_putraw(&frame.payload, conn->cookie, 8); |
| |
80 |
| |
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 */ |
| |
84 |
| |
85 peer_proxy_send(conn, &frame); |
| |
86 } |
| |
87 |
| |
88 /** |
| |
89 * Create a rendezvous "init recv" packet and send it on its merry way. |
| |
90 * This is the first packet sent to the proxy server by the second client |
| |
91 * involved in this rendezvous proxy session. |
| |
92 * |
| |
93 * @param conn The peer connection. |
| |
94 * @param pin The 2 byte PIN sent to us by the other user. This acts |
| |
95 * as our passcode when establishing the proxy session. |
| |
96 */ |
| |
97 static void |
| |
98 peer_proxy_send_join_existing_conn(PeerConnection *conn, guint16 pin) |
| |
99 { |
| |
100 ProxyFrame frame; |
| |
101 GaimAccount *account; |
| |
102 const gchar *sn; |
| |
103 guint8 sn_length; |
| |
104 |
| |
105 memset(&frame, 0, sizeof(ProxyFrame)); |
| |
106 frame.type = PEER_PROXY_TYPE_JOIN; |
| |
107 frame.flags = 0x0000; |
| |
108 |
| |
109 account = gaim_connection_get_account(conn->od->gc); |
| |
110 sn = gaim_account_get_username(account); |
| |
111 sn_length = strlen(sn); |
| |
112 byte_stream_new(&frame.payload, 1 + sn_length + 2 + 8 + 20); |
| |
113 byte_stream_put8(&frame.payload, sn_length); |
| |
114 byte_stream_putraw(&frame.payload, (const guint8 *)sn, sn_length); |
| |
115 byte_stream_put16(&frame.payload, pin); |
| |
116 byte_stream_putraw(&frame.payload, conn->cookie, 8); |
| |
117 |
| |
118 byte_stream_put16(&frame.payload, 0x0001); /* Type */ |
| |
119 byte_stream_put16(&frame.payload, 16); /* Length */ |
| |
120 byte_stream_putcaps(&frame.payload, conn->type); /* Value */ |
| |
121 |
| |
122 peer_proxy_send(conn, &frame); |
| |
123 } |
| |
124 |
| |
125 /** |
| |
126 * Handle an incoming peer proxy negotiation frame. |
| |
127 */ |
| |
128 static void |
| |
129 peer_proxy_recv_frame(PeerConnection *conn, ProxyFrame *frame) |
| |
130 { |
| |
131 gaim_debug_info("oscar", "Incoming peer proxy frame with " |
| |
132 "type=0x%04hx, unknown=0x%08x, " |
| |
133 "flags=0x%04hx, and payload length=%hd\n", frame->type, |
| |
134 frame->unknown, frame->flags, frame->payload.len); |
| |
135 |
| |
136 if (frame->type == PEER_PROXY_TYPE_CREATED) |
| |
137 { |
| |
138 /* |
| |
139 * Read in 2 byte port then 4 byte IP and tell the |
| |
140 * remote user to connect to it by sending an ICBM. |
| |
141 */ |
| |
142 guint16 pin; |
| |
143 int i; |
| |
144 guint8 ip[4]; |
| |
145 |
| |
146 pin = byte_stream_get16(&frame->payload); |
| |
147 for (i = 0; i < 4; i++) |
| |
148 ip[i] = byte_stream_get8(&frame->payload); |
| |
149 if (conn->type == OSCAR_CAPABILITY_DIRECTIM) |
| |
150 aim_im_sendch2_odc_requestproxy(conn->od, |
| |
151 conn->cookie, |
| |
152 conn->sn, ip, pin, ++conn->lastrequestnumber); |
| |
153 else if (conn->type == OSCAR_CAPABILITY_SENDFILE) |
| |
154 { |
| |
155 aim_im_sendch2_sendfile_requestproxy(conn->od, |
| |
156 conn->cookie, conn->sn, |
| |
157 ip, pin, ++conn->lastrequestnumber, |
| |
158 (const gchar *)conn->xferdata.name, |
| |
159 conn->xferdata.size, conn->xferdata.totfiles); |
| |
160 } |
| |
161 } |
| |
162 else if (frame->type == PEER_PROXY_TYPE_READY) |
| |
163 { |
| |
164 gaim_input_remove(conn->watcher_incoming); |
| |
165 conn->watcher_incoming = 0; |
| |
166 |
| |
167 peer_connection_finalize_connection(conn); |
| |
168 } |
| |
169 else if (frame->type == PEER_PROXY_TYPE_ERROR) |
| |
170 { |
| |
171 if (byte_stream_empty(&frame->payload) >= 2) |
| |
172 { |
| |
173 guint16 error; |
| |
174 const char *msg; |
| |
175 error = byte_stream_get16(&frame->payload); |
| |
176 if (error == 0x000d) |
| |
177 msg = "bad request"; |
| |
178 else if (error == 0x0010) |
| |
179 msg = "initial request timed out"; |
| |
180 else if (error == 0x001a) |
| |
181 msg ="accept period timed out"; |
| |
182 else |
| |
183 msg = "unknown reason"; |
| |
184 gaim_debug_info("oscar", "Proxy negotiation failed with " |
| |
185 "error 0x%04hx: %s\n", error, msg); |
| |
186 } |
| |
187 else |
| |
188 { |
| |
189 gaim_debug_warning("oscar", "Proxy negotiation failed with " |
| |
190 "an unknown error\n"); |
| |
191 } |
| |
192 peer_connection_trynext(conn); |
| |
193 } |
| |
194 else |
| |
195 { |
| |
196 gaim_debug_warning("oscar", "Unknown peer proxy frame type 0x%04hx.\n", |
| |
197 frame->type); |
| |
198 } |
| |
199 } |
| |
200 |
| |
201 static void |
| |
202 peer_proxy_connection_recv_cb(gpointer data, gint source, GaimInputCondition cond) |
| |
203 { |
| |
204 PeerConnection *conn; |
| |
205 ssize_t read; |
| |
206 ProxyFrame *frame; |
| |
207 |
| |
208 conn = data; |
| |
209 frame = conn->frame; |
| |
210 |
| |
211 /* Start reading a new proxy frame */ |
| |
212 if (frame == NULL) |
| |
213 { |
| |
214 /* Read the first 12 bytes (frame length and header) */ |
| |
215 read = recv(conn->fd, conn->proxy_header + conn->proxy_header_received, |
| |
216 12 - conn->proxy_header_received, 0); |
| |
217 |
| |
218 /* Check if the proxy server closed the connection */ |
| |
219 if (read == 0) |
| |
220 { |
| |
221 gaim_debug_info("oscar", "Peer proxy server closed connection\n"); |
| |
222 peer_connection_trynext(conn); |
| |
223 return; |
| |
224 } |
| |
225 |
| |
226 /* If there was an error then close the connection */ |
| |
227 if (read == -1) |
| |
228 { |
| |
229 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) |
| |
230 /* No worries */ |
| |
231 return; |
| |
232 |
| |
233 gaim_debug_info("oscar", "Lost connection with peer proxy server\n"); |
| |
234 peer_connection_trynext(conn); |
| |
235 return; |
| |
236 } |
| |
237 |
| |
238 conn->lastactivity = time(NULL); |
| |
239 |
| |
240 /* If we don't even have the first 12 bytes then do nothing */ |
| |
241 conn->proxy_header_received += read; |
| |
242 if (conn->proxy_header_received < 12) |
| |
243 return; |
| |
244 |
| |
245 /* We only support a specific version of the proxy protocol */ |
| |
246 if (aimutil_get16(&conn->proxy_header[2]) != PEER_PROXY_PACKET_VERSION) |
| |
247 { |
| |
248 gaim_debug_warning("oscar", "Expected peer proxy protocol " |
| |
249 "version %u but received version %u. Closing " |
| |
250 "connection.\n", PEER_PROXY_PACKET_VERSION, |
| |
251 aimutil_get16(&conn->proxy_header[2])); |
| |
252 peer_connection_trynext(conn); |
| |
253 return; |
| |
254 } |
| |
255 |
| |
256 /* Initialize a new temporary ProxyFrame for incoming data */ |
| |
257 frame = g_new0(ProxyFrame, 1); |
| |
258 frame->payload.len = aimutil_get16(&conn->proxy_header[0]) - 10; |
| |
259 frame->version = aimutil_get16(&conn->proxy_header[2]); |
| |
260 frame->type = aimutil_get16(&conn->proxy_header[4]); |
| |
261 frame->unknown = aimutil_get16(&conn->proxy_header[6]); |
| |
262 frame->flags = aimutil_get16(&conn->proxy_header[10]); |
| |
263 if (frame->payload.len > 0) |
| |
264 frame->payload.data = g_new(guint8, frame->payload.len); |
| |
265 conn->frame = frame; |
| |
266 } |
| |
267 |
| |
268 /* If this frame has a payload then attempt to read it */ |
| |
269 if (frame->payload.len - frame->payload.offset > 0) |
| |
270 { |
| |
271 /* Read data into the temporary buffer until it is complete */ |
| |
272 read = recv(conn->fd, |
| |
273 &frame->payload.data[frame->payload.offset], |
| |
274 frame->payload.len - frame->payload.offset, |
| |
275 0); |
| |
276 |
| |
277 /* Check if the proxy server closed the connection */ |
| |
278 if (read == 0) |
| |
279 { |
| |
280 gaim_debug_info("oscar", "Peer proxy server closed connection\n"); |
| |
281 g_free(frame->payload.data); |
| |
282 g_free(frame); |
| |
283 conn->frame = NULL; |
| |
284 peer_connection_trynext(conn); |
| |
285 return; |
| |
286 } |
| |
287 |
| |
288 if (read == -1) |
| |
289 { |
| |
290 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) |
| |
291 /* No worries */ |
| |
292 return; |
| |
293 |
| |
294 gaim_debug_info("oscar", "Lost connection with peer proxy server\n"); |
| |
295 g_free(frame->payload.data); |
| |
296 g_free(frame); |
| |
297 conn->frame = NULL; |
| |
298 peer_connection_trynext(conn); |
| |
299 return; |
| |
300 } |
| |
301 |
| |
302 frame->payload.offset += read; |
| |
303 } |
| |
304 |
| |
305 conn->lastactivity = time(NULL); |
| |
306 if (frame->payload.offset < frame->payload.len) |
| |
307 /* Waiting for more data to arrive */ |
| |
308 return; |
| |
309 |
| |
310 /* We have a complete proxy frame! Handle it and continue reading */ |
| |
311 conn->frame = NULL; |
| |
312 byte_stream_rewind(&frame->payload); |
| |
313 peer_proxy_recv_frame(conn, frame); |
| |
314 |
| |
315 g_free(frame->payload.data); |
| |
316 g_free(frame); |
| |
317 |
| |
318 conn->proxy_header_received = 0; |
| |
319 } |
| |
320 |
| |
321 /** |
| |
322 * We tried to make an outgoing connection to a proxy server. It |
| |
323 * either connected or failed to connect. |
| |
324 */ |
| |
325 void |
| |
326 peer_proxy_connection_established_cb(gpointer data, gint source, const gchar *error_message) |
| |
327 { |
| |
328 PeerConnection *conn; |
| |
329 |
| |
330 conn = data; |
| |
331 |
| |
332 conn->verified_connect_data = NULL; |
| |
333 |
| |
334 if (source < 0) |
| |
335 { |
| |
336 peer_connection_trynext(conn); |
| |
337 return; |
| |
338 } |
| |
339 |
| |
340 conn->fd = source; |
| |
341 conn->watcher_incoming = gaim_input_add(conn->fd, |
| |
342 GAIM_INPUT_READ, peer_proxy_connection_recv_cb, conn); |
| |
343 |
| |
344 if (conn->proxyip != NULL) |
| |
345 /* Connect to the session created by the remote user */ |
| |
346 peer_proxy_send_join_existing_conn(conn, conn->port); |
| |
347 else |
| |
348 /* Create a new session */ |
| |
349 peer_proxy_send_create_new_conn(conn); |
| |
350 } |