|
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 } |