jingle-s5b/socks5.c
changeset 154 1ffa1733d4b6
child 156 653fa009fea3
equal deleted inserted replaced
153:eab91df480d3 154:1ffa1733d4b6
       
     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/logprint.h>
       
    37 #include <mcabber/hooks.h>
       
    38 
       
    39 #include <jingle/jingle.h>
       
    40 #include <jingle/check.h>
       
    41 #include <jingle/register.h>
       
    42 
       
    43 #include "socks5.h"
       
    44 
       
    45 static gconstpointer check(JingleContent *cn, GError **err);
       
    46 static void tomessage(gconstpointer data, LmMessageNode *node);
       
    47 // static void _send(session_content *sc, gconstpointer data, gchar *buf, gsize size);
       
    48 static void init(session_content *sc, gconstpointer data);
       
    49 static void end(session_content *sc, gconstpointer data);
       
    50 
       
    51 static void handle_sock_io(GSocket *sock, GIOCondition cond, gpointer data);
       
    52 static GSList *get_all_local_ips();
       
    53 static void jingle_socks5_init(void);
       
    54 static void jingle_socks5_uninit(void);
       
    55 
       
    56 
       
    57 const gchar *deps[] = { "jingle", NULL };
       
    58 
       
    59 static JingleTransportFuncs funcs = {
       
    60   .check     = check,
       
    61   .tomessage = tomessage,
       
    62   .new       = NULL,
       
    63   .send      = NULL,
       
    64   .init      = init,
       
    65   .end       = end
       
    66 };
       
    67 
       
    68 module_info_t  info_jingle_s5b = {
       
    69   .branch          = MCABBER_BRANCH,
       
    70   .api             = MCABBER_API_VERSION,
       
    71   .version         = PROJECT_VERSION,
       
    72   .description     = "Jingle SOCKS5 Bytestream (XEP-0260)\n",
       
    73   .requires        = deps,
       
    74   .init            = jingle_socks5_init,
       
    75   .uninit          = jingle_socks5_uninit,
       
    76   .next            = NULL,
       
    77 };
       
    78 
       
    79 static const gchar *jingle_s5b_types[] = {
       
    80   "assisted",
       
    81   "direct",
       
    82   "proxy",
       
    83   "tunnel",
       
    84   NULL
       
    85 };
       
    86 
       
    87 static const gchar *jingle_s5b_modes[] = {
       
    88   "tcp",
       
    89   "udp",
       
    90   NULL
       
    91 };
       
    92 
       
    93 /**
       
    94  * @brief Linked list of candidates to send on session-initiate
       
    95  */
       
    96 GSList *local_candidates = NULL;
       
    97 
       
    98 
       
    99 static gint index_in_array(const gchar *str, const gchar **array)
       
   100 {
       
   101   gint i;
       
   102   for (i = 0; array[i]; i++) {
       
   103     if (!g_strcmp0(array[i], str)) {
       
   104       return i;
       
   105     }
       
   106   }
       
   107   return -1;
       
   108 }
       
   109 
       
   110 static gint prioritycmp(gconstpointer a, gconstpointer b)
       
   111 {
       
   112   S5BCandidate *s1 = (S5BCandidate *)a, *s2 = (S5BCandidate *)b;
       
   113   if (s1->priority < s2->priority) {
       
   114     return 1;
       
   115   } else if (s1->priority > s2->priority) {
       
   116     return -1;
       
   117   } else {
       
   118     return 0;
       
   119   }
       
   120 }
       
   121 
       
   122 static gconstpointer check(JingleContent *cn, GError **err)
       
   123 {
       
   124   JingleS5B *js5b;
       
   125   LmMessageNode *node = cn->transport, *node2;
       
   126   const gchar *modestr;
       
   127 
       
   128   js5b = g_new0(JingleS5B, 1);
       
   129   modestr    = lm_message_node_get_attribute(node, "mode");
       
   130   js5b->mode = index_in_array(modestr, jingle_s5b_modes);
       
   131   js5b->sid  = g_strdup(lm_message_node_get_attribute(node, "sid"));
       
   132 
       
   133   if (!js5b->sid) {
       
   134     g_set_error(err, JINGLE_CHECK_ERROR, JINGLE_CHECK_ERROR_MISSING,
       
   135                 "an attribute of the transport element is missing");
       
   136     g_free(js5b);
       
   137     return NULL;
       
   138   }
       
   139 
       
   140   for (node2 = node->children; node2; node2 = node2->next) {
       
   141     if (!g_strcmp0(node->name, "candidate")) {
       
   142       const gchar *portstr, *prioritystr, *typestr;
       
   143       S5BCandidate *jc = g_new0(S5BCandidate, 1);
       
   144       jc->cid      = g_strdup(lm_message_node_get_attribute(node2, "cid"));
       
   145       jc->host     = g_strdup(lm_message_node_get_attribute(node2, "host"));
       
   146       jc->jid      = g_strdup(lm_message_node_get_attribute(node2, "jid"));
       
   147       portstr      = lm_message_node_get_attribute(node2, "port");
       
   148       prioritystr  = lm_message_node_get_attribute(node2, "priority");
       
   149       typestr      = lm_message_node_get_attribute(node2, "type");
       
   150 
       
   151       if (!jc->cid || !jc->host || !jc->jid || !prioritystr) {
       
   152         g_free(jc);
       
   153         continue;
       
   154       }
       
   155       jc->port     = g_ascii_strtoull(portstr, NULL, 10);
       
   156       jc->priority = g_ascii_strtoull(prioritystr, NULL, 10);
       
   157       jc->type     = index_in_array(typestr, jingle_s5b_types);
       
   158 
       
   159       if (jc->type == -1) {
       
   160         g_free(jc);
       
   161         continue;
       
   162       }
       
   163 
       
   164       js5b->candidates = g_slist_prepend(js5b->candidates, jc);
       
   165     }
       
   166     js5b->candidates = g_slist_sort(js5b->candidates, prioritycmp);
       
   167   }
       
   168 
       
   169   return (gconstpointer) js5b;
       
   170 }
       
   171 
       
   172 static void tomessage(gconstpointer data, LmMessageNode *node)
       
   173 {
       
   174   JingleS5B *js5 = (JingleS5B *)data;
       
   175   S5BCandidate *js5c;
       
   176   
       
   177   LmMessageNode *node2, *node3;
       
   178   gchar *port;
       
   179   gchar *priority;
       
   180   GSList *el;
       
   181   
       
   182   if (lm_message_node_get_child(node, "transport") != NULL)
       
   183     return;
       
   184   
       
   185   node2 = lm_message_node_add_child(node, "transport", NULL);
       
   186 
       
   187   lm_message_node_set_attributes(node2, "xmlns", NS_JINGLE_TRANSPORT_SOCKS5,
       
   188                                  "sid", js5->sid,
       
   189                                  "mode", jingle_s5b_modes[js5->mode],
       
   190                                  NULL);
       
   191   for (el = js5->candidates; el; el = el->next) {
       
   192     js5c = (S5BCandidate*) el->data;
       
   193     node3 = lm_message_node_add_child(node2, "candidate", NULL);
       
   194     
       
   195     port = g_strdup_printf("%" G_GUINT16_FORMAT, js5c->port);
       
   196     priority = g_strdup_printf("%" G_GUINT64_FORMAT, js5c->priority);
       
   197     
       
   198     lm_message_node_set_attributes(node3, "cid", js5c->cid,
       
   199                                    "host", js5c->host,
       
   200                                    "jid", js5c->jid,
       
   201                                    "port", port,
       
   202                                    "priority", priority,
       
   203                                    "type", jingle_s5b_types[js5c->type],
       
   204                                    NULL);
       
   205     g_free(port);
       
   206     g_free(priority);
       
   207   }
       
   208 }
       
   209 
       
   210 static void init(session_content *sc, gconstpointer data)
       
   211 {
       
   212   JingleS5B *js5 = (JingleS5B *)data;
       
   213   GInetAddress *addr;
       
   214   GSocketAddress *saddr;
       
   215   GSource *socksource;
       
   216   GError *err = NULL;
       
   217   g_assert(js5->sock == NULL);
       
   218 
       
   219   addr = g_inet_address_new_from_string(((S5BCandidate *)js5->candidates->data)->host);
       
   220   js5->sock = g_socket_new(g_inet_address_get_family(addr), G_SOCKET_TYPE_STREAM,
       
   221                            G_SOCKET_PROTOCOL_TCP, &err);
       
   222   if (js5->sock == NULL) {
       
   223     scr_LogPrint(LPRINT_LOGNORM, "Jingle SOCKS5: Error while creating a new socket: %s",
       
   224                  err->message != NULL ? err->message : "(no message)");
       
   225     return; // TODO: we need a way to return errors...
       
   226   }
       
   227   g_socket_set_blocking(js5->sock, FALSE);
       
   228   socksource = g_socket_create_source(js5->sock, ~0, NULL);
       
   229 
       
   230   g_source_set_callback(socksource, (GSourceFunc)handle_sock_io, NULL, NULL);
       
   231   g_source_attach(socksource, NULL);
       
   232   g_source_unref(socksource);
       
   233 
       
   234   saddr = g_inet_socket_address_new(addr, 31337);
       
   235   if (!g_socket_connect(js5->sock, saddr, NULL, &err)) {
       
   236     scr_LogPrint(LPRINT_LOGNORM, "Jingle SOCKS5: Error while connecting to the host: %s",
       
   237                  err->message != NULL ? err->message : "(no message)");
       
   238     return;
       
   239   }
       
   240 
       
   241 }
       
   242 
       
   243 /**
       
   244  * Handle any event on a sock
       
   245  */
       
   246 static void handle_sock_io(GSocket *sock, GIOCondition cond, gpointer data)
       
   247 {
       
   248   switch (cond) {
       
   249     case G_IO_IN:
       
   250       break;
       
   251     case G_IO_OUT:
       
   252       break;
       
   253     case G_IO_ERR:
       
   254       break;
       
   255     case G_IO_HUP:
       
   256       break;
       
   257     default:
       
   258       ;
       
   259       // ?!
       
   260   }
       
   261 }
       
   262 
       
   263 /**
       
   264  * @brief Discover all IPs of this computer
       
   265  * @return A linked list of GInetAddress
       
   266  */
       
   267 static GSList *get_all_local_ips() {
       
   268   GSList *addresses = NULL;
       
   269   GInetAddress *thisaddr;
       
   270   GSocketFamily family;
       
   271   struct ifaddrs *first, *ifaddr;
       
   272   struct sockaddr_in *native;
       
   273   struct sockaddr_in6 *native6;
       
   274   const guint8 *addrdata;
       
   275   int rval;
       
   276 
       
   277   rval = getifaddrs(&first);
       
   278 
       
   279   for (ifaddr = first; ifaddr; ifaddr = ifaddr->ifa_next) {
       
   280     if (!(ifaddr->ifa_flags & IFF_UP) || ifaddr->ifa_flags & IFF_LOOPBACK)
       
   281       continue;
       
   282 
       
   283     if (ifaddr->ifa_addr->sa_family == AF_INET) {
       
   284       native = (struct sockaddr_in *)ifaddr->ifa_addr;
       
   285       addrdata = (const guint8 *)&native->sin_addr.s_addr;
       
   286       family = G_SOCKET_FAMILY_IPV4;
       
   287     } else if (ifaddr->ifa_addr->sa_family == AF_INET6) {
       
   288       native6 = (struct sockaddr_in6 *)ifaddr->ifa_addr;
       
   289       addrdata = (const guint8 *)&native6->sin6_addr.s6_addr;
       
   290       family = G_SOCKET_FAMILY_IPV6;
       
   291     } else
       
   292       continue;
       
   293 
       
   294     thisaddr = g_inet_address_new_from_bytes(addrdata, family);
       
   295     if (g_inet_address_get_is_link_local(thisaddr)) {
       
   296       g_object_unref(thisaddr);
       
   297       continue;
       
   298     }/* else if (g_inset_address_get_is_site_local(thisaddr)) {
       
   299       // TODO: should we offer a way the offer of LAN ips ?
       
   300     } */
       
   301     addresses = g_slist_prepend(addresses, thisaddr);
       
   302   }
       
   303   return addresses;
       
   304 }
       
   305 
       
   306 static void jingle_socks5_init(void)
       
   307 {
       
   308   g_type_init();
       
   309   jingle_register_transport(NS_JINGLE_TRANSPORT_SOCKS5, &funcs,
       
   310                             JINGLE_TRANSPORT_STREAMING,
       
   311                             JINGLE_TRANSPORT_PRIO_HIGH);
       
   312   xmpp_add_feature(NS_JINGLE_TRANSPORT_SOCKS5);
       
   313   local_candidates = get_all_local_ips();
       
   314 }
       
   315 
       
   316 static void jingle_socks5_uninit(void)
       
   317 {
       
   318   xmpp_del_feature(NS_JINGLE_TRANSPORT_SOCKS5);
       
   319   jingle_unregister_transport(NS_JINGLE_TRANSPORT_SOCKS5);
       
   320   g_slist_foreach(local_candidates, (GFunc)g_object_unref, NULL);
       
   321 }