jingle-s5b/s5b.c
changeset 166 c42c167a2a5c
child 167 97f93fa3cd95
equal deleted inserted replaced
165:763c26abd99d 166:c42c167a2a5c
       
     1 /*
       
     2  * socks5.c
       
     3  *
       
     4  * Copyrigth (C) 2010 Nicolas Cornu <nicolas.cornu@ensi-bourges.fr>
       
     5  *
       
     6  * This program is free software; you can redistribute it and/or modify
       
     7  * it under the terms of the GNU General Public License as published by
       
     8  * the Free Software Foundation; either version 2 of the License, or (at
       
     9  * your option) any later version.
       
    10  *
       
    11  * This program is distributed in the hope that it will be useful, but
       
    12  * WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    14  * General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU General Public License
       
    17  * along with this program; if not, write to the Free Software
       
    18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
       
    19  * USA
       
    20  */
       
    21 
       
    22 #include "config.h"
       
    23 
       
    24 #include <glib.h>
       
    25 #include <gio/gio.h>
       
    26 
       
    27 #include <sys/types.h>
       
    28 #include <ifaddrs.h>
       
    29 #include <net/if.h>
       
    30 #include <netinet/in.h>
       
    31 
       
    32 #include <mcabber/xmpp.h>
       
    33 #include <mcabber/modules.h>
       
    34 #include <mcabber/utils.h>
       
    35 #include <mcabber/xmpp_helper.h>
       
    36 #include <mcabber/settings.h>
       
    37 #include <mcabber/logprint.h>
       
    38 #include <mcabber/hooks.h>
       
    39 
       
    40 #include <jingle/jingle.h>
       
    41 #include <jingle/check.h>
       
    42 #include <jingle/register.h>
       
    43 
       
    44 #include "s5b.h"
       
    45 
       
    46 static gconstpointer newfrommessage(JingleContent *cn, GError **err);
       
    47 static JingleHandleStatus handle(JingleAction action, gconstpointer data,
       
    48                                  LmMessageNode *node, GError **err);
       
    49 static void tomessage(gconstpointer data, LmMessageNode *node);
       
    50 static gconstpointer new(void);
       
    51 static void _send(session_content *sc, gconstpointer data, gchar *buf, gsize size);
       
    52 static void init(session_content *sc, gconstpointer data);
       
    53 static void end(session_content *sc, gconstpointer data);
       
    54 static gchar *info(gconstpointer data);
       
    55 
       
    56 static void connect_candidate(JingleS5B *js5b, S5BCandidate *cand);
       
    57 static void connect_next_candidate(JingleS5B *js5b, S5BCandidate *cand);
       
    58 static void
       
    59 handle_listener_accept(GObject *_listener, GAsyncResult *res, gpointer data);
       
    60 static void
       
    61 handle_client_connect(GObject *_client, GAsyncResult *res, gpointer data);
       
    62 static void handle_sock_io(GSocket *sock, GIOCondition cond, gpointer data);
       
    63 static GSList *get_all_local_ips();
       
    64 static gchar *gen_random_sid(void);
       
    65 static gchar *gen_random_cid(void);
       
    66 static void jingle_socks5_init(void);
       
    67 static void jingle_socks5_uninit(void);
       
    68 
       
    69 
       
    70 const gchar *deps[] = { "jingle", NULL };
       
    71 
       
    72 static JingleTransportFuncs funcs = {
       
    73   .newfrommessage = newfrommessage,
       
    74   .handle         = handle,
       
    75   .tomessage      = tomessage,
       
    76   .new            = new,
       
    77   .send           = _send,
       
    78   .init           = init,
       
    79   .end            = end,
       
    80   .info           = info
       
    81 };
       
    82 
       
    83 module_info_t  info_jingle_s5b = {
       
    84   .branch          = MCABBER_BRANCH,
       
    85   .api             = MCABBER_API_VERSION,
       
    86   .version         = PROJECT_VERSION,
       
    87   .description     = "Jingle SOCKS5 Bytestream (XEP-0260)\n",
       
    88   .requires        = deps,
       
    89   .init            = jingle_socks5_init,
       
    90   .uninit          = jingle_socks5_uninit,
       
    91   .next            = NULL,
       
    92 };
       
    93 
       
    94 static const gchar *jingle_s5b_types[] = {
       
    95   "direct",
       
    96   "assisted",
       
    97   "tunnel",
       
    98   "proxy",
       
    99   NULL
       
   100 };
       
   101 
       
   102 static const gchar *jingle_s5b_modes[] = {
       
   103   "tcp",
       
   104   "udp",
       
   105   NULL
       
   106 };
       
   107 
       
   108 typedef struct {
       
   109   GInetAddress *address;
       
   110   guint32       priority;
       
   111   JingleS5BType type;
       
   112 } LocalIP;
       
   113 
       
   114 /**
       
   115  * @brief Linked list of candidates to send on session-initiate
       
   116  */
       
   117 static GSList *local_ips = NULL;
       
   118 
       
   119 
       
   120 static gint index_in_array(const gchar *str, const gchar **array)
       
   121 {
       
   122   gint i;
       
   123   for (i = 0; array[i]; i++) {
       
   124     if (!g_strcmp0(array[i], str)) {
       
   125       return i;
       
   126     }
       
   127   }
       
   128   return -1;
       
   129 }
       
   130 
       
   131 static gint prioritycmp(gconstpointer a, gconstpointer b)
       
   132 {
       
   133   S5BCandidate *s1 = (S5BCandidate *)a, *s2 = (S5BCandidate *)b;
       
   134   if (s1->priority < s2->priority) {
       
   135     return 1;
       
   136   } else if (s1->priority > s2->priority) {
       
   137     return -1;
       
   138   } else {
       
   139     return 0;
       
   140   }
       
   141 }
       
   142 
       
   143 /**
       
   144  * @brief Parse a list of <candidate> elements
       
   145  * @return a list of S5BCandidate
       
   146  */
       
   147 static GSList *parse_candidates(LmMessageNode *node)
       
   148 {
       
   149   LmMessageNode *node2;
       
   150   GSList *list = NULL;
       
   151 
       
   152   for (node2 = node->children; node2; node2 = node2->next) {
       
   153     if (g_strcmp0(node->name, "candidate"))
       
   154         continue;
       
   155     const gchar *hoststr, *portstr, *prioritystr, *typestr;
       
   156     S5BCandidate *cand = g_new0(S5BCandidate, 1);
       
   157     cand->cid    = g_strdup(lm_message_node_get_attribute(node2, "cid"));
       
   158     cand->jid    = g_strdup(lm_message_node_get_attribute(node2, "jid"));
       
   159     hoststr      = lm_message_node_get_attribute(node2, "host");
       
   160     portstr      = lm_message_node_get_attribute(node2, "port");
       
   161     prioritystr  = lm_message_node_get_attribute(node2, "priority");
       
   162     typestr      = lm_message_node_get_attribute(node2, "type");
       
   163 
       
   164     if (!cand->cid || !hoststr || !cand->jid || !prioritystr) {
       
   165       g_free(cand);
       
   166       continue;
       
   167     }
       
   168     cand->host     = g_inet_address_new_from_string(hoststr);
       
   169     cand->port     = g_ascii_strtoull(portstr, NULL, 10);
       
   170     cand->priority = g_ascii_strtoull(prioritystr, NULL, 10);
       
   171     cand->type     = index_in_array(typestr, jingle_s5b_types);
       
   172 
       
   173     if (cand->type == -1 || cand->host == NULL) {
       
   174       g_free(cand);
       
   175       continue;
       
   176     }
       
   177 
       
   178     list = g_slist_prepend(list, cand);
       
   179   }
       
   180   list = g_slist_sort(list, prioritycmp);
       
   181   return list;
       
   182 }
       
   183 
       
   184 static GSList *get_our_candidates(guint16 port)
       
   185 {
       
   186   GSList *our_candidates = NULL, *entry;
       
   187 
       
   188   for (entry = local_ips; entry; entry = entry->next) {
       
   189     LocalIP *lcand = (LocalIP *)entry->data;
       
   190     S5BCandidate *cand = g_new0(S5BCandidate, 1);
       
   191     cand->cid      = gen_random_cid();
       
   192     cand->host     = g_object_ref(lcand->address);
       
   193     cand->jid      = g_strdup(lm_connection_get_jid(lconnection));
       
   194     cand->port     = port;
       
   195     cand->priority = lcand->priority;
       
   196 
       
   197     our_candidates = g_slist_prepend(our_candidates, cand);
       
   198   }
       
   199   our_candidates = g_slist_sort(our_candidates, prioritycmp);
       
   200   return our_candidates;
       
   201 }
       
   202 
       
   203 /**
       
   204  * @brief Get a port number by settings or randomly
       
   205  * @return A guint16 containing the port number
       
   206  * */
       
   207 static guint16 get_port(void)
       
   208 {
       
   209   // TODO: find a way to make sure the port is not already used
       
   210   guint64 portstart, portend;
       
   211   guint16 port;
       
   212   const gchar *port_range = settings_opt_get("js5b_portrange");
       
   213 
       
   214   if (port_range != NULL) {
       
   215     sscanf(port_range, "%" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT, &portstart, &portend);
       
   216 
       
   217     if ((portstart >= 1024 && portstart <= (guint16)~0) &&
       
   218         (portend >= 1024 && portend <= (guint16)~0) && portstart <= portend) {
       
   219       port = g_random_int_range(portstart, portend);
       
   220     } else {
       
   221       scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Invalid port range specified");
       
   222       port = g_random_int_range(1024, (guint16)~0);
       
   223     }
       
   224   } else {
       
   225     port = g_random_int_range(1024, (guint16)~0);
       
   226   }
       
   227 
       
   228   return port;
       
   229 }
       
   230 
       
   231 static gconstpointer newfrommessage(JingleContent *cn, GError **err)
       
   232 {
       
   233   JingleS5B *js5b;
       
   234   LmMessageNode *node = cn->transport;
       
   235   const gchar *modestr;
       
   236 
       
   237   js5b = g_new0(JingleS5B, 1);
       
   238   modestr    = lm_message_node_get_attribute(node, "mode");
       
   239   js5b->mode = index_in_array(modestr, jingle_s5b_modes);
       
   240   js5b->sid  = g_strdup(lm_message_node_get_attribute(node, "sid"));
       
   241 
       
   242   if (!js5b->sid) {
       
   243     g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_MISSING,
       
   244                 "an attribute of the transport element is missing");
       
   245     g_free(js5b);
       
   246     return NULL;
       
   247   }
       
   248 
       
   249   js5b->candidates = parse_candidates(node);
       
   250   js5b->ourcandidates = get_our_candidates(get_port());
       
   251 
       
   252   return (gconstpointer) js5b;
       
   253 }
       
   254 
       
   255 static gconstpointer new(void)
       
   256 {
       
   257   JingleS5B *js5b = g_new0(JingleS5B, 1);
       
   258 
       
   259   js5b->mode = JINGLE_S5B_TCP;
       
   260   js5b->sid  = gen_random_sid();
       
   261 
       
   262   js5b->ourcandidates = get_our_candidates(get_port());
       
   263 
       
   264   return js5b;
       
   265 }
       
   266 
       
   267 static JingleHandleStatus handle(JingleAction action, gconstpointer data,
       
   268                                  LmMessageNode *node, GError **err)
       
   269 {
       
   270   JingleS5B *js5b = (JingleS5B *)data;
       
   271 
       
   272   if (action == JINGLE_SESSION_ACCEPT) {
       
   273     js5b->candidates = parse_candidates(node);
       
   274     return JINGLE_STATUS_HANDLED;
       
   275   } else if (action == JINGLE_TRANSPORT_INFO) {
       
   276     LmMessageNode *errorn, *usedn;
       
   277     if (g_strcmp0(lm_message_node_get_attribute(node, "sid"), js5b->sid))
       
   278       return JINGLE_STATUS_HANDLED; // huh.. not the same socks5 sid ?
       
   279 
       
   280     errorn = lm_message_node_get_child(node, "candidate-error");
       
   281     usedn = lm_message_node_get_child(node, "candidate-used");
       
   282     if (errorn != FALSE) {
       
   283       //got_candidate_error
       
   284     } else if (usedn != FALSE) {
       
   285       //got_candidate_used
       
   286     }
       
   287     return JINGLE_STATUS_HANDLED;
       
   288   }
       
   289   return JINGLE_STATUS_NOT_HANDLED;
       
   290 }
       
   291 
       
   292 static void tomessage(gconstpointer data, LmMessageNode *node)
       
   293 {
       
   294   JingleS5B *js5 = (JingleS5B *)data;
       
   295   S5BCandidate *js5c;
       
   296   
       
   297   LmMessageNode *node2, *node3;
       
   298   gchar *port;
       
   299   gchar *priority;
       
   300   GSList *el;
       
   301   
       
   302   if (lm_message_node_get_child(node, "transport") != NULL)
       
   303     return;
       
   304   
       
   305   node2 = lm_message_node_add_child(node, "transport", NULL);
       
   306 
       
   307   lm_message_node_set_attributes(node2, "xmlns", NS_JINGLE_TRANSPORT_SOCKS5,
       
   308                                  "sid", js5->sid,
       
   309                                  "mode", jingle_s5b_modes[js5->mode],
       
   310                                  NULL);
       
   311   for (el = js5->ourcandidates; el; el = el->next) {
       
   312     js5c = (S5BCandidate*) el->data;
       
   313     node3 = lm_message_node_add_child(node2, "candidate", NULL);
       
   314     
       
   315     port = g_strdup_printf("%" G_GUINT16_FORMAT, js5c->port);
       
   316     priority = g_strdup_printf("%" G_GUINT64_FORMAT, js5c->priority);
       
   317     
       
   318     lm_message_node_set_attributes(node3, "cid", js5c->cid,
       
   319                                    "host", g_inet_address_to_string(js5c->host),
       
   320                                    "jid", js5c->jid,
       
   321                                    "port", port,
       
   322                                    "priority", priority,
       
   323                                    "type", jingle_s5b_types[js5c->type],
       
   324                                    NULL);
       
   325     g_free(port);
       
   326     g_free(priority);
       
   327   }
       
   328 }
       
   329 
       
   330 static void init(session_content *sc, gconstpointer data)
       
   331 {
       
   332   JingleS5B *js5b = (JingleS5B *)data;
       
   333   GSocketAddress *saddr;
       
   334   guint numlistening = 0; // number of addresses we are listening to
       
   335   GSList *entry;
       
   336   GError *err = NULL;
       
   337   GSocket *sock;
       
   338 
       
   339   // First, we listen on all ips
       
   340   js5b->listener = g_socket_listener_new();
       
   341   for (entry = js5b->ourcandidates; entry; entry = entry->next) {
       
   342     S5BCandidate *cand = (S5BCandidate *)entry->data;
       
   343 
       
   344     sock = g_socket_new(g_inet_address_get_family(cand->host),
       
   345                         G_SOCKET_TYPE_STREAM,
       
   346                         G_SOCKET_PROTOCOL_TCP, &err);
       
   347     if (sock == NULL) {
       
   348       scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Error while creating a new socket: %s",
       
   349                    err->message ? err->message : "(no message)");
       
   350       continue;
       
   351     }
       
   352     g_socket_set_blocking(sock, FALSE);
       
   353 
       
   354     saddr = g_inet_socket_address_new(cand->host, cand->port);
       
   355     if (!g_socket_bind(sock, saddr, TRUE, &err)) {
       
   356       scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Unable to bind a socket on %s port %u: %s",
       
   357                    g_inet_address_to_string(cand->host),
       
   358                    cand->port,
       
   359                    err->message ? err->message : "(no message)");
       
   360       goto cleancontinue;
       
   361     }
       
   362 
       
   363     if (!g_socket_listen(sock, &err)) {
       
   364       scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Unable to listen on %s port %u: %s",
       
   365                    g_inet_address_to_string(cand->host),
       
   366                    cand->port,
       
   367                    err->message ? err->message : "(no message)");
       
   368       goto cleancontinue;
       
   369     }
       
   370 
       
   371     if (!g_socket_listener_add_socket(js5b->listener, sock, NULL, &err)) {
       
   372       scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Unable to add our socket to the"
       
   373                    " GSocketListener: %s",
       
   374                    err->message ? err->message : "(no message)");
       
   375       goto cleancontinue;
       
   376     }
       
   377 
       
   378     scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Listening on %s:%u",
       
   379                  g_inet_address_to_string(cand->host),
       
   380                  cand->port);
       
   381     ++numlistening;
       
   382 
       
   383 cleancontinue:
       
   384       if (err != NULL) g_clear_error(&err);
       
   385       g_object_unref(saddr);
       
   386       g_object_unref(sock);
       
   387   }
       
   388 
       
   389   if (numlistening > 0) {
       
   390     g_socket_listener_accept_async(js5b->listener, NULL, handle_listener_accept, NULL);
       
   391   } else {
       
   392       g_object_unref(js5b->listener);
       
   393   }
       
   394 
       
   395   // Then, we start connecting to the other entity's candidates, if any.
       
   396   if (js5b->candidates) {
       
   397     js5b->client = g_socket_client_new();
       
   398     S5BCandidate *cand = (S5BCandidate *)js5b->candidates->data;
       
   399     connect_candidate(js5b, cand);
       
   400   }
       
   401 }
       
   402 
       
   403 /**
       
   404  * @brief Called when a connection was established
       
   405  * 
       
   406  * This function free/unref everything created by init like
       
   407  * the GSocketListener and GClientSocket objects.
       
   408  */
       
   409 static void free_after_connection(JingleS5B *js5b)
       
   410 {
       
   411   g_socket_listener_close(js5b->listener);
       
   412   g_object_unref(js5b->listener);
       
   413   g_object_unref(js5b->client);
       
   414 }
       
   415 
       
   416 /**
       
   417  * @brief Cancel an ongoing connection after 5 seconds
       
   418  * @param data  A GPtrArray
       
   419  * 
       
   420  * "A client SHOULD NOT wait for a TCP timeout on connect.
       
   421  * If it is unable to connect to any candidate within 5 seconds
       
   422  * it SHOULD send a candidate-error to the other party."
       
   423  */
       
   424 static gboolean connect_cancel_timeout(gpointer data)
       
   425 {
       
   426   GPtrArray *args = (GPtrArray *)data;
       
   427   JingleS5B *js5b = g_ptr_array_index(args, 0);
       
   428   //S5BCandidate *cand = g_ptr_array_index(args, 1);
       
   429   g_ptr_array_unref(args);
       
   430 
       
   431   g_cancellable_cancel(js5b->cancelconnect);
       
   432   // we need to send a candidate-error in case we cannot connect.
       
   433   return FALSE;
       
   434 }
       
   435 
       
   436 static void connect_candidate(JingleS5B *js5b, S5BCandidate *cand)
       
   437 {
       
   438   GSocketAddress *saddr;
       
   439   GPtrArray *args;
       
   440   guint eventid;
       
   441 
       
   442   args = g_ptr_array_sized_new(2);
       
   443   g_ptr_array_add(args, js5b);
       
   444   g_ptr_array_add(args, cand);
       
   445 
       
   446   saddr = g_inet_socket_address_new(cand->host, cand->port);
       
   447   js5b->cancelconnect = g_cancellable_new();
       
   448 
       
   449   eventid = g_timeout_add_seconds(5, connect_cancel_timeout, g_ptr_array_ref(args));
       
   450   g_ptr_array_add(args, GUINT_TO_POINTER(eventid));
       
   451   g_socket_client_connect_async(js5b->client, G_SOCKET_CONNECTABLE(saddr),
       
   452                                 js5b->cancelconnect, handle_client_connect, args);
       
   453   g_object_unref(saddr);
       
   454 }
       
   455 
       
   456 /**
       
   457  * Convenience function that find the next candidate to try and
       
   458  * call connect_candidate
       
   459  */
       
   460 static void connect_next_candidate(JingleS5B *js5b, S5BCandidate *cand)
       
   461 {
       
   462   GSList *link = g_slist_find(js5b->candidates, cand);
       
   463   if (js5b->cancelconnect != NULL)
       
   464     g_object_unref(js5b->cancelconnect);
       
   465   g_assert(link != NULL);
       
   466   if (link->next == NULL) {
       
   467     // there is no next candidate to try.
       
   468   }
       
   469   connect_candidate(js5b, (S5BCandidate *)link->next->data);
       
   470   return;
       
   471 }
       
   472 
       
   473 static gchar *info(gconstpointer data)
       
   474 {
       
   475   //JingleS5B *js5b = (JingleS5B *)data;
       
   476   gchar *info = g_strdup_printf("S5B");
       
   477   return info;
       
   478 }
       
   479 
       
   480 static void end(session_content *sc, gconstpointer data) {
       
   481   return;
       
   482 }
       
   483 
       
   484 /**
       
   485  * @brief Handle incoming connections
       
   486  */
       
   487 static void
       
   488 handle_listener_accept(GObject *_listener, GAsyncResult *res, gpointer data)
       
   489 {
       
   490   GError *err = NULL;
       
   491   GSocketConnection *conn;
       
   492   //scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Got Incoming Connection");
       
   493   conn = g_socket_listener_accept_finish(G_SOCKET_LISTENER(_listener), res, NULL, &err);
       
   494 }
       
   495 
       
   496 /**
       
   497  * @brief Handle outgoing connections
       
   498  */
       
   499 static void
       
   500 handle_client_connect(GObject *_client, GAsyncResult *res, gpointer data)
       
   501 {
       
   502   GError *err = NULL;
       
   503   GSocketConnection *conn;
       
   504   GPtrArray *args;
       
   505   JingleS5B *js5b;
       
   506   S5BCandidate *cand;
       
   507   //scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Got Outgoing Connection");
       
   508   
       
   509   args = (GPtrArray *)data;
       
   510   js5b = g_ptr_array_index(args, 0);
       
   511   cand = g_ptr_array_index(args, 1);
       
   512   g_ptr_array_unref(args);
       
   513 
       
   514   conn = g_socket_client_connect_finish(G_SOCKET_CLIENT(_client), res, &err);
       
   515 
       
   516   if (!g_error_matches(err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
       
   517     // if we did not received a CANCELLED error, then the time limit was not
       
   518     // reached and we need to clean up the GSource and unref args a second time.
       
   519     guint eventid = GPOINTER_TO_UINT(g_ptr_array_index(args, 2));
       
   520     GSource *s = g_main_context_find_source_by_id(NULL, eventid);
       
   521     g_source_destroy(s);
       
   522     g_ptr_array_unref(args);
       
   523     connect_next_candidate(js5b, cand);
       
   524     return;
       
   525   }
       
   526 
       
   527   if (err != NULL) {
       
   528     if (err->domain == G_IO_ERROR)
       
   529       scr_LogPrint(LPRINT_DEBUG, "Jingle S5B: IO Error (%s)",
       
   530                    err->message ? err->message : "no message");
       
   531     else
       
   532       scr_LogPrint(LPRINT_DEBUG, "Jingle S5B: %s Error (%s)",
       
   533                    g_quark_to_string (err->domain),
       
   534                    err->message ? err->message : "no message");
       
   535 
       
   536     g_error_free(err);
       
   537     connect_next_candidate(js5b, cand);
       
   538     return;
       
   539   }
       
   540   js5b->connection = conn; // we have a valid connection
       
   541   // TODO: send transport-info activated IQ
       
   542 }
       
   543 
       
   544 /**
       
   545  * @brief Handle any event on a sock
       
   546  */
       
   547 static void handle_sock_io(GSocket *sock, GIOCondition cond, gpointer data)
       
   548 {
       
   549   switch (cond) {
       
   550     case G_IO_IN:
       
   551       break;
       
   552     case G_IO_OUT:
       
   553       break;
       
   554     case G_IO_ERR:
       
   555       break;
       
   556     case G_IO_HUP:
       
   557       break;
       
   558     default:
       
   559       ;
       
   560       // ?!
       
   561   }
       
   562 }
       
   563 
       
   564 static void _send(session_content *sc, gconstpointer data, gchar *buf, gsize size)
       
   565 {
       
   566   return;
       
   567 }
       
   568 
       
   569 /**
       
   570  * @brief Discover all IPs of this computer
       
   571  * @return A linked list of GInetAddress
       
   572  */
       
   573 static GSList *get_all_local_ips(void) {
       
   574   GSList *addresses = NULL;
       
   575   GInetAddress *thisaddr;
       
   576   GSocketFamily family;
       
   577   struct ifaddrs *first, *ifaddr;
       
   578   struct sockaddr_in *native;
       
   579   struct sockaddr_in6 *native6;
       
   580   const guint8 *addrdata;
       
   581   guint16 ifacecounter = 0; // for lack of a better method
       
   582   LocalIP *candidate;
       
   583   gchar **ifblacklist;
       
   584   guint ifblkcnt;
       
   585 
       
   586   gint rval = getifaddrs(&first);
       
   587   if (rval != 0) {
       
   588     scr_LogPrint(LPRINT_LOGNORM, "Jingle S5B: Unable to retreive local ip addresses");
       
   589     return NULL;
       
   590   }
       
   591 
       
   592   if (settings_opt_get("js5b_iface_blacklist") != NULL) {
       
   593     ifblacklist = g_strsplit(settings_opt_get("js5b_iface_blacklist"), ",", 0);
       
   594   } else {
       
   595     ifblacklist = (gchar*[]){NULL};
       
   596   }
       
   597 
       
   598   for (ifaddr = first; ifaddr; ifaddr = ifaddr->ifa_next) {
       
   599     gboolean continueloop = FALSE;
       
   600     if (!(ifaddr->ifa_flags & IFF_UP) || ifaddr->ifa_flags & IFF_LOOPBACK)
       
   601       continue;
       
   602 
       
   603     for (ifblkcnt = 0; ifblacklist[ifblkcnt]; ifblkcnt++)
       
   604       if (!g_strcmp0(ifaddr->ifa_name, ifblacklist[ifblkcnt])) {
       
   605         continueloop = TRUE;
       
   606         break;
       
   607       }
       
   608     
       
   609     if (continueloop) continue;
       
   610 
       
   611     if (ifaddr->ifa_addr->sa_family == AF_INET) {
       
   612       native = (struct sockaddr_in *)ifaddr->ifa_addr;
       
   613       addrdata = (const guint8 *)&native->sin_addr.s_addr;
       
   614       family = G_SOCKET_FAMILY_IPV4;
       
   615     } else if (ifaddr->ifa_addr->sa_family == AF_INET6) {
       
   616       native6 = (struct sockaddr_in6 *)ifaddr->ifa_addr;
       
   617       addrdata = (const guint8 *)&native6->sin6_addr.s6_addr;
       
   618       family = G_SOCKET_FAMILY_IPV6;
       
   619     } else
       
   620       continue;
       
   621 
       
   622     thisaddr = g_inet_address_new_from_bytes(addrdata, family);
       
   623     if (g_inet_address_get_is_link_local(thisaddr)) {
       
   624       g_object_unref(thisaddr);
       
   625       continue;
       
   626     }/* else if (g_inset_address_get_is_site_local(thisaddr)) {
       
   627       // TODO: should we offer a way to filter the offer of LAN ips ?
       
   628     } */
       
   629     candidate = g_new0(LocalIP, 1);
       
   630     candidate->address  = thisaddr;
       
   631     candidate->priority = (1<<16)*126+ifacecounter;
       
   632     candidate->type     = JINGLE_S5B_DIRECT;
       
   633     addresses = g_slist_prepend(addresses, candidate);
       
   634     ++ifacecounter;
       
   635   }
       
   636   freeifaddrs(first);
       
   637 
       
   638   return addresses;
       
   639 }
       
   640 
       
   641 static gchar *random_str(guint len)
       
   642 {
       
   643   gchar *str;
       
   644   gchar car[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
       
   645   gint i;
       
   646   str = g_new0(gchar, 8);
       
   647   for (i = 0; i < len; i++)
       
   648     str[i] = car[g_random_int_range(0, sizeof(car)/sizeof(car[0])-1)];
       
   649 
       
   650   str[len] = '\0';
       
   651   return str;
       
   652 }
       
   653 
       
   654 static gchar *gen_random_sid(void)
       
   655 {
       
   656   return random_str(7);
       
   657 }
       
   658 
       
   659 static gchar *gen_random_cid(void)
       
   660 {
       
   661   return random_str(7);
       
   662 }
       
   663 
       
   664 static void free_localip(LocalIP *l) {
       
   665   g_object_unref(l->address);
       
   666   g_free(l);
       
   667 }
       
   668 
       
   669 static void jingle_socks5_init(void)
       
   670 {
       
   671   // ugly hack to fix the segfault when quitting:
       
   672   // mcabber doesn't load gthread or gobject but they are required by gio,
       
   673   // and cannot be unloaded once they are loaded or a segfault occur.
       
   674   // We dlopen gio with global | nodelete flags. This will also load gobject
       
   675   // and gthread as dependencies. g_type_init will init gobject/gthread (and
       
   676   // set threads_got_initialized to true).
       
   677   if (g_threads_got_initialized == FALSE) {
       
   678     void *dlopen(const char *filename, int flag);
       
   679     // RTLD_LAZY | RTLD_GLOBAL | RTLD_NODELETE
       
   680     dlopen("libgio-2.0.so", 0x00001 | 0x00100 | 0x01000);
       
   681     g_type_init();
       
   682   }
       
   683   jingle_register_transport(NS_JINGLE_TRANSPORT_SOCKS5, &funcs,
       
   684                             JINGLE_TRANSPORT_STREAMING,
       
   685                             JINGLE_TRANSPORT_PRIO_HIGH);
       
   686   xmpp_add_feature(NS_JINGLE_TRANSPORT_SOCKS5);
       
   687   local_ips = get_all_local_ips();
       
   688 }
       
   689 
       
   690 static void jingle_socks5_uninit(void)
       
   691 {
       
   692   xmpp_del_feature(NS_JINGLE_TRANSPORT_SOCKS5);
       
   693   jingle_unregister_transport(NS_JINGLE_TRANSPORT_SOCKS5);
       
   694   g_slist_foreach(local_ips, (GFunc)free_localip, NULL);
       
   695   g_slist_free(local_ips);
       
   696 }