Mercurial > pidgin.yaz
comparison libpurple/protocols/msn/directconn.c @ 30433:119bd7b072eb
Initial support for direct connections. Preliminary patch from ticket #247
by Gbor Szuromi. Still needs lots of testing and fixes.
References #247.
committer: Elliott Sales de Andrade <qulogic@pidgin.im>
author | kukkerman@gmail.com |
---|---|
date | Wed, 17 Mar 2010 03:45:07 +0000 |
parents | 3157a8ea0012 |
children | b1cda3f8fdc9 |
comparison
equal
deleted
inserted
replaced
30001:bfaf039aed87 | 30433:119bd7b072eb |
---|---|
25 #include "directconn.h" | 25 #include "directconn.h" |
26 | 26 |
27 #include "slp.h" | 27 #include "slp.h" |
28 #include "slpmsg.h" | 28 #include "slpmsg.h" |
29 | 29 |
30 /************************************************************************** | 30 #define DC_SESSION_ID_OFFS 0 |
31 * Directconn Specific | 31 #define DC_SEQ_ID_OFFS 4 |
32 **************************************************************************/ | 32 #define DC_DATA_OFFSET_OFFS 8 |
33 #define DC_TOTAL_DATA_SIZE_OFFS 16 | |
34 #define DC_MESSAGE_LENGTH_OFFS 24 | |
35 #define DC_FLAGS_OFFS 28 | |
36 #define DC_ACK_ID_OFFS 32 | |
37 #define DC_ACK_UID_OFFS 36 | |
38 #define DC_ACK_DATA_SIZE_OFFS 40 | |
39 #define DC_MESSAGE_BODY_OFFS 48 | |
40 | |
41 #define DC_PACKET_HEADER_SIZE 48 | |
42 #define DC_MAX_BODY_SIZE 1352 | |
43 #define DC_MAX_PACKET_SIZE (DC_PACKET_HEADER_SIZE + DC_MAX_BODY_SIZE) | |
44 | |
45 static void | |
46 msn_dc_generate_nonce(MsnDirectConn *dc) | |
47 { | |
48 PurpleCipher *cipher = NULL; | |
49 PurpleCipherContext *context = NULL; | |
50 static guchar digest[20]; | |
51 int i; | |
52 | |
53 guint32 g1; | |
54 guint16 g2; | |
55 guint16 g3; | |
56 guint64 g4; | |
57 | |
58 cipher = purple_ciphers_find_cipher("sha1"); | |
59 g_return_if_fail(cipher != NULL); | |
60 | |
61 for (i = 0; i < 16; i++) | |
62 dc->nonce[i] = rand() & 0xff; | |
63 | |
64 context = purple_cipher_context_new(cipher, NULL); | |
65 purple_cipher_context_append(context, dc->nonce, 16); | |
66 purple_cipher_context_digest(context, 20, digest, NULL); | |
67 purple_cipher_context_destroy(context); | |
68 | |
69 g1 = *((guint32*)(digest + 0)); | |
70 g1 = GUINT32_FROM_LE(g1); | |
71 | |
72 g2 = *((guint16*)(digest + 4)); | |
73 g2 = GUINT16_FROM_LE(g2); | |
74 | |
75 g3 = *((guint16*)(digest + 6)); | |
76 g3 = GUINT32_FROM_LE(g3); | |
77 | |
78 g4 = *((guint64*)(digest + 8)); | |
79 g4 = GUINT64_FROM_BE(g4); | |
80 | |
81 g_sprintf( | |
82 dc->nonce_hash, | |
83 "%08X-%04X-%04X-%04X-%08X%04X", | |
84 g1, | |
85 g2, | |
86 g3, | |
87 (guint16)(g4 >> 48), | |
88 (guint32)((g4 >> 16) & 0xffffffff), | |
89 (guint16)(g4 & 0xffff) | |
90 ); | |
91 } | |
92 | |
93 static MsnDirectConnPacket* | |
94 msn_dc_new_packet() | |
95 { | |
96 MsnDirectConnPacket *p; | |
97 | |
98 p = g_new0(MsnDirectConnPacket, 1); | |
99 p->data = NULL; | |
100 p->sent_cb = NULL; | |
101 p->msg = NULL; | |
102 | |
103 return p; | |
104 } | |
105 | |
106 static void | |
107 msn_dc_destroy_packet(MsnDirectConnPacket *p) | |
108 { | |
109 if (p->data) | |
110 g_free(p->data); | |
111 | |
112 if (p->msg) | |
113 msn_message_unref(p->msg); | |
114 | |
115 g_free(p); | |
116 } | |
117 | |
118 MsnDirectConn* | |
119 msn_dc_new(MsnSlpCall *slpcall) | |
120 { | |
121 MsnDirectConn *dc; | |
122 | |
123 purple_debug_info("msn", "msn_dc_new\n"); | |
124 | |
125 g_return_val_if_fail(slpcall != NULL, NULL); | |
126 | |
127 dc = g_new0(MsnDirectConn, 1); | |
128 | |
129 dc->slplink = slpcall->slplink; | |
130 dc->slpcall = slpcall; | |
131 | |
132 if (dc->slplink->dc != NULL) | |
133 purple_debug_warning("msn", "msn_dc_new: slplink already has an allocated DC!\n"); | |
134 | |
135 dc->slplink->dc = dc; | |
136 | |
137 dc->msg_body = NULL; | |
138 dc->prev_ack = NULL; | |
139 dc->listen_data = NULL; | |
140 dc->connect_data = NULL; | |
141 dc->listenfd = -1; | |
142 dc->listenfd_handle = 0; | |
143 dc->connect_timeout_handle = 0; | |
144 dc->fd = -1; | |
145 dc->recv_handle = 0; | |
146 dc->send_handle = 0; | |
147 dc->state = DC_STATE_CLOSED; | |
148 dc->in_buffer = NULL; | |
149 dc->out_queue = g_queue_new(); | |
150 dc->msg_pos = 0; | |
151 dc->send_connection_info_msg_cb = NULL; | |
152 dc->ext_ip = NULL; | |
153 dc->timeout_handle = 0; | |
154 dc->progress = FALSE; | |
155 //dc->num_calls = 1; | |
156 | |
157 msn_dc_generate_nonce(dc); | |
158 | |
159 return dc; | |
160 } | |
33 | 161 |
34 void | 162 void |
35 msn_directconn_send_handshake(MsnDirectConn *directconn) | 163 msn_dc_destroy(MsnDirectConn *dc) |
36 { | 164 { |
37 MsnSlpLink *slplink; | 165 MsnSlpLink *slplink; |
38 MsnSlpMessage *slpmsg; | 166 |
39 | 167 purple_debug_info("msn", "msn_dc_destroy\n"); |
40 g_return_if_fail(directconn != NULL); | 168 |
41 | 169 g_return_if_fail(dc != NULL); |
42 slplink = directconn->slplink; | 170 |
43 | 171 slplink = dc->slplink; |
44 slpmsg = msn_slpmsg_new(slplink); | 172 |
45 slpmsg->flags = 0x100; | 173 if (dc->slpcall != NULL) |
46 | 174 dc->slpcall->wait_for_socket = FALSE; |
47 if (directconn->nonce != NULL) | 175 |
48 { | 176 slplink->dc = NULL; |
49 guint32 t1; | 177 |
50 guint16 t2; | 178 if (slplink->swboard == NULL) |
51 guint16 t3; | 179 msn_slplink_destroy(slplink); |
52 guint16 t4; | 180 |
53 guint64 t5; | 181 if (dc->msg_body != NULL) { |
54 | 182 g_free(dc->msg_body); |
55 sscanf (directconn->nonce, "%08X-%04hX-%04hX-%04hX-%012" G_GINT64_MODIFIER "X", &t1, &t2, &t3, &t4, &t5); | 183 dc->msg_body = NULL; |
56 | 184 } |
57 t1 = GUINT32_TO_LE(t1); | 185 |
58 t2 = GUINT16_TO_LE(t2); | 186 if (dc->prev_ack) { |
59 t3 = GUINT16_TO_LE(t3); | 187 msn_slpmsg_destroy(dc->prev_ack); |
60 t4 = GUINT16_TO_BE(t4); | 188 dc->prev_ack = NULL; |
61 t5 = GUINT64_TO_BE(t5); | 189 } |
62 | 190 |
63 slpmsg->ack_id = t1; | 191 if (dc->listen_data != NULL) { |
64 slpmsg->ack_sub_id = t2 | (t3 << 16); | 192 purple_network_listen_cancel(dc->listen_data); |
65 slpmsg->ack_size = t4 | t5; | 193 dc->listen_data = NULL; |
66 } | 194 } |
67 | 195 |
68 g_free(directconn->nonce); | 196 if (dc->connect_data != NULL) { |
69 | 197 purple_proxy_connect_cancel(dc->connect_data); |
70 msn_slplink_send_slpmsg(slplink, slpmsg); | 198 dc->connect_data = NULL; |
71 | 199 } |
72 directconn->acked =TRUE; | 200 |
73 } | 201 if (dc->listenfd != -1) { |
74 | 202 purple_network_remove_port_mapping(dc->listenfd); |
75 /************************************************************************** | 203 close(dc->listenfd); |
76 * Connection Functions | 204 dc->listenfd = -1; |
77 **************************************************************************/ | 205 } |
206 | |
207 if (dc->listenfd_handle != 0) { | |
208 purple_timeout_remove(dc->listenfd_handle); | |
209 dc->listenfd_handle = 0; | |
210 } | |
211 | |
212 if (dc->connect_timeout_handle != 0) { | |
213 purple_timeout_remove(dc->connect_timeout_handle); | |
214 dc->connect_timeout_handle = 0; | |
215 } | |
216 | |
217 if (dc->fd != -1) { | |
218 close(dc->fd); | |
219 dc->fd = -1; | |
220 } | |
221 | |
222 if (dc->send_handle != 0) { | |
223 purple_input_remove(dc->send_handle); | |
224 dc->send_handle = 0; | |
225 } | |
226 | |
227 if (dc->recv_handle != 0) { | |
228 purple_input_remove(dc->recv_handle); | |
229 dc->recv_handle = 0; | |
230 } | |
231 | |
232 if (dc->in_buffer != NULL) { | |
233 g_free(dc->in_buffer); | |
234 dc->in_buffer = NULL; | |
235 } | |
236 | |
237 if (dc->out_queue != NULL) { | |
238 while (!g_queue_is_empty(dc->out_queue)) | |
239 msn_dc_destroy_packet( g_queue_pop_head(dc->out_queue) ); | |
240 | |
241 g_queue_free(dc->out_queue); | |
242 } | |
243 | |
244 if (dc->ext_ip != NULL) { | |
245 g_free(dc->ext_ip); | |
246 dc->ext_ip = NULL; | |
247 } | |
248 | |
249 if (dc->timeout_handle != 0) { | |
250 purple_timeout_remove(dc->timeout_handle); | |
251 dc->timeout_handle = 0; | |
252 } | |
253 | |
254 g_free(dc); | |
255 } | |
256 | |
257 /* | |
258 void | |
259 msn_dc_ref(MsnDirectConn *dc) | |
260 { | |
261 g_return_if_fail(dc != NULL); | |
262 | |
263 dc->num_calls++; | |
264 } | |
265 | |
266 void | |
267 msn_dc_unref(MsnDirectConn *dc) | |
268 { | |
269 g_return_if_fail(dc != NULL); | |
270 | |
271 | |
272 if (dc->num_calls > 0) { | |
273 dc->num_calls--; | |
274 } | |
275 } | |
276 */ | |
277 | |
278 void | |
279 msn_dc_send_invite(MsnDirectConn *dc) | |
280 { | |
281 MsnSlpCall *slpcall; | |
282 MsnSlpMessage *msg; | |
283 gchar *header; | |
284 | |
285 purple_debug_info("msn", "msn_dc_send_invite\n"); | |
286 | |
287 g_return_if_fail(dc != NULL); | |
288 | |
289 slpcall = dc->slpcall; | |
290 g_return_if_fail(slpcall != NULL); | |
291 | |
292 header = g_strdup_printf( | |
293 "INVITE MSNMSGR:%s MSNSLP/1.0", | |
294 slpcall->slplink->remote_user | |
295 ); | |
296 | |
297 msg = msn_slpmsg_sip_new( | |
298 slpcall, | |
299 0, | |
300 header, | |
301 slpcall->branch, | |
302 "application/x-msnmsgr-transrespbody", | |
303 dc->msg_body | |
304 ); | |
305 g_free(header); | |
306 g_free(dc->msg_body); | |
307 dc->msg_body = NULL; | |
308 | |
309 msn_slplink_queue_slpmsg(slpcall->slplink, msg); | |
310 } | |
311 | |
312 void | |
313 msn_dc_send_ok(MsnDirectConn *dc) | |
314 { | |
315 purple_debug_info("msn", "msn_dc_send_ok\n"); | |
316 | |
317 g_return_if_fail(dc != NULL); | |
318 | |
319 msn_slp_send_ok(dc->slpcall, dc->slpcall->branch, | |
320 "application/x-msnmsgr-transrespbody", dc->msg_body); | |
321 g_free(dc->msg_body); | |
322 dc->msg_body = NULL; | |
323 | |
324 msn_slplink_send_slpmsg(dc->slpcall->slplink, dc->prev_ack); | |
325 msn_slpmsg_destroy(dc->prev_ack); | |
326 dc->prev_ack = NULL; | |
327 msn_slplink_send_queued_slpmsgs(dc->slpcall->slplink); | |
328 } | |
329 | |
330 static void | |
331 msn_dc_fallback_to_p2p(MsnDirectConn *dc) | |
332 { | |
333 MsnSlpCall *slpcall; | |
334 PurpleXfer *xfer; | |
335 | |
336 purple_debug_info("msn", "msn_dc_try_fallback_to_p2p\n"); | |
337 | |
338 g_return_if_fail(dc != NULL); | |
339 | |
340 slpcall = dc->slpcall; | |
341 g_return_if_fail(slpcall != NULL); | |
342 | |
343 xfer = slpcall->xfer; | |
344 g_return_if_fail(xfer != NULL); | |
345 | |
346 msn_dc_destroy(dc); | |
347 | |
348 msn_slpcall_session_init(slpcall); | |
349 | |
350 /* | |
351 switch (purple_xfer_get_status(xfer)) { | |
352 case PURPLE_XFER_STATUS_NOT_STARTED: | |
353 case PURPLE_XFER_STATUS_ACCEPTED: | |
354 msn_slpcall_session_init(slpcall); | |
355 break; | |
356 | |
357 case PURPLE_XFER_STATUS_STARTED: | |
358 slpcall->session_init_cb = NULL; | |
359 slpcall->end_cb = NULL; | |
360 slpcall->progress_cb = NULL; | |
361 slpcall->cb = NULL; | |
362 | |
363 if (fail_local) | |
364 purple_xfer_cancel_local(xfer); | |
365 else | |
366 purple_xfer_cancel_remote(xfer); | |
367 break; | |
368 | |
369 default: | |
370 slpcall->session_init_cb = NULL; | |
371 slpcall->end_cb = NULL; | |
372 slpcall->progress_cb = NULL; | |
373 slpcall->cb = NULL; | |
374 | |
375 if (fail_local) | |
376 purple_xfer_cancel_local(xfer); | |
377 else | |
378 purple_xfer_cancel_remote(xfer); | |
379 | |
380 break; | |
381 } | |
382 */ | |
383 } | |
384 | |
385 static void | |
386 msn_dc_parse_binary_header(MsnDirectConn *dc) | |
387 { | |
388 MsnSlpHeader *h; | |
389 gchar *buffer; | |
390 | |
391 g_return_if_fail(dc != NULL); | |
392 | |
393 h = &dc->header; | |
394 /* Skip packet size */ | |
395 buffer = dc->in_buffer + 4; | |
396 | |
397 memcpy(&h->session_id, buffer + DC_SESSION_ID_OFFS, sizeof(h->session_id)); | |
398 h->session_id = GUINT32_FROM_LE(h->session_id); | |
399 | |
400 memcpy(&h->id, buffer + DC_SEQ_ID_OFFS, sizeof(h->id)); | |
401 h->id = GUINT32_FROM_LE(h->id); | |
402 | |
403 memcpy(&h->offset, buffer + DC_DATA_OFFSET_OFFS, sizeof(h->offset)); | |
404 h->offset = GUINT64_FROM_LE(h->offset); | |
405 | |
406 memcpy(&h->total_size, buffer + DC_TOTAL_DATA_SIZE_OFFS, sizeof(h->total_size)); | |
407 h->total_size = GUINT64_FROM_LE(h->total_size); | |
408 | |
409 memcpy(&h->length, buffer + DC_MESSAGE_LENGTH_OFFS, sizeof(h->length)); | |
410 h->length = GUINT32_FROM_LE(h->length); | |
411 | |
412 memcpy(&h->flags, buffer + DC_FLAGS_OFFS, sizeof(h->flags)); | |
413 h->flags = GUINT32_FROM_LE(h->flags); | |
414 | |
415 memcpy(&h->ack_id, buffer + DC_ACK_ID_OFFS, sizeof(h->ack_id)); | |
416 h->ack_id = GUINT32_FROM_LE(h->ack_id); | |
417 | |
418 memcpy(&h->ack_sub_id, buffer + DC_ACK_UID_OFFS, sizeof(h->ack_sub_id)); | |
419 h->ack_sub_id = GUINT32_FROM_LE(h->ack_sub_id); | |
420 | |
421 memcpy(&h->ack_size, buffer + DC_ACK_DATA_SIZE_OFFS, sizeof(h->ack_size)); | |
422 h->ack_size = GUINT64_FROM_LE(h->ack_size); | |
423 } | |
424 | |
425 static gchar* | |
426 msn_dc_serialize_binary_header(MsnDirectConn *dc) { | |
427 static MsnSlpHeader h; | |
428 static gchar bin_header[DC_PACKET_HEADER_SIZE]; | |
429 | |
430 g_return_val_if_fail(dc != NULL, NULL); | |
431 | |
432 memcpy(&h, &dc->header, sizeof(h)); | |
433 | |
434 h.session_id = GUINT32_TO_LE(h.session_id); | |
435 memcpy(bin_header + DC_SESSION_ID_OFFS, &h.session_id, sizeof(h.session_id)); | |
436 | |
437 h.id = GUINT32_TO_LE(h.id); | |
438 memcpy(bin_header + DC_SEQ_ID_OFFS, &h.id, sizeof(h.id)); | |
439 | |
440 h.offset = GUINT64_TO_LE(h.offset); | |
441 memcpy(bin_header + DC_DATA_OFFSET_OFFS, &h.offset, sizeof(h.offset)); | |
442 | |
443 h.total_size = GUINT64_TO_LE(h.total_size); | |
444 memcpy(bin_header + DC_TOTAL_DATA_SIZE_OFFS, &h.total_size, sizeof(h.total_size)); | |
445 | |
446 h.length = GUINT32_TO_LE(h.length); | |
447 memcpy(bin_header + DC_MESSAGE_LENGTH_OFFS, &h.length, sizeof(h.length)); | |
448 | |
449 h.flags = GUINT32_TO_LE(h.flags); | |
450 memcpy(bin_header + DC_FLAGS_OFFS, &h.flags, sizeof(h.flags)); | |
451 | |
452 h.ack_id = GUINT32_TO_LE(h.ack_id); | |
453 memcpy(bin_header + DC_ACK_ID_OFFS, &h.ack_id, sizeof(h.ack_id)); | |
454 | |
455 h.ack_sub_id = GUINT32_TO_LE(h.ack_sub_id); | |
456 memcpy(bin_header + DC_ACK_UID_OFFS, &h.ack_sub_id, sizeof(h.ack_sub_id)); | |
457 | |
458 h.ack_size = GUINT64_TO_LE(h.ack_size); | |
459 memcpy(bin_header + DC_ACK_DATA_SIZE_OFFS, &h.ack_size, sizeof(h.ack_size)); | |
460 | |
461 return bin_header; | |
462 } | |
463 | |
464 /* | |
465 static void | |
466 msn_dc_send_bye(MsnDirectConn *dc) | |
467 { | |
468 MsnSlpLink *slplink; | |
469 PurpleAccount *account; | |
470 char *body; | |
471 int body_len; | |
472 | |
473 purple_debug_info("msn", "msn_dc_send_bye\n"); | |
474 | |
475 g_return_if_fail(dc != NULL); | |
476 g_return_if_fail(dc->slpcall != NULL); | |
477 | |
478 slplink = dc->slpcall->slplink; | |
479 account = slplink->session->account; | |
480 | |
481 dc->header.session_id = 0; | |
482 dc->header.id = dc->slpcall->slplink->slp_seq_id++; | |
483 dc->header.offset = 0; | |
484 | |
485 body = g_strdup_printf( | |
486 "BYE MSNMSGR:%s MSNSLP/1.0\r\n" | |
487 "To: <msnmsgr:%s>\r\n" | |
488 "From: <msnmsgr:%s>\r\n" | |
489 "Via: MSNSLP/1.0/TLP ;branch={%s}\r\n" | |
490 "CSeq: 0\r\n" | |
491 "Call-ID: {%s}\r\n" | |
492 "Max-Forwards: 0\r\n" | |
493 "Content-Type: application/x-msnmsgr-sessionclosebody\r\n" | |
494 "Content-Length: 3\r\n" | |
495 "\r\n\r\n", | |
496 | |
497 slplink->remote_user, | |
498 slplink->remote_user, | |
499 purple_account_get_username(account), | |
500 dc->slpcall->branch, | |
501 dc->slpcall->id | |
502 ); | |
503 body_len = strlen(body) + 1; | |
504 memcpy(dc->buffer, body, body_len); | |
505 g_free(body); | |
506 | |
507 dc->header.total_size = body_len; | |
508 dc->header.length = body_len; | |
509 dc->header.flags = 0; | |
510 dc->header.ack_sub_id = 0; | |
511 dc->header.ack_size = 0; | |
512 | |
513 msn_dc_send_packet(dc); | |
514 } | |
515 | |
516 static void | |
517 msn_dc_send_ack(MsnDirectConn *dc) | |
518 { | |
519 g_return_if_fail(dc != NULL); | |
520 | |
521 dc->header.session_id = 0; | |
522 dc->header.ack_sub_id = dc->header.ack_id; | |
523 dc->header.ack_id = dc->header.id; | |
524 dc->header.id = dc->slpcall->slplink->slp_seq_id++; | |
525 dc->header.offset = 0; | |
526 dc->header.length = 0; | |
527 dc->header.flags = 0x02; | |
528 dc->header.ack_size = dc->header.total_size; | |
529 | |
530 msn_dc_send_packet(dc); | |
531 } | |
532 | |
533 static void | |
534 msn_dc_send_data_ack(MsnDirectConn *dc) | |
535 { | |
536 g_return_if_fail(dc != NULL); | |
537 | |
538 dc->header.session_id = dc->slpcall->session_id; | |
539 dc->header.ack_sub_id = dc->header.ack_id; | |
540 dc->header.ack_id = dc->header.id; | |
541 dc->header.id = dc->slpcall->slplink->slp_seq_id++; | |
542 dc->header.offset = 0; | |
543 dc->header.length = 0; | |
544 dc->header.flags = 0x02; | |
545 dc->header.ack_size = dc->header.total_size; | |
546 | |
547 msn_dc_send_packet(dc); | |
548 } | |
549 | |
550 static void | |
551 msn_dc_xfer_send_cancel(PurpleXfer *xfer) | |
552 { | |
553 MsnSlpCall *slpcall; | |
554 MsnDirectConn *dc; | |
555 | |
556 purple_debug_info("msn", "msn_dc_xfer_send_cancel\n"); | |
557 | |
558 g_return_if_fail(xfer != NULL); | |
559 | |
560 slpcall = xfer->data; | |
561 g_return_if_fail(slpcall != NULL); | |
562 | |
563 dc = slpcall->dc; | |
564 g_return_if_fail(dc != NULL); | |
565 | |
566 switch (dc->state) { | |
567 case DC_STATE_TRANSFER: | |
568 msn_dc_send_bye(dc); | |
569 dc->state = DC_STATE_CANCELLED; | |
570 break; | |
571 | |
572 default: | |
573 msn_dc_destroy(dc); | |
574 break; | |
575 } | |
576 } | |
577 | |
578 static void | |
579 msn_dc_xfer_recv_cancel(PurpleXfer *xfer) | |
580 { | |
581 MsnSlpCall *slpcall; | |
582 MsnDirectConn *dc; | |
583 | |
584 purple_debug_info("msn", "msn_dc_xfer_recv_cancel\n"); | |
585 | |
586 g_return_if_fail(xfer != NULL); | |
587 | |
588 slpcall = xfer->data; | |
589 g_return_if_fail(slpcall != NULL); | |
590 | |
591 dc = slpcall->dc; | |
592 g_return_if_fail(dc != NULL); | |
593 | |
594 switch (dc->state) { | |
595 case DC_STATE_TRANSFER: | |
596 msn_dc_send_bye(dc); | |
597 dc->state = DC_STATE_CANCELLED; | |
598 break; | |
599 | |
600 default: | |
601 msn_dc_destroy(dc); | |
602 break; | |
603 } | |
604 } | |
605 */ | |
606 | |
607 static void | |
608 msn_dc_send_cb(gpointer data, gint fd, PurpleInputCondition cond) | |
609 { | |
610 MsnDirectConn *dc = data; | |
611 MsnDirectConnPacket *p; | |
612 int bytes_to_send; | |
613 int bytes_sent; | |
614 | |
615 g_return_if_fail(dc != NULL); | |
616 g_return_if_fail(fd != -1); | |
617 | |
618 if(g_queue_is_empty(dc->out_queue)) { | |
619 if (dc->send_handle != 0) { | |
620 purple_input_remove(dc->send_handle); | |
621 dc->send_handle = 0; | |
622 } | |
623 return; | |
624 } | |
625 | |
626 p = g_queue_peek_head(dc->out_queue); | |
627 bytes_to_send = p->length - dc->msg_pos; | |
628 | |
629 bytes_sent = send(fd, p->data, bytes_to_send, 0); | |
630 if (bytes_sent < 0) { | |
631 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) | |
632 return; | |
633 | |
634 purple_debug_warning("msn", "msn_dc_send_cb: send error\n"); | |
635 msn_dc_destroy(dc); | |
636 return; | |
637 } | |
638 | |
639 dc->progress = TRUE; | |
640 | |
641 dc->msg_pos += bytes_sent; | |
642 if (dc->msg_pos == p->length) { | |
643 if (p->sent_cb != NULL) | |
644 p->sent_cb(p); | |
645 | |
646 g_queue_pop_head(dc->out_queue); | |
647 msn_dc_destroy_packet(p); | |
648 | |
649 dc->msg_pos = 0; | |
650 } | |
651 } | |
652 | |
653 static void | |
654 msn_dc_enqueue_packet(MsnDirectConn *dc, MsnDirectConnPacket *p) | |
655 { | |
656 gboolean was_empty; | |
657 | |
658 was_empty = g_queue_is_empty(dc->out_queue); | |
659 g_queue_push_tail(dc->out_queue, p); | |
660 | |
661 if (was_empty && dc->send_handle == 0) { | |
662 dc->send_handle = purple_input_add(dc->fd, PURPLE_INPUT_WRITE, msn_dc_send_cb, dc); | |
663 msn_dc_send_cb(dc, dc->fd, PURPLE_INPUT_WRITE); | |
664 } | |
665 } | |
666 | |
667 static void | |
668 msn_dc_send_foo(MsnDirectConn *dc) | |
669 { | |
670 MsnDirectConnPacket *p; | |
671 | |
672 purple_debug_info("msn", "msn_dc_send_foo\n"); | |
673 | |
674 g_return_if_fail(dc != NULL); | |
675 | |
676 p = msn_dc_new_packet(); | |
677 | |
678 p->length = 8; | |
679 p->data = (guchar*)g_strdup("\4\0\0\0foo"); | |
680 p->sent_cb = NULL; | |
681 | |
682 msn_dc_enqueue_packet(dc, p); | |
683 } | |
684 | |
685 static void | |
686 msn_dc_send_handshake(MsnDirectConn *dc) | |
687 { | |
688 MsnDirectConnPacket *p; | |
689 gchar *h; | |
690 guint32 l; | |
691 | |
692 g_return_if_fail(dc != NULL); | |
693 | |
694 p = msn_dc_new_packet(); | |
695 | |
696 p->length = 4 + DC_PACKET_HEADER_SIZE; | |
697 p->data = g_malloc(p->length); | |
698 | |
699 l = DC_PACKET_HEADER_SIZE; | |
700 l = GUINT32_TO_LE(l); | |
701 memcpy(p->data, &l, 4); | |
702 | |
703 dc->header.session_id = 0; | |
704 dc->header.id = dc->slpcall->slplink->slp_seq_id++; | |
705 dc->header.offset = 0; | |
706 dc->header.total_size = 0; | |
707 dc->header.length = 0; | |
708 dc->header.flags = 0x100; | |
709 | |
710 h = msn_dc_serialize_binary_header(dc); | |
711 memcpy(p->data + 4, h, DC_PACKET_HEADER_SIZE); | |
712 memcpy(p->data + 4 + DC_ACK_ID_OFFS, dc->nonce, 16); | |
713 | |
714 msn_dc_enqueue_packet(dc, p); | |
715 } | |
716 | |
717 static void | |
718 msn_dc_send_handshake_reply(MsnDirectConn *dc) | |
719 { | |
720 MsnDirectConnPacket *p; | |
721 gchar *h; | |
722 guint32 l; | |
723 | |
724 g_return_if_fail(dc != NULL); | |
725 | |
726 p = msn_dc_new_packet(); | |
727 | |
728 p->length = 4 + DC_PACKET_HEADER_SIZE; | |
729 p->data = g_malloc(p->length); | |
730 | |
731 l = DC_PACKET_HEADER_SIZE; | |
732 l = GUINT32_TO_LE(l); | |
733 memcpy(p->data, &l, 4); | |
734 | |
735 dc->header.id = dc->slpcall->slplink->slp_seq_id++; | |
736 dc->header.length = 0; | |
737 | |
738 h = msn_dc_serialize_binary_header(dc); | |
739 memcpy(p->data + 4, h, DC_PACKET_HEADER_SIZE); | |
740 memcpy(p->data + 4 + DC_ACK_ID_OFFS, dc->nonce, 16); | |
741 | |
742 msn_dc_enqueue_packet(dc, p); | |
743 } | |
744 | |
745 static void | |
746 msn_dc_send_packet_cb(MsnDirectConnPacket *p) | |
747 { | |
748 if (p->msg != NULL && p->msg->ack_cb != NULL) | |
749 p->msg->ack_cb(p->msg, p->msg->ack_data); | |
750 } | |
751 | |
752 void | |
753 msn_dc_enqueue_msg(MsnDirectConn *dc, MsnMessage *msg) | |
754 { | |
755 MsnDirectConnPacket *p = msn_dc_new_packet(); | |
756 guint32 length = msg->body_len + DC_PACKET_HEADER_SIZE; | |
757 | |
758 p->length = 4 + length; | |
759 p->data = g_malloc(p->length); | |
760 | |
761 length = GUINT32_TO_LE(length); | |
762 memcpy(p->data, &length, 4); | |
763 memcpy(p->data + 4, &msg->msnslp_header, DC_PACKET_HEADER_SIZE); | |
764 memcpy(p->data + 4 + DC_PACKET_HEADER_SIZE, msg->body, msg->body_len); | |
765 | |
766 p->sent_cb = msn_dc_send_packet_cb; | |
767 p->msg = msg; | |
768 msn_message_ref(msg); | |
769 | |
770 msn_dc_enqueue_packet(dc, p); | |
771 } | |
78 | 772 |
79 static int | 773 static int |
80 create_listener(int port) | 774 msn_dc_process_packet(MsnDirectConn *dc, guint32 packet_length) |
81 { | 775 { |
82 int fd; | 776 g_return_val_if_fail(dc != NULL, DC_PROCESS_ERROR); |
83 int flags; | 777 |
84 const int on = 1; | 778 switch (dc->state) { |
85 | 779 case DC_STATE_CLOSED: |
780 break; | |
781 | |
782 case DC_STATE_FOO: { | |
783 /* FOO message is always 4 bytes long */ | |
784 if (packet_length != 4 || memcmp(dc->in_buffer, "\4\0\0\0foo", 8) != 0) | |
785 return DC_PROCESS_FALLBACK; | |
786 | |
787 dc->state = DC_STATE_HANDSHAKE; | |
788 break; | |
789 } | |
790 | |
791 case DC_STATE_HANDSHAKE: { | |
792 if (packet_length != DC_PACKET_HEADER_SIZE) | |
793 return DC_PROCESS_FALLBACK; | |
794 | |
795 /* TODO: Check! */ | |
796 msn_dc_send_handshake_reply(dc); | |
797 dc->state = DC_STATE_ESTABILISHED; | |
798 | |
799 msn_slpcall_session_init(dc->slpcall); | |
800 dc->slpcall = NULL; | |
801 break; | |
802 } | |
803 | |
804 case DC_STATE_HANDSHAKE_REPLY: | |
805 /* TODO: Check! */ | |
806 dc->state = DC_STATE_ESTABILISHED; | |
807 | |
808 msn_slpcall_session_init(dc->slpcall); | |
809 dc->slpcall = NULL; | |
810 break; | |
811 | |
812 case DC_STATE_ESTABILISHED: | |
813 msn_slplink_process_msg( | |
814 dc->slplink, | |
815 &dc->header, | |
816 dc->in_buffer + 4 + DC_PACKET_HEADER_SIZE, | |
817 dc->header.length | |
818 ); | |
819 | |
820 /* | |
821 if (dc->num_calls == 0) { | |
822 msn_dc_destroy(dc); | |
823 | |
824 return DC_PROCESS_CLOSE; | |
825 } | |
826 */ | |
827 break; | |
86 #if 0 | 828 #if 0 |
87 struct addrinfo hints; | 829 { |
88 struct addrinfo *c, *res; | 830 guint64 file_size; |
89 char port_str[5]; | 831 int bytes_written; |
90 | 832 PurpleXfer *xfer; |
91 snprintf(port_str, sizeof(port_str), "%d", port); | 833 MsnSlpHeader *h = &dc->header; |
92 | 834 |
93 memset(&hints, 0, sizeof(hints)); | 835 if (packet_length < DC_PACKET_HEADER_SIZE) |
94 | 836 return DC_TRANSFER_FALLBACK; |
95 hints.ai_flags = AI_PASSIVE; | 837 |
96 hints.ai_family = AF_UNSPEC; | 838 /* |
97 hints.ai_socktype = SOCK_STREAM; | 839 * TODO: MSN Messenger 7.0 sends BYE with flags 0x0000000 so we'll get rid of |
98 | 840 * 0x1000000 bit but file data is always sent with flags 0x1000030 in both |
99 if (getaddrinfo(NULL, port_str, &hints, &res) != 0) | 841 * MSN Messenger and Live.*/ |
100 { | 842 switch (h->flags) { |
101 purple_debug_error("msn", "Could not get address info: %s.\n", | 843 case 0x0000000: |
102 port_str); | 844 case 0x1000000: |
103 return -1; | 845 msn_dc_send_ack(dc); |
104 } | 846 if (strncmp(dc->buffer, "BYE", 3) == 0) { |
105 | 847 /* Remote side cancelled the transfer. */ |
106 for (c = res; c != NULL; c = c->ai_next) | 848 purple_xfer_cancel_remote(dc->slpcall->xfer); |
107 { | 849 return DC_TRANSFER_CANCELLED; |
108 fd = socket(c->ai_family, c->ai_socktype, c->ai_protocol); | 850 } |
109 | |
110 if (fd < 0) | |
111 continue; | |
112 | |
113 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); | |
114 | |
115 if (bind(fd, c->ai_addr, c->ai_addrlen) == 0) | |
116 break; | 851 break; |
117 | 852 |
118 close(fd); | 853 case 0x1000030: |
119 } | 854 /* File data */ |
120 | 855 xfer = dc->slpcall->xfer; |
121 if (c == NULL) | 856 file_size = purple_xfer_get_size(xfer); |
122 { | 857 |
123 purple_debug_error("msn", "Could not find socket: %s.\n", port_str); | 858 /* Packet sanity checks */ |
124 return -1; | 859 if ( h->session_id != dc->slpcall->session_id || |
125 } | 860 h->offset >= file_size || |
126 | 861 h->total_size != file_size || |
127 freeaddrinfo(res); | 862 h->length != packet_length - DC_PACKET_HEADER_SIZE || |
128 #else | 863 h->offset + h->length > file_size) { |
129 struct sockaddr_in sockin; | 864 |
130 | 865 purple_debug_warning("msn", "msn_dc_recv_process_packet_cb: packet range check error!\n"); |
131 fd = socket(AF_INET, SOCK_STREAM, 0); | 866 purple_xfer_cancel_local(dc->slpcall->xfer); |
132 | 867 return DC_TRANSFER_CANCELLED; |
133 if (fd < 0) | 868 } |
134 return -1; | 869 |
135 | 870 bytes_written = fwrite(dc->buffer, 1, h->length, xfer->dest_fp); |
136 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) | 871 if (bytes_written != h->length) { |
137 { | 872 purple_debug_warning("msn", "msn_dc_recv_process_packet_cb: cannot write whole packet to file!\n"); |
138 close(fd); | 873 purple_xfer_cancel_local(dc->slpcall->xfer); |
139 return -1; | 874 return DC_TRANSFER_CANCELLED; |
140 } | 875 } |
141 | 876 |
142 memset(&sockin, 0, sizeof(struct sockaddr_in)); | 877 xfer->bytes_sent = (h->offset + h->length); |
143 sockin.sin_family = AF_INET; | 878 xfer->bytes_remaining = h->total_size - xfer->bytes_sent; |
144 sockin.sin_port = htons(port); | 879 |
145 | 880 purple_xfer_update_progress(xfer); |
146 if (bind(fd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) | 881 |
147 { | 882 if (xfer->bytes_remaining == 0) { |
148 close(fd); | 883 /* ACK only the last data packet */ |
149 return -1; | 884 msn_dc_send_data_ack(dc); |
150 } | 885 purple_xfer_set_completed(xfer, TRUE); |
886 dc->state = DC_STATE_BYE; | |
887 } | |
888 break; | |
889 default: | |
890 /* | |
891 * TODO: Packet with unknown flags. Should we ACK these? | |
892 */ | |
893 msn_dc_send_ack(dc); | |
894 | |
895 purple_debug_warning( | |
896 "msn", | |
897 "msn_dc_recv_process_packet_cb: received packet with unknown flags: 0x%08x\n", | |
898 dc->header.flags | |
899 ); | |
900 } | |
901 break; | |
902 } | |
903 | |
904 case DC_STATE_BYE: | |
905 /* TODO: Check! */ | |
906 switch (dc->header.flags) { | |
907 case 0x0000000: | |
908 case 0x1000000: | |
909 msn_dc_send_ack(dc); | |
910 if (strncmp(dc->buffer, "BYE", 3) == 0) { | |
911 dc->state = DC_STATE_COMPLETED; | |
912 return DC_TRANSFER_COMPLETED; | |
913 } | |
914 break; | |
915 | |
916 default: | |
917 /* | |
918 * TODO: Packet with unknown flags. Should we ACK these? | |
919 */ | |
920 msn_dc_send_ack(dc); | |
921 purple_debug_warning( | |
922 "msn", | |
923 "msn_dc_recv_process_packet_cb: received packet with unknown flags: 0x%08x\n", | |
924 dc->header.flags | |
925 ); | |
926 } | |
927 break; | |
151 #endif | 928 #endif |
152 | 929 } |
153 if (listen (fd, 4) != 0) | 930 |
154 { | 931 return DC_PROCESS_OK; |
155 close (fd); | 932 } |
156 return -1; | 933 |
157 } | 934 static void |
158 | 935 msn_dc_recv_cb(gpointer data, gint fd, PurpleInputCondition cond) |
159 flags = fcntl(fd, F_GETFL); | 936 { |
160 fcntl(fd, F_SETFL, flags | O_NONBLOCK); | 937 MsnDirectConn *dc; |
161 #ifndef _WIN32 | 938 int free_buf_space; |
162 fcntl(fd, F_SETFD, FD_CLOEXEC); | 939 int bytes_received; |
940 guint32 packet_length; | |
941 | |
942 g_return_if_fail(data != NULL); | |
943 g_return_if_fail(fd != -1); | |
944 | |
945 dc = data; | |
946 free_buf_space = dc->in_size - dc->in_pos; | |
947 | |
948 bytes_received = recv(fd, dc->in_buffer + dc->in_pos, free_buf_space, 0); | |
949 if (bytes_received < 0) { | |
950 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) | |
951 return; | |
952 | |
953 purple_debug_warning("msn", "msn_dc_recv_cb: recv error\n"); | |
954 | |
955 if(dc->state != DC_STATE_ESTABILISHED) | |
956 msn_dc_fallback_to_p2p(dc); | |
957 else | |
958 msn_dc_destroy(dc); | |
959 return; | |
960 | |
961 } else if (bytes_received == 0) { | |
962 /* EOF. Remote side closed connection. */ | |
963 purple_debug_info("msn", "msn_dc_recv_cb: recv EOF\n"); | |
964 | |
965 if(dc->state != DC_STATE_ESTABILISHED) | |
966 msn_dc_fallback_to_p2p(dc); | |
967 else | |
968 msn_dc_destroy(dc); | |
969 return; | |
970 } | |
971 | |
972 dc->progress = TRUE; | |
973 | |
974 dc->in_pos += bytes_received; | |
975 | |
976 /* Wait for packet length */ | |
977 while (dc->in_pos >= 4) { | |
978 packet_length = *((guint32*)dc->in_buffer); | |
979 packet_length = GUINT32_FROM_LE(packet_length); | |
980 | |
981 if (packet_length > DC_MAX_PACKET_SIZE) { | |
982 /* Oversized packet */ | |
983 purple_debug_warning("msn", "msn_dc_recv_cb: oversized packet received\n"); | |
984 return; | |
985 } | |
986 | |
987 /* Wait for the whole packet to arrive */ | |
988 if (dc->in_pos < 4 + packet_length) | |
989 return; | |
990 | |
991 if (dc->state != DC_STATE_FOO) { | |
992 msn_dc_parse_binary_header(dc); | |
993 } | |
994 | |
995 switch (msn_dc_process_packet(dc, packet_length)) { | |
996 case DC_PROCESS_CLOSE: | |
997 return; | |
998 | |
999 case DC_PROCESS_FALLBACK: | |
1000 purple_debug_warning("msn", "msn_dc_recv_cb: packet processing error, fall back to p2p\n"); | |
1001 msn_dc_fallback_to_p2p(dc); | |
1002 return; | |
1003 | |
1004 } | |
1005 | |
1006 if (dc->in_pos > packet_length + 4) { | |
1007 memcpy(dc->in_buffer, dc->in_buffer + 4 + packet_length, dc->in_pos - packet_length - 4); | |
1008 } | |
1009 | |
1010 dc->in_pos -= packet_length + 4; | |
1011 } | |
1012 } | |
1013 | |
1014 #if 0 | |
1015 static gboolean | |
1016 msn_dc_send_next_packet(MsnDirectConn *dc) | |
1017 { | |
1018 MsnSlpMessage *msg; | |
1019 | |
1020 if(g_queue_is_empty(dc->out_queue)) | |
1021 return TRUE; | |
1022 | |
1023 msg = g_queue_peek_head(dc->out_queue); | |
1024 msn_slplink_send_msgpart(dc->slplink, msg); | |
1025 | |
1026 | |
1027 | |
1028 PurpleXfer *xfer; | |
1029 int bytes_read; | |
1030 | |
1031 g_return_val_if_fail(dc != NULL, FALSE); | |
1032 g_return_val_if_fail(dc->slpcall != NULL, FALSE); | |
1033 | |
1034 xfer = dc->slpcall->xfer; | |
1035 | |
1036 bytes_read = fread(dc->buffer, 1, DC_MAX_BODY_SIZE, xfer->dest_fp); | |
1037 | |
1038 if (bytes_read > 0) { | |
1039 dc->header.session_id = dc->slpcall->session_id; | |
1040 /* Only increment seq. ID before sending BYE */ | |
1041 dc->header.id = dc->slpcall->slplink->slp_seq_id; | |
1042 dc->header.offset = xfer->bytes_sent; | |
1043 dc->header.total_size = xfer->size; | |
1044 dc->header.length = bytes_read; | |
1045 dc->header.flags = 0x1000030; | |
1046 dc->header.ack_id = rand() % G_MAXUINT32; | |
1047 dc->header.ack_sub_id = 0; | |
1048 dc->header.ack_size = 0; | |
1049 | |
1050 msn_dc_send_packet(dc); | |
1051 | |
1052 xfer->bytes_sent += bytes_read; | |
1053 xfer->bytes_remaining -= bytes_read; | |
1054 purple_xfer_update_progress(xfer); | |
1055 | |
1056 if (xfer->bytes_remaining == 0) { | |
1057 purple_xfer_set_completed(xfer, TRUE); | |
1058 | |
1059 /* Increment seq. ID for the next BYE message */ | |
1060 dc->slpcall->slplink->slp_seq_id++; | |
1061 dc->state = DC_STATE_DATA_ACK; | |
1062 } | |
1063 | |
1064 } else { | |
1065 /* File read error */ | |
1066 purple_xfer_cancel_local(xfer); | |
1067 return FALSE; | |
1068 } | |
1069 | |
1070 return TRUE; | |
1071 } | |
1072 | |
1073 static int | |
1074 msn_dc_send_process_packet_cb(MsnDirectConn *dc, guint32 packet_length) | |
1075 { | |
1076 g_return_val_if_fail(dc != NULL, DC_TRANSFER_CANCELLED); | |
1077 | |
1078 switch (dc->state) { | |
1079 case DC_STATE_FOO: { | |
1080 if (packet_length != 4) | |
1081 return DC_TRANSFER_FALLBACK; | |
1082 | |
1083 if (memcmp(dc->in_buffer, "\4\0\0\0foo", 8) != 0) | |
1084 return DC_TRANSFER_FALLBACK; | |
1085 | |
1086 dc->state = DC_STATE_HANDSHAKE; | |
1087 break; | |
1088 } | |
1089 | |
1090 case DC_STATE_HANDSHAKE: { | |
1091 if (packet_length != DC_PACKET_HEADER_SIZE) | |
1092 return DC_TRANSFER_FALLBACK; | |
1093 | |
1094 /* TODO: Check! */ | |
1095 msn_dc_send_handshake_reply(dc); | |
1096 dc->state = DC_STATE_TRANSFER; | |
1097 | |
1098 purple_xfer_set_request_denied_fnc(dc->slpcall->xfer, msn_dc_xfer_send_cancel); | |
1099 purple_xfer_set_cancel_send_fnc(dc->slpcall->xfer, msn_dc_xfer_send_cancel); | |
1100 purple_xfer_set_end_fnc(dc->slpcall->xfer, msn_dc_xfer_end); | |
1101 purple_xfer_start(dc->slpcall->xfer, -1, NULL, 0); | |
1102 break; | |
1103 } | |
1104 | |
1105 case DC_STATE_HANDSHAKE_REPLY: | |
1106 /* TODO: Check! */ | |
1107 dc->state = DC_STATE_TRANSFER; | |
1108 break; | |
1109 | |
1110 case DC_STATE_TRANSFER: { | |
1111 switch (dc->header.flags) { | |
1112 case 0x0000000: | |
1113 case 0x1000000: | |
1114 msn_dc_send_ack(dc); | |
1115 if (strncmp(dc->buffer, "BYE", 3) == 0) { | |
1116 /* Remote side cancelled the transfer. */ | |
1117 purple_xfer_cancel_remote(dc->slpcall->xfer); | |
1118 return DC_TRANSFER_CANCELLED; | |
1119 } | |
1120 break; | |
1121 } | |
1122 break; | |
1123 } | |
1124 | |
1125 case DC_STATE_DATA_ACK: { | |
1126 /* TODO: Check! */ | |
1127 msn_dc_send_bye(dc); | |
1128 dc->state = DC_STATE_BYE_ACK; | |
1129 break; | |
1130 } | |
1131 | |
1132 case DC_STATE_BYE_ACK: | |
1133 /* TODO: Check! */ | |
1134 dc->state = DC_STATE_COMPLETED; | |
1135 return DC_TRANSFER_COMPLETED; | |
1136 } | |
1137 | |
1138 return DC_TRANSFER_OK; | |
1139 } | |
163 #endif | 1140 #endif |
164 | 1141 |
165 return fd; | 1142 static gboolean |
166 } | 1143 msn_dc_timeout(gpointer data) |
167 | 1144 { |
168 static gssize | 1145 MsnDirectConn *dc = data; |
169 msn_directconn_write(MsnDirectConn *directconn, | 1146 |
170 const char *data, size_t len) | 1147 g_return_val_if_fail(dc != NULL, FALSE); |
171 { | 1148 |
172 char *buffer, *tmp; | 1149 if (dc->progress) |
173 size_t buf_size; | 1150 dc->progress = FALSE; |
174 gssize ret; | 1151 else |
175 guint32 sent_len; | 1152 msn_dc_destroy(dc); |
176 | 1153 |
177 g_return_val_if_fail(directconn != NULL, 0); | 1154 return TRUE; |
178 | 1155 } |
179 buf_size = len + 4; | 1156 |
180 buffer = tmp = g_malloc(buf_size); | 1157 static void |
181 | 1158 msn_dc_init(MsnDirectConn *dc) |
182 sent_len = GUINT32_TO_LE(len); | 1159 { |
183 | 1160 g_return_if_fail(dc != NULL); |
184 memcpy(tmp, &sent_len, 4); | 1161 |
185 tmp += 4; | 1162 dc->in_size = DC_MAX_PACKET_SIZE + 4; |
186 memcpy(tmp, data, len); | 1163 dc->in_pos = 0; |
187 tmp += len; | 1164 dc->in_buffer = g_malloc(dc->in_size); |
188 | 1165 |
189 ret = write(directconn->fd, buffer, buf_size); | 1166 dc->recv_handle = purple_input_add(dc->fd, PURPLE_INPUT_READ, msn_dc_recv_cb, dc); |
190 | 1167 dc->send_handle = purple_input_add(dc->fd, PURPLE_INPUT_WRITE, msn_dc_send_cb, dc); |
191 #ifdef DEBUG_DC | 1168 |
192 char *str; | 1169 dc->timeout_handle = purple_timeout_add_seconds(DC_TIMEOUT, msn_dc_timeout, dc); |
193 str = g_strdup_printf("%s/msntest/w%.4d.bin", g_get_home_dir(), directconn->c); | 1170 } |
194 | 1171 |
195 FILE *tf = g_fopen(str, "w"); | |
196 fwrite(buffer, 1, buf_size, tf); | |
197 fclose(tf); | |
198 | |
199 g_free(str); | |
200 #endif | |
201 | |
202 g_free(buffer); | |
203 | |
204 directconn->c++; | |
205 | |
206 return ret; | |
207 } | |
208 | |
209 #if 0 | |
210 void | 1172 void |
211 msn_directconn_parse_nonce(MsnDirectConn *directconn, const char *nonce) | 1173 msn_dc_connected_to_peer_cb(gpointer data, gint fd, const gchar *error_msg) |
212 { | 1174 { |
213 guint32 t1; | 1175 MsnDirectConn *dc; |
214 guint16 t2; | 1176 |
215 guint16 t3; | 1177 purple_debug_info("msn", "msn_dc_connected_to_peer_cb\n"); |
216 guint16 t4; | 1178 |
217 guint64 t5; | 1179 g_return_if_fail(data != NULL); |
218 | 1180 |
219 g_return_if_fail(directconn != NULL); | 1181 dc = data; |
220 g_return_if_fail(nonce != NULL); | 1182 |
221 | 1183 dc->connect_data = NULL; |
222 sscanf (nonce, "%08X-%04hX-%04hX-%04hX-%012llX", &t1, &t2, &t3, &t4, &t5); | 1184 purple_timeout_remove(dc->connect_timeout_handle); |
223 | 1185 dc->connect_timeout_handle = 0; |
224 t1 = GUINT32_TO_LE(t1); | 1186 |
225 t2 = GUINT16_TO_LE(t2); | 1187 dc->fd = fd; |
226 t3 = GUINT16_TO_LE(t3); | 1188 if (dc->fd != -1) { |
227 t4 = GUINT16_TO_BE(t4); | 1189 msn_dc_init(dc); |
228 t5 = GUINT64_TO_BE(t5); | 1190 msn_dc_send_foo(dc); |
229 | 1191 msn_dc_send_handshake(dc); |
230 directconn->slpheader = g_new0(MsnSlpHeader, 1); | 1192 dc->state = DC_STATE_HANDSHAKE_REPLY; |
231 | 1193 } |
232 directconn->slpheader->ack_id = t1; | 1194 } |
233 directconn->slpheader->ack_sub_id = t2 | (t3 << 16); | 1195 |
234 directconn->slpheader->ack_size = t4 | t5; | 1196 /* |
235 } | 1197 * This callback will be called when we're the server |
236 #endif | 1198 * and nobody has connected us in DC_CONNECT_TIMEOUT seconds |
1199 */ | |
1200 static gboolean | |
1201 msn_dc_incoming_connection_timeout_cb(gpointer data) { | |
1202 MsnDirectConn *dc = data; | |
1203 MsnSlpCall *slpcall = dc->slpcall; | |
1204 | |
1205 purple_debug_info("msn", "msn_dc_incoming_connection_timeout_cb\n"); | |
1206 | |
1207 dc = data; | |
1208 g_return_val_if_fail(dc != NULL, FALSE); | |
1209 | |
1210 slpcall = dc->slpcall; | |
1211 g_return_val_if_fail(slpcall != NULL, FALSE); | |
1212 | |
1213 if (dc->listen_data != NULL) { | |
1214 purple_network_listen_cancel(dc->listen_data); | |
1215 dc->listen_data = NULL; | |
1216 } | |
1217 | |
1218 if (dc->listenfd_handle != 0) { | |
1219 purple_timeout_remove(dc->listenfd_handle); | |
1220 dc->listenfd_handle = 0; | |
1221 } | |
1222 | |
1223 if (dc->listenfd != -1) { | |
1224 purple_network_remove_port_mapping(dc->listenfd); | |
1225 close(dc->listenfd); | |
1226 dc->listenfd = -1; | |
1227 } | |
1228 | |
1229 msn_dc_destroy(dc); | |
1230 /* Start p2p file transfer */ | |
1231 msn_slpcall_session_init(slpcall); | |
1232 | |
1233 return FALSE; | |
1234 } | |
1235 | |
1236 /* | |
1237 * This callback will be called when we're unable to connect to | |
1238 * the remote host in DC_CONNECT_TIMEOUT seconds. | |
1239 */ | |
1240 gboolean | |
1241 msn_dc_outgoing_connection_timeout_cb(gpointer data) | |
1242 { | |
1243 MsnDirectConn *dc = data; | |
1244 | |
1245 purple_debug_info("msn", "msn_dc_outgoing_connection_timeout_cb\n"); | |
1246 | |
1247 g_return_val_if_fail(dc != NULL, FALSE); | |
1248 | |
1249 if (dc->connect_timeout_handle != 0) { | |
1250 purple_timeout_remove(dc->connect_timeout_handle); | |
1251 dc->connect_timeout_handle = 0; | |
1252 } | |
1253 | |
1254 if (dc->connect_data != NULL) { | |
1255 purple_proxy_connect_cancel(dc->connect_data); | |
1256 dc->connect_data = NULL; | |
1257 } | |
1258 | |
1259 if (dc->ext_ip && dc->ext_port) { | |
1260 /* Try external IP/port if available. */ | |
1261 dc->connect_data = purple_proxy_connect( | |
1262 NULL, | |
1263 dc->slpcall->slplink->session->account, | |
1264 dc->ext_ip, | |
1265 dc->ext_port, | |
1266 msn_dc_connected_to_peer_cb, | |
1267 dc | |
1268 ); | |
1269 | |
1270 g_free(dc->ext_ip); | |
1271 dc->ext_ip = NULL; | |
1272 | |
1273 if (dc->connect_data) { | |
1274 dc->connect_timeout_handle = purple_timeout_add_seconds( | |
1275 DC_CONNECT_TIMEOUT, | |
1276 msn_dc_outgoing_connection_timeout_cb, | |
1277 dc | |
1278 ); | |
1279 } | |
1280 | |
1281 } else { | |
1282 /* | |
1283 * Both internal and external connection attempts are failed. | |
1284 * Fall back to p2p transfer. | |
1285 */ | |
1286 MsnSlpCall *slpcall = dc->slpcall; | |
1287 | |
1288 msn_dc_destroy(dc); | |
1289 /* Start p2p file transfer */ | |
1290 msn_slpcall_session_init(slpcall); | |
1291 } | |
1292 | |
1293 return FALSE; | |
1294 } | |
1295 | |
1296 /* | |
1297 * This callback will be called when we're the server | |
1298 * and somebody has connected to us in DC_CONNECT_TIMEOUT seconds. | |
1299 */ | |
1300 static void | |
1301 msn_dc_incoming_connection_cb(gpointer data, gint listenfd, PurpleInputCondition cond) | |
1302 { | |
1303 MsnDirectConn *dc = data; | |
1304 | |
1305 purple_debug_info("msn", "msn_dc_incoming_connection_cb\n"); | |
1306 | |
1307 g_return_if_fail(dc != NULL); | |
1308 | |
1309 if (dc->connect_timeout_handle != 0) { | |
1310 purple_timeout_remove(dc->connect_timeout_handle); | |
1311 dc->connect_timeout_handle = 0; | |
1312 } | |
1313 | |
1314 if (dc->listenfd_handle != 0) { | |
1315 purple_input_remove(dc->listenfd_handle); | |
1316 dc->listenfd_handle = 0; | |
1317 } | |
1318 | |
1319 dc->fd = accept(listenfd, NULL, 0); | |
1320 | |
1321 purple_network_remove_port_mapping(dc->listenfd); | |
1322 close(dc->listenfd); | |
1323 dc->listenfd = -1; | |
1324 | |
1325 if (dc->fd != -1) { | |
1326 msn_dc_init(dc); | |
1327 dc->state = DC_STATE_FOO; | |
1328 } | |
1329 } | |
237 | 1330 |
238 void | 1331 void |
239 msn_directconn_send_msg(MsnDirectConn *directconn, MsnMessage *msg) | 1332 msn_dc_listen_socket_created_cb(int listenfd, gpointer data) |
240 { | 1333 { |
241 char *body; | 1334 MsnDirectConn *dc = data; |
242 size_t body_len; | 1335 |
243 | 1336 purple_debug_info("msn", "msn_dc_listen_socket_created_cb\n"); |
244 body = msn_message_gen_slp_body(msg, &body_len); | 1337 |
245 | 1338 g_return_if_fail(dc != NULL); |
246 msn_directconn_write(directconn, body, body_len); | 1339 |
247 } | 1340 dc->listen_data = NULL; |
248 | 1341 |
249 static void | 1342 if (listenfd != -1) { |
250 read_cb(gpointer data, gint source, PurpleInputCondition cond) | 1343 const char *ext_ip; |
251 { | 1344 const char *int_ip; |
252 MsnDirectConn* directconn; | 1345 int port; |
253 char *body; | 1346 |
254 size_t body_len; | 1347 ext_ip = purple_network_get_my_ip(listenfd); |
255 gssize len; | 1348 int_ip = purple_network_get_local_system_ip(listenfd); |
256 | 1349 port = purple_network_get_port_from_fd(listenfd); |
257 purple_debug_info("msn", "read_cb: %d, %d\n", source, cond); | 1350 |
258 | 1351 dc->listenfd = listenfd; |
259 directconn = data; | 1352 dc->listenfd_handle = purple_input_add( |
260 | 1353 listenfd, |
261 /* Let's read the length of the data. */ | 1354 PURPLE_INPUT_READ, |
262 #error This code is broken. See the note below. | 1355 msn_dc_incoming_connection_cb, |
263 /* | 1356 dc |
264 * TODO: This has problems! First of all, sizeof(body_len) will be | 1357 ); |
265 * different on 32bit systems and on 64bit systems (4 bytes | 1358 dc->connect_timeout_handle = purple_timeout_add_seconds( |
266 * vs. 8 bytes). | 1359 DC_CONNECT_TIMEOUT * 2, /* Internal + external connection attempts */ |
267 * Secondly, we're reading from a TCP stream. There is no | 1360 msn_dc_incoming_connection_timeout_cb, |
268 * guarantee that we have received the number of bytes we're | 1361 dc |
269 * trying to read. We need to read into a buffer. If read | 1362 ); |
270 * returns <0 then we need to check errno. If errno is EAGAIN | 1363 |
271 * then don't destroy anything, just exit and wait for more | 1364 if (strcmp(int_ip, ext_ip) != 0) { |
272 * data. See every other function in libpurple that does this | 1365 dc->msg_body = g_strdup_printf( |
273 * correctly for an example. | 1366 "Bridge: TCPv1\r\n" |
274 */ | 1367 "Listening: true\r\n" |
275 len = read(directconn->fd, &body_len, sizeof(body_len)); | 1368 "Hashed-Nonce: {%s}\r\n" |
276 | 1369 "IPv4External-Addrs: %s\r\n" |
277 if (len <= 0) | 1370 "IPv4External-Port: %d\r\n" |
278 { | 1371 "IPv4Internal-Addrs: %s\r\n" |
279 /* ERROR */ | 1372 "IPv4Internal-Port: %d\r\n" |
280 purple_debug_error("msn", "error reading\n"); | 1373 "\r\n", |
281 | 1374 |
282 if (directconn->inpa) | 1375 dc->nonce_hash, |
283 purple_input_remove(directconn->inpa); | 1376 ext_ip, |
284 | 1377 port, |
285 close(directconn->fd); | 1378 int_ip, |
286 | 1379 port |
287 msn_directconn_destroy(directconn); | 1380 ); |
288 | 1381 |
289 return; | 1382 } else { |
290 } | 1383 dc->msg_body = g_strdup_printf( |
291 | 1384 "Bridge: TCPv1\r\n" |
292 body_len = GUINT32_FROM_LE(body_len); | 1385 "Listening: true\r\n" |
293 | 1386 "Hashed-Nonce: {%s}\r\n" |
294 purple_debug_info("msn", "body_len=%" G_GSIZE_FORMAT "\n", body_len); | 1387 "IPv4External-Addrs: %s\r\n" |
295 | 1388 "IPv4External-Port: %d\r\n" |
296 if (body_len <= 0) | 1389 "\r\n", |
297 { | 1390 |
298 /* ERROR */ | 1391 dc->nonce_hash, |
299 purple_debug_error("msn", "error reading\n"); | 1392 ext_ip, |
300 | 1393 port |
301 if (directconn->inpa) | 1394 ); |
302 purple_input_remove(directconn->inpa); | 1395 } |
303 | 1396 |
304 close(directconn->fd); | 1397 if (dc->slpcall->wait_for_socket) { |
305 | 1398 if (dc->send_connection_info_msg_cb != NULL) |
306 msn_directconn_destroy(directconn); | 1399 dc->send_connection_info_msg_cb(dc); |
307 | 1400 |
308 return; | 1401 dc->slpcall->wait_for_socket = FALSE; |
309 } | 1402 } |
310 | 1403 } |
311 body = g_try_malloc(body_len); | 1404 } |
312 | 1405 |
313 if (body != NULL) | |
314 { | |
315 /* Let's read the data. */ | |
316 len = read(directconn->fd, body, body_len); | |
317 | |
318 purple_debug_info("msn", "len=%" G_GSIZE_FORMAT "\n", len); | |
319 } | |
320 else | |
321 { | |
322 purple_debug_error("msn", "Failed to allocate memory for read\n"); | |
323 len = 0; | |
324 } | |
325 | |
326 if (len > 0) | |
327 { | |
328 MsnMessage *msg; | |
329 | |
330 #ifdef DEBUG_DC | |
331 str = g_strdup_printf("%s/msntest/r%.4d.bin", g_get_home_dir(), directconn->c); | |
332 | |
333 FILE *tf = g_fopen(str, "w"); | |
334 fwrite(body, 1, len, tf); | |
335 fclose(tf); | |
336 | |
337 g_free(str); | |
338 #endif | |
339 | |
340 directconn->c++; | |
341 | |
342 msg = msn_message_new_msnslp(); | |
343 msn_message_parse_slp_body(msg, body, body_len); | |
344 | |
345 purple_debug_info("msn", "directconn: process_msg\n"); | |
346 msn_slplink_process_msg(directconn->slplink, msg); | |
347 } | |
348 else | |
349 { | |
350 /* ERROR */ | |
351 purple_debug_error("msn", "error reading\n"); | |
352 | |
353 if (directconn->inpa) | |
354 purple_input_remove(directconn->inpa); | |
355 | |
356 close(directconn->fd); | |
357 | |
358 msn_directconn_destroy(directconn); | |
359 } | |
360 | |
361 g_free(body); | |
362 } | |
363 | |
364 static void | |
365 connect_cb(gpointer data, gint source, PurpleInputCondition cond) | |
366 { | |
367 MsnDirectConn* directconn; | |
368 int fd; | |
369 | |
370 purple_debug_misc("msn", "directconn: connect_cb: %d, %d.\n", source, cond); | |
371 | |
372 directconn = data; | |
373 directconn->connect_data = NULL; | |
374 | |
375 if (TRUE) | |
376 { | |
377 fd = source; | |
378 } | |
379 else | |
380 { | |
381 struct sockaddr_in client_addr; | |
382 socklen_t client; | |
383 fd = accept (source, (struct sockaddr *)&client_addr, &client); | |
384 } | |
385 | |
386 directconn->fd = fd; | |
387 | |
388 if (fd > 0) | |
389 { | |
390 directconn->inpa = purple_input_add(fd, PURPLE_INPUT_READ, read_cb, | |
391 directconn); | |
392 | |
393 if (TRUE) | |
394 { | |
395 /* Send foo. */ | |
396 msn_directconn_write(directconn, "foo", strlen("foo") + 1); | |
397 | |
398 /* Send Handshake */ | |
399 msn_directconn_send_handshake(directconn); | |
400 } | |
401 else | |
402 { | |
403 } | |
404 } | |
405 else | |
406 { | |
407 /* ERROR */ | |
408 purple_debug_error("msn", "could not add input\n"); | |
409 | |
410 if (directconn->inpa) | |
411 purple_input_remove(directconn->inpa); | |
412 | |
413 close(directconn->fd); | |
414 } | |
415 } | |
416 | |
417 static void | |
418 directconn_connect_cb(gpointer data, gint source, const gchar *error_message) | |
419 { | |
420 if (error_message) | |
421 purple_debug_error("msn", "Error making direct connection: %s\n", error_message); | |
422 | |
423 connect_cb(data, source, PURPLE_INPUT_READ); | |
424 } | |
425 | |
426 gboolean | |
427 msn_directconn_connect(MsnDirectConn *directconn, const char *host, int port) | |
428 { | |
429 MsnSession *session; | |
430 | |
431 g_return_val_if_fail(directconn != NULL, FALSE); | |
432 g_return_val_if_fail(host != NULL, TRUE); | |
433 g_return_val_if_fail(port > 0, FALSE); | |
434 | |
435 session = directconn->slplink->session; | |
436 | |
437 #if 0 | |
438 if (session->http_method) | |
439 { | |
440 servconn->http_data->gateway_host = g_strdup(host); | |
441 } | |
442 #endif | |
443 | |
444 directconn->connect_data = purple_proxy_connect(NULL, session->account, | |
445 host, port, directconn_connect_cb, directconn); | |
446 | |
447 return (directconn->connect_data != NULL); | |
448 } | |
449 | |
450 void | |
451 msn_directconn_listen(MsnDirectConn *directconn) | |
452 { | |
453 int port; | |
454 int fd; | |
455 | |
456 port = 7000; | |
457 | |
458 for (fd = -1; fd < 0;) | |
459 fd = create_listener(++port); | |
460 | |
461 directconn->fd = fd; | |
462 | |
463 directconn->inpa = purple_input_add(fd, PURPLE_INPUT_READ, connect_cb, | |
464 directconn); | |
465 | |
466 directconn->port = port; | |
467 directconn->c = 0; | |
468 } | |
469 | |
470 MsnDirectConn* | |
471 msn_directconn_new(MsnSlpLink *slplink) | |
472 { | |
473 MsnDirectConn *directconn; | |
474 | |
475 directconn = g_new0(MsnDirectConn, 1); | |
476 | |
477 directconn->slplink = slplink; | |
478 | |
479 if (slplink->directconn != NULL) | |
480 purple_debug_info("msn", "got_transresp: LEAK\n"); | |
481 | |
482 slplink->directconn = directconn; | |
483 | |
484 return directconn; | |
485 } | |
486 | |
487 void | |
488 msn_directconn_destroy(MsnDirectConn *directconn) | |
489 { | |
490 if (directconn->connect_data != NULL) | |
491 purple_proxy_connect_cancel(directconn->connect_data); | |
492 | |
493 if (directconn->inpa != 0) | |
494 purple_input_remove(directconn->inpa); | |
495 | |
496 if (directconn->fd >= 0) | |
497 close(directconn->fd); | |
498 | |
499 if (directconn->nonce != NULL) | |
500 g_free(directconn->nonce); | |
501 | |
502 directconn->slplink->directconn = NULL; | |
503 | |
504 g_free(directconn); | |
505 } |