Mercurial > pidgin
comparison libpurple/protocols/oscar/peer_proxy.c @ 15373:5fe8042783c1
Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Sat, 20 Jan 2007 02:32:10 +0000 |
parents | |
children | 32c366eeeb99 |
comparison
equal
deleted
inserted
replaced
15372:f79e0f4df793 | 15373:5fe8042783c1 |
---|---|
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 } |