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 }