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