Sun, 15 Apr 2007 02:10:37 +0000
propagate from branch 'im.pidgin.gaim' (head b2836a24d81e7a1bd1d21b3aea8794b094391344)
to branch 'im.pidgin.rlaager.merging.soc-msnp13-to-svn18164' (head 463b4fa9f067b279f843520d95a822adc86a0a1b)
| 13593 | 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; | |
|
15151
bb985812fefa
[gaim-migrate @ 17875]
Mark Doliner <markdoliner@pidgin.im>
parents:
14324
diff
changeset
|
41 | byte_stream_new(&bs, length); |
| 13593 | 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 | ||
|
15151
bb985812fefa
[gaim-migrate @ 17875]
Mark Doliner <markdoliner@pidgin.im>
parents:
14324
diff
changeset
|
51 | g_free(bs.data); |
| 13593 | 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); | |
|
15151
bb985812fefa
[gaim-migrate @ 17875]
Mark Doliner <markdoliner@pidgin.im>
parents:
14324
diff
changeset
|
76 | byte_stream_new(&frame.payload, 1 + sn_length + 8 + 20); |
| 13593 | 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); | |
|
15151
bb985812fefa
[gaim-migrate @ 17875]
Mark Doliner <markdoliner@pidgin.im>
parents:
14324
diff
changeset
|
112 | byte_stream_new(&frame.payload, 1 + sn_length + 2 + 8 + 20); |
| 13593 | 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 | { | |
|
15256
c57887cd5d08
[gaim-migrate @ 17983]
Mark Doliner <markdoliner@pidgin.im>
parents:
15173
diff
changeset
|
214 | /* Read the first 12 bytes (frame length and header) */ |
|
c57887cd5d08
[gaim-migrate @ 17983]
Mark Doliner <markdoliner@pidgin.im>
parents:
15173
diff
changeset
|
215 | read = recv(conn->fd, conn->proxy_header + conn->proxy_header_received, |
|
c57887cd5d08
[gaim-migrate @ 17983]
Mark Doliner <markdoliner@pidgin.im>
parents:
15173
diff
changeset
|
216 | 12 - conn->proxy_header_received, 0); |
| 13593 | 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 */ | |
|
15256
c57887cd5d08
[gaim-migrate @ 17983]
Mark Doliner <markdoliner@pidgin.im>
parents:
15173
diff
changeset
|
241 | conn->proxy_header_received += read; |
|
c57887cd5d08
[gaim-migrate @ 17983]
Mark Doliner <markdoliner@pidgin.im>
parents:
15173
diff
changeset
|
242 | if (conn->proxy_header_received < 12) |
| 13593 | 243 | return; |
| 244 | ||
| 245 | /* We only support a specific version of the proxy protocol */ | |
|
15256
c57887cd5d08
[gaim-migrate @ 17983]
Mark Doliner <markdoliner@pidgin.im>
parents:
15173
diff
changeset
|
246 | if (aimutil_get16(&conn->proxy_header[2]) != PEER_PROXY_PACKET_VERSION) |
| 13593 | 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, | |
|
15256
c57887cd5d08
[gaim-migrate @ 17983]
Mark Doliner <markdoliner@pidgin.im>
parents:
15173
diff
changeset
|
251 | aimutil_get16(&conn->proxy_header[2])); |
| 13593 | 252 | peer_connection_trynext(conn); |
| 253 | return; | |
| 254 | } | |
| 255 | ||
| 256 | /* Initialize a new temporary ProxyFrame for incoming data */ | |
| 257 | frame = g_new0(ProxyFrame, 1); | |
|
15256
c57887cd5d08
[gaim-migrate @ 17983]
Mark Doliner <markdoliner@pidgin.im>
parents:
15173
diff
changeset
|
258 | frame->payload.len = aimutil_get16(&conn->proxy_header[0]) - 10; |
|
c57887cd5d08
[gaim-migrate @ 17983]
Mark Doliner <markdoliner@pidgin.im>
parents:
15173
diff
changeset
|
259 | frame->version = aimutil_get16(&conn->proxy_header[2]); |
|
c57887cd5d08
[gaim-migrate @ 17983]
Mark Doliner <markdoliner@pidgin.im>
parents:
15173
diff
changeset
|
260 | frame->type = aimutil_get16(&conn->proxy_header[4]); |
|
c57887cd5d08
[gaim-migrate @ 17983]
Mark Doliner <markdoliner@pidgin.im>
parents:
15173
diff
changeset
|
261 | frame->unknown = aimutil_get16(&conn->proxy_header[6]); |
|
c57887cd5d08
[gaim-migrate @ 17983]
Mark Doliner <markdoliner@pidgin.im>
parents:
15173
diff
changeset
|
262 | frame->flags = aimutil_get16(&conn->proxy_header[10]); |
| 13593 | 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 | } | |
|
13596
4fc017ae8c25
[gaim-migrate @ 15981]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
301 | |
|
4fc017ae8c25
[gaim-migrate @ 15981]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
302 | frame->payload.offset += read; |
| 13593 | 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); | |
|
15256
c57887cd5d08
[gaim-migrate @ 17983]
Mark Doliner <markdoliner@pidgin.im>
parents:
15173
diff
changeset
|
314 | |
| 13593 | 315 | g_free(frame->payload.data); |
| 316 | g_free(frame); | |
|
15256
c57887cd5d08
[gaim-migrate @ 17983]
Mark Doliner <markdoliner@pidgin.im>
parents:
15173
diff
changeset
|
317 | |
|
c57887cd5d08
[gaim-migrate @ 17983]
Mark Doliner <markdoliner@pidgin.im>
parents:
15173
diff
changeset
|
318 | conn->proxy_header_received = 0; |
| 13593 | 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 | |
|
14171
351b731b9553
[gaim-migrate @ 16743]
Mark Doliner <markdoliner@pidgin.im>
parents:
14166
diff
changeset
|
326 | peer_proxy_connection_established_cb(gpointer data, gint source, const gchar *error_message) |
| 13593 | 327 | { |
| 328 | PeerConnection *conn; | |
| 329 | ||
|
14166
59f2d7f35ad8
[gaim-migrate @ 16735]
Mark Doliner <markdoliner@pidgin.im>
parents:
14151
diff
changeset
|
330 | conn = data; |
| 13593 | 331 | |
| 15173 | 332 | conn->verified_connect_data = NULL; |
| 13593 | 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 | } |