46 static gconstpointer newfrommessage(JingleContent *cn, GError **err); |
46 static gconstpointer newfrommessage(JingleContent *cn, GError **err); |
47 static JingleHandleStatus handle(JingleAction action, gconstpointer data, |
47 static JingleHandleStatus handle(JingleAction action, gconstpointer data, |
48 LmMessageNode *node, GError **err); |
48 LmMessageNode *node, GError **err); |
49 static void tomessage(gconstpointer data, LmMessageNode *node); |
49 static void tomessage(gconstpointer data, LmMessageNode *node); |
50 static gconstpointer new(void); |
50 static gconstpointer new(void); |
51 // static void _send(session_content *sc, gconstpointer data, gchar *buf, gsize size); |
51 static void _send(session_content *sc, gconstpointer data, gchar *buf, gsize size); |
52 static void init(session_content *sc, gconstpointer data); |
52 static void init(session_content *sc, gconstpointer data); |
53 static void end(session_content *sc, gconstpointer data); |
53 static void end(session_content *sc, gconstpointer data); |
54 static gchar *info(gconstpointer data); |
54 static gchar *info(gconstpointer data); |
55 |
55 |
|
56 static void connect_candidate(JingleS5B *js5b, S5BCandidate *cand); |
|
57 static void connect_next_candidate(JingleS5B *js5b, S5BCandidate *cand); |
56 static void |
58 static void |
57 handle_listener_accept(GObject *_listener, GAsyncResult *res, gpointer userdata); |
59 handle_listener_accept(GObject *_listener, GAsyncResult *res, gpointer data); |
|
60 static void |
|
61 handle_client_connect(GObject *_client, GAsyncResult *res, gpointer data); |
58 static void handle_sock_io(GSocket *sock, GIOCondition cond, gpointer data); |
62 static void handle_sock_io(GSocket *sock, GIOCondition cond, gpointer data); |
59 static GSList *get_all_local_ips(); |
63 static GSList *get_all_local_ips(); |
60 static gchar *gen_random_sid(void); |
64 static gchar *gen_random_sid(void); |
61 static gchar *gen_random_cid(void); |
65 static gchar *gen_random_cid(void); |
62 static void jingle_socks5_init(void); |
66 static void jingle_socks5_init(void); |
318 |
322 |
319 static void init(session_content *sc, gconstpointer data) |
323 static void init(session_content *sc, gconstpointer data) |
320 { |
324 { |
321 JingleS5B *js5b = (JingleS5B *)data; |
325 JingleS5B *js5b = (JingleS5B *)data; |
322 GSocketAddress *saddr; |
326 GSocketAddress *saddr; |
323 //GSource *socksource; |
|
324 guint numlistening = 0; // number of addresses we are listening to |
327 guint numlistening = 0; // number of addresses we are listening to |
325 GSList *entry; |
328 GSList *entry; |
326 GError *err = NULL; |
329 GError *err = NULL; |
|
330 GSocket *sock; |
327 |
331 |
328 // First, we listen on all ips |
332 // First, we listen on all ips |
329 js5b->listener = g_socket_listener_new(); |
333 js5b->listener = g_socket_listener_new(); |
330 for (entry = js5b->ourcandidates; entry; entry = entry->next) { |
334 for (entry = js5b->ourcandidates; entry; entry = entry->next) { |
331 S5BCandidate *cand = (S5BCandidate *)entry->data; |
335 S5BCandidate *cand = (S5BCandidate *)entry->data; |
332 |
336 |
333 cand->sock = g_socket_new(g_inet_address_get_family(cand->host), |
337 sock = g_socket_new(g_inet_address_get_family(cand->host), |
334 G_SOCKET_TYPE_STREAM, |
338 G_SOCKET_TYPE_STREAM, |
335 G_SOCKET_PROTOCOL_TCP, &err); |
339 G_SOCKET_PROTOCOL_TCP, &err); |
336 if (cand->sock == NULL) { |
340 if (sock == NULL) { |
337 scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Error while creating a new socket: %s", |
341 scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Error while creating a new socket: %s", |
338 err->message != NULL ? err->message : "(no message)"); |
342 err->message ? err->message : "(no message)"); |
339 continue; |
343 continue; |
340 } |
344 } |
341 g_socket_set_blocking(cand->sock, FALSE); |
345 g_socket_set_blocking(sock, FALSE); |
342 |
346 |
343 saddr = g_inet_socket_address_new(cand->host, cand->port); |
347 saddr = g_inet_socket_address_new(cand->host, cand->port); |
344 if (!g_socket_bind(cand->sock, saddr, TRUE, &err)) { |
348 if (!g_socket_bind(sock, saddr, TRUE, &err)) { |
345 scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Unable to bind a socket on %s port %u: %s", |
349 scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Unable to bind a socket on %s port %u: %s", |
346 g_inet_address_to_string(cand->host), |
350 g_inet_address_to_string(cand->host), |
347 cand->port, |
351 cand->port, |
348 err->message != NULL ? err->message : "(no message)"); |
352 err->message ? err->message : "(no message)"); |
349 goto cleancontinue; |
353 goto cleancontinue; |
350 } |
354 } |
351 |
355 |
352 if (!g_socket_listen(cand->sock, &err)) { |
356 if (!g_socket_listen(sock, &err)) { |
353 scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Unable to listen on %s port %u: %s", |
357 scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Unable to listen on %s port %u: %s", |
354 g_inet_address_to_string(cand->host), |
358 g_inet_address_to_string(cand->host), |
355 cand->port, |
359 cand->port, |
356 err->message != NULL ? err->message : "(no message)"); |
360 err->message ? err->message : "(no message)"); |
357 goto cleancontinue; |
361 goto cleancontinue; |
358 } |
362 } |
359 |
363 |
360 if (!g_socket_listener_add_socket(js5b->listener, cand->sock, NULL, &err)) { |
364 if (!g_socket_listener_add_socket(js5b->listener, sock, NULL, &err)) { |
361 scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Unable to add our socket to the" |
365 scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Unable to add our socket to the" |
362 " GSocketListener: %s", |
366 " GSocketListener: %s", |
363 err->message != NULL ? err->message : "(no message)"); |
367 err->message ? err->message : "(no message)"); |
364 goto cleancontinue; |
368 goto cleancontinue; |
365 } |
369 } |
366 |
370 |
367 scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Listening on %s:%u", |
371 scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Listening on %s:%u", |
368 g_inet_address_to_string(cand->host), |
372 g_inet_address_to_string(cand->host), |
369 cand->port); |
373 cand->port); |
370 ++numlistening; |
374 ++numlistening; |
371 |
375 |
372 cleancontinue: |
376 cleancontinue: |
|
377 if (err != NULL) g_clear_error(&err); |
373 g_object_unref(saddr); |
378 g_object_unref(saddr); |
374 g_object_unref(cand->sock); |
379 g_object_unref(sock); |
375 } |
380 } |
376 |
381 |
377 if (numlistening > 0) { |
382 if (numlistening > 0) { |
378 g_socket_listener_accept_async(js5b->listener, NULL, handle_listener_accept, NULL); |
383 g_socket_listener_accept_async(js5b->listener, NULL, handle_listener_accept, NULL); |
379 } else { |
384 } else { |
380 g_object_unref(js5b->listener); |
385 g_object_unref(js5b->listener); |
381 } |
386 } |
382 |
387 |
383 // Then, we start connecting to the other entity's candidates, if any. |
388 // Then, we start connecting to the other entity's candidates, if any. |
|
389 if (js5b->candidates) { |
|
390 js5b->client = g_socket_client_new(); |
|
391 S5BCandidate *cand = (S5BCandidate *)js5b->candidates->data; |
|
392 connect_candidate(js5b, cand); |
|
393 } |
|
394 } |
|
395 |
|
396 /** |
|
397 * @brief Cancel an ongoing connection after 5 seconds |
|
398 * @param data A GPtrArray |
|
399 * |
|
400 * "A client SHOULD NOT wait for a TCP timeout on connect. |
|
401 * If it is unable to connect to any candidate within 5 seconds |
|
402 * it SHOULD send a candidate-error to the other party." |
|
403 */ |
|
404 static gboolean connect_cancel_timeout(gpointer data) |
|
405 { |
|
406 GPtrArray *args = (GPtrArray *)data; |
|
407 JingleS5B *js5b = g_ptr_array_index(args, 0); |
|
408 //S5BCandidate *cand = g_ptr_array_index(args, 1); |
|
409 g_ptr_array_unref(args); |
|
410 |
|
411 g_cancellable_cancel(js5b->cancelconnect); |
|
412 return FALSE; |
|
413 } |
|
414 |
|
415 static void connect_candidate(JingleS5B *js5b, S5BCandidate *cand) |
|
416 { |
|
417 GSocketAddress *saddr; |
|
418 GPtrArray *args; |
|
419 guint eventid; |
|
420 |
|
421 args = g_ptr_array_sized_new(2); |
|
422 g_ptr_array_add(args, js5b); |
|
423 g_ptr_array_add(args, cand); |
|
424 |
|
425 saddr = g_inet_socket_address_new(cand->host, cand->port); |
|
426 js5b->cancelconnect = g_cancellable_new(); |
|
427 |
|
428 eventid = g_timeout_add_seconds(5, connect_cancel_timeout, g_ptr_array_ref(args)); |
|
429 g_ptr_array_add(args, GUINT_TO_POINTER(eventid)); |
|
430 g_socket_client_connect_async(js5b->client, G_SOCKET_CONNECTABLE(saddr), |
|
431 js5b->cancelconnect, handle_client_connect, args); |
|
432 g_object_unref(saddr); |
|
433 } |
|
434 |
|
435 /** |
|
436 * Convenience function that find the next candidate to try and |
|
437 * call connect_candidate |
|
438 */ |
|
439 static void connect_next_candidate(JingleS5B *js5b, S5BCandidate *cand) |
|
440 { |
|
441 GSList *link = g_slist_find(js5b->candidates, cand); |
|
442 if (js5b->cancelconnect != NULL) |
|
443 g_object_unref(js5b->cancelconnect); |
|
444 g_assert(link != NULL); |
|
445 if (link->next == NULL) { |
|
446 // there is no next candidate to try. |
|
447 } |
|
448 connect_candidate(js5b, (S5BCandidate *)link->next->data); |
|
449 return; |
384 } |
450 } |
385 |
451 |
386 static gchar *info(gconstpointer data) |
452 static gchar *info(gconstpointer data) |
387 { |
453 { |
388 //JingleS5B *js5b = (JingleS5B *)data; |
454 //JingleS5B *js5b = (JingleS5B *)data; |
392 |
458 |
393 static void end(session_content *sc, gconstpointer data) { |
459 static void end(session_content *sc, gconstpointer data) { |
394 return; |
460 return; |
395 } |
461 } |
396 |
462 |
397 |
|
398 /** |
463 /** |
399 * @brief Handle incoming connections |
464 * @brief Handle incoming connections |
400 */ |
465 */ |
401 static void |
466 static void |
402 handle_listener_accept(GObject *_listener, GAsyncResult *res, gpointer userdata) |
467 handle_listener_accept(GObject *_listener, GAsyncResult *res, gpointer data) |
403 { |
468 { |
404 GError *err = NULL; |
469 GError *err = NULL; |
405 GSocketConnection *conn; |
470 GSocketConnection *conn; |
406 conn = g_socket_listener_accept_finish((GSocketListener *) _listener, res, NULL, &err); |
471 //scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Got Incoming Connection"); |
|
472 conn = g_socket_listener_accept_finish(G_SOCKET_LISTENER(_listener), res, NULL, &err); |
|
473 } |
|
474 |
|
475 /** |
|
476 * @brief Handle outgoing connections |
|
477 */ |
|
478 static void |
|
479 handle_client_connect(GObject *_client, GAsyncResult *res, gpointer data) |
|
480 { |
|
481 GError *err = NULL; |
|
482 GSocketConnection *conn; |
|
483 GPtrArray *args; |
|
484 JingleS5B *js5b; |
|
485 S5BCandidate *cand; |
|
486 //scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Got Outgoing Connection"); |
|
487 |
|
488 args = (GPtrArray *)data; |
|
489 js5b = g_ptr_array_index(args, 0); |
|
490 cand = g_ptr_array_index(args, 1); |
|
491 g_ptr_array_unref(args); |
|
492 |
|
493 conn = g_socket_client_connect_finish(G_SOCKET_CLIENT(_client), res, &err); |
|
494 |
|
495 if (!g_error_matches(err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { |
|
496 // if we did not received a CANCELLED error, then the time limit was not |
|
497 // reached and we need to clean up the GSource and unref args a second time. |
|
498 guint eventid = GPOINTER_TO_UINT(g_ptr_array_index(args, 2)); |
|
499 GSource *s = g_main_context_find_source_by_id(NULL, eventid); |
|
500 g_source_destroy(s); |
|
501 g_ptr_array_unref(args); |
|
502 connect_next_candidate(js5b, cand); |
|
503 // we need to send a candidate-error in case we cannot connect. |
|
504 return; |
|
505 } |
|
506 |
|
507 if (err != NULL) { |
|
508 if (err->domain == G_IO_ERROR) |
|
509 scr_LogPrint(LPRINT_DEBUG, "Jingle S5B: IO Error (%s)", |
|
510 err->message ? err->message : "no message"); |
|
511 else |
|
512 scr_LogPrint(LPRINT_DEBUG, "Jingle S5B: %s Error (%s)", |
|
513 g_quark_to_string (err->domain), |
|
514 err->message ? err->message : "no message"); |
|
515 |
|
516 g_error_free(err); |
|
517 connect_next_candidate(js5b, cand); |
|
518 return; |
|
519 } |
|
520 js5b->connection = conn; |
|
521 // we have a valid connection |
407 } |
522 } |
408 |
523 |
409 /** |
524 /** |
410 * @brief Handle any event on a sock |
525 * @brief Handle any event on a sock |
411 */ |
526 */ |
438 struct sockaddr_in *native; |
558 struct sockaddr_in *native; |
439 struct sockaddr_in6 *native6; |
559 struct sockaddr_in6 *native6; |
440 const guint8 *addrdata; |
560 const guint8 *addrdata; |
441 guint16 ifacecounter = 0; // for lack of a better method |
561 guint16 ifacecounter = 0; // for lack of a better method |
442 LocalIP *candidate; |
562 LocalIP *candidate; |
|
563 gchar **ifblacklist; |
|
564 guint ifblkcnt; |
443 |
565 |
444 gint rval = getifaddrs(&first); |
566 gint rval = getifaddrs(&first); |
445 if (rval != 0) |
567 if (rval != 0) { |
|
568 scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Unable to retreive local ip addresses"); |
446 return NULL; |
569 return NULL; |
|
570 } |
|
571 |
|
572 if (settings_opt_get("js5b_iface_blacklist") != NULL) { |
|
573 ifblacklist = g_strsplit(settings_opt_get("js5b_iface_blacklist"), ",", 0); |
|
574 } else { |
|
575 ifblacklist = (gchar*[]){NULL}; |
|
576 } |
447 |
577 |
448 for (ifaddr = first; ifaddr; ifaddr = ifaddr->ifa_next) { |
578 for (ifaddr = first; ifaddr; ifaddr = ifaddr->ifa_next) { |
|
579 gboolean continueloop = FALSE; |
449 if (!(ifaddr->ifa_flags & IFF_UP) || ifaddr->ifa_flags & IFF_LOOPBACK) |
580 if (!(ifaddr->ifa_flags & IFF_UP) || ifaddr->ifa_flags & IFF_LOOPBACK) |
450 continue; |
581 continue; |
|
582 |
|
583 for (ifblkcnt = 0; ifblacklist[ifblkcnt]; ifblkcnt++) |
|
584 if (!g_strcmp0(ifaddr->ifa_name, ifblacklist[ifblkcnt])) { |
|
585 continueloop = TRUE; |
|
586 break; |
|
587 } |
|
588 |
|
589 if (continueloop) continue; |
451 |
590 |
452 if (ifaddr->ifa_addr->sa_family == AF_INET) { |
591 if (ifaddr->ifa_addr->sa_family == AF_INET) { |
453 native = (struct sockaddr_in *)ifaddr->ifa_addr; |
592 native = (struct sockaddr_in *)ifaddr->ifa_addr; |
454 addrdata = (const guint8 *)&native->sin_addr.s_addr; |
593 addrdata = (const guint8 *)&native->sin_addr.s_addr; |
455 family = G_SOCKET_FAMILY_IPV4; |
594 family = G_SOCKET_FAMILY_IPV4; |