Fri, 07 Apr 2006 14:13:44 +0000
[gaim-migrate @ 15981]
Fix some bugs. I don't understand why I don't get "might be used
uninitialized" warnings when I compile on my machine.
| 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; | |
| 41 | byte_stream_init(&bs, malloc(length), 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 | 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 | size_t length; | |
| 69 | ||
| 70 | memset(&frame, 0, sizeof(ProxyFrame)); | |
| 71 | frame.type = PEER_PROXY_TYPE_CREATE; | |
| 72 | frame.flags = 0x0000; | |
| 73 | ||
| 74 | account = gaim_connection_get_account(conn->od->gc); | |
| 75 | sn = gaim_account_get_username(account); | |
| 76 | sn_length = strlen(sn); | |
| 77 | length = 1 + sn_length + 8 + 20; | |
| 78 | byte_stream_init(&frame.payload, malloc(length), length); | |
| 79 | byte_stream_put8(&frame.payload, sn_length); | |
| 80 | byte_stream_putraw(&frame.payload, (const guint8 *)sn, sn_length); | |
| 81 | byte_stream_putraw(&frame.payload, conn->cookie, 8); | |
| 82 | ||
| 83 | byte_stream_put16(&frame.payload, 0x0001); /* Type */ | |
| 84 | byte_stream_put16(&frame.payload, 16); /* Length */ | |
| 85 | byte_stream_putcaps(&frame.payload, conn->type); /* Value */ | |
| 86 | ||
| 87 | peer_proxy_send(conn, &frame); | |
| 88 | } | |
| 89 | ||
| 90 | /** | |
| 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. | |
| 94 | * | |
| 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. | |
| 98 | */ | |
| 99 | static void | |
| 100 | peer_proxy_send_join_existing_conn(PeerConnection *conn, guint16 pin) | |
| 101 | { | |
| 102 | ProxyFrame frame; | |
| 103 | GaimAccount *account; | |
| 104 | const gchar *sn; | |
| 105 | guint8 sn_length; | |
| 106 | size_t length; | |
| 107 | ||
| 108 | memset(&frame, 0, sizeof(ProxyFrame)); | |
| 109 | frame.type = PEER_PROXY_TYPE_JOIN; | |
| 110 | frame.flags = 0x0000; | |
| 111 | ||
| 112 | account = gaim_connection_get_account(conn->od->gc); | |
| 113 | sn = gaim_account_get_username(account); | |
| 114 | sn_length = strlen(sn); | |
| 115 | length = 1 + sn_length + 2 + 8 + 20; | |
| 116 | byte_stream_init(&frame.payload, malloc(length), length); | |
| 117 | byte_stream_put8(&frame.payload, sn_length); | |
| 118 | byte_stream_putraw(&frame.payload, (const guint8 *)sn, sn_length); | |
| 119 | byte_stream_put16(&frame.payload, pin); | |
| 120 | byte_stream_putraw(&frame.payload, conn->cookie, 8); | |
| 121 | ||
| 122 | byte_stream_put16(&frame.payload, 0x0001); /* Type */ | |
| 123 | byte_stream_put16(&frame.payload, 16); /* Length */ | |
| 124 | byte_stream_putcaps(&frame.payload, conn->type); /* Value */ | |
| 125 | ||
| 126 | peer_proxy_send(conn, &frame); | |
| 127 | } | |
| 128 | ||
| 129 | /** | |
| 130 | * Handle an incoming peer proxy negotiation frame. | |
| 131 | */ | |
| 132 | static void | |
| 133 | peer_proxy_recv_frame(PeerConnection *conn, ProxyFrame *frame) | |
| 134 | { | |
| 135 | gaim_debug_info("oscar", "Incoming peer proxy frame with " | |
| 136 | "type=0x%04hx, unknown=0x%08x, " | |
| 137 | "flags=0x%04hx, and payload length=%hd\n", frame->type, | |
| 138 | frame->unknown, frame->flags, frame->payload.len); | |
| 139 | ||
| 140 | if (frame->type == PEER_PROXY_TYPE_CREATED) | |
| 141 | { | |
| 142 | /* | |
| 143 | * Read in 2 byte port then 4 byte IP and tell the | |
| 144 | * remote user to connect to it by sending an ICBM. | |
| 145 | */ | |
| 146 | guint16 pin; | |
| 147 | int i; | |
| 148 | guint8 ip[4]; | |
| 149 | ||
| 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, | |
| 155 | conn->cookie, | |
| 156 | conn->sn, ip, pin, ++conn->lastrequestnumber); | |
| 157 | else if (conn->type == OSCAR_CAPABILITY_SENDFILE) | |
| 158 | { | |
| 159 | aim_im_sendch2_sendfile_requestproxy(conn->od, | |
| 160 | conn->cookie, conn->sn, | |
| 161 | ip, pin, ++conn->lastrequestnumber, | |
| 162 | (const gchar *)conn->xferdata.name, | |
| 163 | conn->xferdata.size, conn->xferdata.totfiles); | |
| 164 | } | |
| 165 | } | |
| 166 | else if (frame->type == PEER_PROXY_TYPE_READY) | |
| 167 | { | |
| 168 | gaim_input_remove(conn->watcher_incoming); | |
| 169 | conn->watcher_incoming = 0; | |
| 170 | ||
| 171 | peer_connection_finalize_connection(conn); | |
| 172 | } | |
| 173 | else if (frame->type == PEER_PROXY_TYPE_ERROR) | |
| 174 | { | |
| 175 | if (byte_stream_empty(&frame->payload) >= 2) | |
| 176 | { | |
| 177 | guint16 error; | |
| 178 | const char *msg; | |
| 179 | error = byte_stream_get16(&frame->payload); | |
| 180 | if (error == 0x000d) | |
| 181 | msg = "bad request"; | |
| 182 | else if (error == 0x0010) | |
| 183 | msg = "initial request timed out"; | |
| 184 | else if (error == 0x001a) | |
| 185 | msg ="accept period timed out"; | |
| 186 | else | |
| 187 | msg = "unknown reason"; | |
| 188 | gaim_debug_info("oscar", "Proxy negotiation failed with " | |
| 189 | "error 0x%04hx: %s\n", error, msg); | |
| 190 | } | |
| 191 | else | |
| 192 | { | |
| 193 | gaim_debug_warning("oscar", "Proxy negotiation failed with " | |
| 194 | "an unknown error\n"); | |
| 195 | } | |
| 196 | peer_connection_trynext(conn); | |
| 197 | } | |
| 198 | else | |
| 199 | { | |
| 200 | gaim_debug_warning("oscar", "Unknown peer proxy frame type 0x%04hx.\n", | |
| 201 | frame->type); | |
| 202 | } | |
| 203 | } | |
| 204 | ||
| 205 | static void | |
| 206 | peer_proxy_connection_recv_cb(gpointer data, gint source, GaimInputCondition cond) | |
| 207 | { | |
| 208 | PeerConnection *conn; | |
| 209 | ssize_t read; | |
| 210 | guint8 header[12]; | |
| 211 | ProxyFrame *frame; | |
| 212 | ||
| 213 | conn = data; | |
| 214 | frame = conn->frame; | |
| 215 | ||
| 216 | /* Start reading a new proxy frame */ | |
| 217 | if (frame == NULL) | |
| 218 | { | |
| 219 | /* Peek at the first 12 bytes to get the length */ | |
| 220 | read = recv(conn->fd, &header, 12, MSG_PEEK); | |
| 221 | ||
| 222 | /* Check if the proxy server closed the connection */ | |
| 223 | if (read == 0) | |
| 224 | { | |
| 225 | gaim_debug_info("oscar", "Peer proxy server closed connection\n"); | |
| 226 | peer_connection_trynext(conn); | |
| 227 | return; | |
| 228 | } | |
| 229 | ||
| 230 | /* If there was an error then close the connection */ | |
| 231 | if (read == -1) | |
| 232 | { | |
| 233 | if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) | |
| 234 | /* No worries */ | |
| 235 | return; | |
| 236 | ||
| 237 | gaim_debug_info("oscar", "Lost connection with peer proxy server\n"); | |
| 238 | peer_connection_trynext(conn); | |
| 239 | return; | |
| 240 | } | |
| 241 | ||
| 242 | conn->lastactivity = time(NULL); | |
| 243 | ||
| 244 | /* If we don't even have the first 12 bytes then do nothing */ | |
| 245 | if (read < 12) | |
| 246 | return; | |
| 247 | ||
| 248 | /* Read the first 12 bytes (frame length and header) */ | |
| 249 | read = recv(conn->fd, &header, 12, 0); | |
| 250 | ||
| 251 | /* We only support a specific version of the proxy protocol */ | |
| 252 | if (aimutil_get16(&header[2]) != PEER_PROXY_PACKET_VERSION) | |
| 253 | { | |
| 254 | gaim_debug_warning("oscar", "Expected peer proxy protocol " | |
| 255 | "version %u but received version %u. Closing " | |
| 256 | "connection.\n", PEER_PROXY_PACKET_VERSION, | |
| 257 | aimutil_get16(&header[2])); | |
| 258 | peer_connection_trynext(conn); | |
| 259 | return; | |
| 260 | } | |
| 261 | ||
| 262 | /* Initialize a new temporary ProxyFrame for incoming data */ | |
| 263 | frame = g_new0(ProxyFrame, 1); | |
| 264 | frame->payload.len = aimutil_get16(&header[0]) - 10; | |
| 265 | frame->version = aimutil_get16(&header[2]); | |
| 266 | frame->type = aimutil_get16(&header[4]); | |
| 267 | frame->unknown = aimutil_get16(&header[6]); | |
| 268 | frame->flags = aimutil_get16(&header[10]); | |
| 269 | if (frame->payload.len > 0) | |
| 270 | frame->payload.data = g_new(guint8, frame->payload.len); | |
| 271 | conn->frame = frame; | |
| 272 | } | |
| 273 | ||
| 274 | /* If this frame has a payload then attempt to read it */ | |
| 275 | if (frame->payload.len - frame->payload.offset > 0) | |
| 276 | { | |
| 277 | /* Read data into the temporary buffer until it is complete */ | |
| 278 | read = recv(conn->fd, | |
| 279 | &frame->payload.data[frame->payload.offset], | |
| 280 | frame->payload.len - frame->payload.offset, | |
| 281 | 0); | |
| 282 | ||
| 283 | /* Check if the proxy server closed the connection */ | |
| 284 | if (read == 0) | |
| 285 | { | |
| 286 | gaim_debug_info("oscar", "Peer proxy server closed connection\n"); | |
| 287 | g_free(frame->payload.data); | |
| 288 | g_free(frame); | |
| 289 | conn->frame = NULL; | |
| 290 | peer_connection_trynext(conn); | |
| 291 | return; | |
| 292 | } | |
| 293 | ||
| 294 | if (read == -1) | |
| 295 | { | |
| 296 | if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) | |
| 297 | /* No worries */ | |
| 298 | return; | |
| 299 | ||
| 300 | gaim_debug_info("oscar", "Lost connection with peer proxy server\n"); | |
| 301 | g_free(frame->payload.data); | |
| 302 | g_free(frame); | |
| 303 | conn->frame = NULL; | |
| 304 | peer_connection_trynext(conn); | |
| 305 | return; | |
| 306 | } | |
|
13596
4fc017ae8c25
[gaim-migrate @ 15981]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
307 | |
|
4fc017ae8c25
[gaim-migrate @ 15981]
Mark Doliner <markdoliner@pidgin.im>
parents:
13593
diff
changeset
|
308 | frame->payload.offset += read; |
| 13593 | 309 | } |
| 310 | ||
| 311 | conn->lastactivity = time(NULL); | |
| 312 | if (frame->payload.offset < frame->payload.len) | |
| 313 | /* Waiting for more data to arrive */ | |
| 314 | return; | |
| 315 | ||
| 316 | /* We have a complete proxy frame! Handle it and continue reading */ | |
| 317 | conn->frame = NULL; | |
| 318 | byte_stream_rewind(&frame->payload); | |
| 319 | peer_proxy_recv_frame(conn, frame); | |
| 320 | g_free(frame->payload.data); | |
| 321 | g_free(frame); | |
| 322 | } | |
| 323 | ||
| 324 | /** | |
| 325 | * We tried to make an outgoing connection to a proxy server. It | |
| 326 | * either connected or failed to connect. | |
| 327 | */ | |
| 328 | void | |
| 329 | peer_proxy_connection_established_cb(gpointer data, gint source, GaimInputCondition cond) | |
| 330 | { | |
| 331 | NewPeerConnectionData *new_conn_data; | |
| 332 | GaimConnection *gc; | |
| 333 | PeerConnection *conn; | |
| 334 | ||
| 335 | new_conn_data = data; | |
| 336 | gc = new_conn_data->gc; | |
| 337 | conn = new_conn_data->conn; | |
| 338 | g_free(new_conn_data); | |
| 339 | ||
| 340 | if (!g_list_find(gaim_connections_get_all(), gc)) | |
| 341 | { | |
| 342 | if (source >= 0) | |
| 343 | close(source); | |
| 344 | return; | |
| 345 | } | |
| 346 | ||
| 347 | if (source < 0) | |
| 348 | { | |
| 349 | peer_connection_trynext(conn); | |
| 350 | return; | |
| 351 | } | |
| 352 | ||
| 353 | conn->fd = source; | |
| 354 | conn->watcher_incoming = gaim_input_add(conn->fd, | |
| 355 | GAIM_INPUT_READ, peer_proxy_connection_recv_cb, conn); | |
| 356 | ||
| 357 | if (conn->proxyip != NULL) | |
| 358 | /* Connect to the session created by the remote user */ | |
| 359 | peer_proxy_send_join_existing_conn(conn, conn->port); | |
| 360 | else | |
| 361 | /* Create a new session */ | |
| 362 | peer_proxy_send_create_new_conn(conn); | |
| 363 | } |