1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2004 Imendio AB
4 * Copyright (C) 2004 Josh Beam <josh@3ddrome.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, see <https://www.gnu.org/licenses>
18 */
19
20 /**
21 * SECTION:lm-proxy
22 * @Title: LmProxy
23 * @Short_description: API for the proxy support in Loudmouth
24 *
25 * Use this together with an #LmConnection to get the connection to use connect through a proxy. Example of how to use the #LmProxy API.
26 * <informalexample><programlisting><![CDATA[
27 * LmConnection *connection;
28 * LmProxy *proxy;
29 *
30 * connection = lm_connection_new ("myserver");
31 * proxy = lm_proxy_new_with_server (LM_PROXY_TYPE_HTTP,
32 * "myproxyserver",
33 * 8080);
34 * lm_connection_set_proxy (connection, proxy);
35 * ...]]></programlisting></informalexample>
36 */
37
38 #include <config.h>
39
40 #include <glib.h>
41 #include <string.h>
42
43 #ifndef G_OS_WIN32
44
45 #include <unistd.h>
46 #include <sys/socket.h>
47
48 #else /* G_OS_WIN32 */
49
50 #include <winsock2.h>
51
52 #endif /* G_OS_WIN32 */
53
54 #include "lm-internals.h"
55 #include "lm-proxy.h"
56 #include "lm-utils.h"
57
58 struct _LmProxy {
59 LmProxyType type;
60 gchar *server;
61 guint port;
62 gchar *username;
63 gchar *password;
64 guint io_watch;
65
66 gint ref_count;
67 };
68
69 static void proxy_free (LmProxy *proxy);
70 static gboolean proxy_http_negotiate (LmProxy *proxy,
71 gint fd,
72 const gchar *server,
73 guint port);
74 static gboolean proxy_negotiate (LmProxy *proxy,
75 gint fd,
76 const gchar *server,
77 guint port);
78 static gboolean proxy_http_read_cb (GIOChannel *source,
79 GIOCondition condition,
80 gpointer data);
81 static gboolean proxy_read_cb (GIOChannel *source,
82 GIOCondition condition,
83 gpointer data);
84
85 static void
proxy_free(LmProxy * proxy)86 proxy_free (LmProxy *proxy)
87 {
88 g_free (proxy->server);
89 g_free (proxy->username);
90 g_free (proxy->password);
91
92 g_free (proxy);
93 }
94
95 static gboolean
proxy_http_negotiate(LmProxy * proxy,gint fd,const gchar * server,guint port)96 proxy_http_negotiate (LmProxy *proxy, gint fd, const gchar *server, guint port)
97 {
98 gchar *str;
99
100 if (proxy->username && proxy->password) {
101 gchar *tmp1;
102 gchar *tmp2;
103
104 tmp1 = g_strdup_printf ("%s:%s",
105 proxy->username,
106 proxy->password);
107 tmp2 = g_base64_encode ((const guchar *) tmp1,
108 (gsize) strlen (tmp1));
109 g_free (tmp1);
110
111 str = g_strdup_printf ("CONNECT %s:%u HTTP/1.1\r\nHost: %s:%u\r\nProxy-Authorization: Basic %s\r\n\r\n",
112 server, port,
113 server, port,
114 tmp2);
115 g_free (tmp2);
116 } else {
117 str = g_strdup_printf ("CONNECT %s:%u HTTP/1.1\r\nHost: %s:%u\r\n\r\n",
118 server, port,
119 server, port);
120 }
121
122 send (fd, str, strlen (str), 0);
123 g_free (str);
124 return TRUE;
125 }
126
127 /* returns TRUE when connected through proxy */
128 static gboolean
proxy_http_read_cb(GIOChannel * source,GIOCondition condition,gpointer data)129 proxy_http_read_cb (GIOChannel *source, GIOCondition condition, gpointer data)
130 {
131 gchar buf[512];
132 gsize bytes_read;
133 GError *error = NULL;
134
135 g_io_channel_read_chars (source, buf, 512, &bytes_read, &error);
136
137 if (bytes_read < 16) {
138 return FALSE;
139 }
140
141 if (strncmp (buf, "HTTP/1.1 200", 12) != 0 &&
142 strncmp (buf, "HTTP/1.0 200", 12) != 0) {
143 return FALSE;
144 }
145
146 if (strncmp (buf + (bytes_read - 4), "\r\n\r\n", 4) != 0) {
147 return FALSE;
148 }
149
150 return TRUE;
151 }
152
153 static gboolean
proxy_read_cb(GIOChannel * source,GIOCondition condition,gpointer data)154 proxy_read_cb (GIOChannel *source, GIOCondition condition, gpointer data)
155 {
156 LmConnectData *connect_data;
157 LmConnection *connection;
158 LmProxy *proxy;
159 gboolean retval = FALSE;
160
161 connect_data = (LmConnectData *) data;
162 connection = connect_data->connection;
163 proxy = lm_connection_get_proxy (connection);
164
165 g_return_val_if_fail (proxy != NULL, FALSE);
166
167 if (lm_connection_is_open (connection)) {
168 return FALSE;
169 }
170
171 switch (lm_proxy_get_type (proxy)) {
172 default:
173 case LM_PROXY_TYPE_NONE:
174 g_assert_not_reached ();
175 break;
176 case LM_PROXY_TYPE_HTTP:
177 retval = proxy_http_read_cb (source, condition, data);
178 break;
179 }
180
181 if (retval == TRUE) {
182 g_source_remove (proxy->io_watch);
183 _lm_old_socket_succeeded ((LmConnectData *) data);
184 }
185
186 return FALSE;
187 }
188
189 gboolean
proxy_negotiate(LmProxy * proxy,gint fd,const gchar * server,guint port)190 proxy_negotiate (LmProxy *proxy, gint fd, const gchar *server, guint port)
191 {
192 g_return_val_if_fail (proxy != NULL, FALSE);
193
194 switch (proxy->type) {
195 case LM_PROXY_TYPE_NONE:
196 return TRUE;
197 case LM_PROXY_TYPE_HTTP:
198 return proxy_http_negotiate (proxy, fd, server, port);
199 default:
200 g_assert_not_reached ();
201 }
202
203 return FALSE;
204 }
205
206 gboolean
_lm_proxy_connect_cb(GIOChannel * source,GIOCondition condition,gpointer data)207 _lm_proxy_connect_cb (GIOChannel *source, GIOCondition condition, gpointer data)
208 {
209 LmConnection *connection;
210 LmConnectData *connect_data;
211 LmProxy *proxy;
212 int error;
213 socklen_t len;
214
215 connect_data = (LmConnectData *) data;
216 connection = connect_data->connection;
217 proxy = lm_connection_get_proxy (connection);
218
219 g_return_val_if_fail (proxy != NULL, FALSE);
220
221 if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
222 len = sizeof (error);
223 _lm_sock_get_error (connect_data->fd, &error, &len);
224 _lm_old_socket_failed_with_error (connect_data, error);
225 return FALSE;
226 } else if (condition & G_IO_OUT) {
227 if (!proxy_negotiate (lm_connection_get_proxy (connection), connect_data->fd, lm_connection_get_server (connection), lm_connection_get_port (connection))) {
228 _lm_old_socket_failed (connect_data);
229 return FALSE;
230 }
231
232 proxy->io_watch = g_io_add_watch (connect_data->io_channel,
233 G_IO_IN|G_IO_ERR,
234 (GIOFunc) proxy_read_cb,
235 connect_data);
236 } else {
237 g_assert_not_reached ();
238 }
239
240 return FALSE;
241 }
242
243 /**
244 * lm_proxy_new
245 * @type: the type of the new proxy
246 *
247 * Creates a new Proxy. Used #lm_connection_set_proxy to make a connection
248 * user this proxy.
249 *
250 * Return value: a newly create proxy
251 **/
252 LmProxy *
lm_proxy_new(LmProxyType type)253 lm_proxy_new (LmProxyType type)
254 {
255 LmProxy *proxy;
256
257 proxy = g_new0 (LmProxy, 1);
258
259 proxy->ref_count = 1;
260 proxy->type = type;
261
262 switch (proxy->type) {
263 case LM_PROXY_TYPE_HTTP:
264 proxy->port = 8000;
265 break;
266 default:
267 proxy->port = 0;
268 }
269
270 return proxy;
271 }
272
273 /**
274 * lm_proxy_new_with_server
275 * @type: the type of the new proxy
276 * @server: the proxy server
277 * @port: the proxy server port
278 *
279 * Creates a new Proxy. Use #lm_connection_set_proxy to make a connection
280 * user this proxy.
281 *
282 * Return value: a newly create proxy
283 **/
284 LmProxy *
lm_proxy_new_with_server(LmProxyType type,const gchar * server,guint port)285 lm_proxy_new_with_server (LmProxyType type,
286 const gchar *server,
287 guint port)
288 {
289 LmProxy *proxy;
290
291 proxy = lm_proxy_new (type);
292 lm_proxy_set_server (proxy, server);
293 lm_proxy_set_port (proxy, port);
294
295 return proxy;
296 }
297
298 /**
299 * lm_proxy_get_type
300 * @proxy: an #LmProxy
301 *
302 * Fetches the proxy type
303 *
304 * Return value: the type
305 **/
306 LmProxyType
lm_proxy_get_type(LmProxy * proxy)307 lm_proxy_get_type (LmProxy *proxy)
308 {
309 g_return_val_if_fail (proxy != NULL, LM_PROXY_TYPE_NONE);
310
311 return proxy->type;
312 }
313
314 /**
315 * lm_proxy_set_type
316 * @proxy: an #LmProxy
317 * @type: an #LmProxyType
318 *
319 * Sets the proxy type for @proxy to @type.
320 **/
321 void
lm_proxy_set_type(LmProxy * proxy,LmProxyType type)322 lm_proxy_set_type (LmProxy *proxy, LmProxyType type)
323 {
324 g_return_if_fail (proxy != NULL);
325
326 proxy->type = type;
327 }
328
329 /**
330 * lm_proxy_get_server:
331 * @proxy: an #LmProxy
332 *
333 * Fetches the server address that @proxy is using.
334 *
335 * Return value: the proxy server address
336 **/
337 const gchar *
lm_proxy_get_server(LmProxy * proxy)338 lm_proxy_get_server (LmProxy *proxy)
339 {
340 g_return_val_if_fail (proxy != NULL, NULL);
341
342 return proxy->server;
343 }
344
345 /**
346 * lm_proxy_set_server:
347 * @proxy: an #LmProxy
348 * @server: Address of the proxy server
349 *
350 * Sets the server address for @proxy to @server.
351 **/
352 void
lm_proxy_set_server(LmProxy * proxy,const gchar * server)353 lm_proxy_set_server (LmProxy *proxy, const gchar *server)
354 {
355 g_return_if_fail (proxy != NULL);
356 g_return_if_fail (server != NULL);
357
358 g_free (proxy->server);
359 proxy->server = _lm_utils_hostname_to_punycode (server);
360 }
361
362 /**
363 * lm_proxy_get_port:
364 * @proxy: an #LmProxy
365 *
366 * Fetches the port that @proxy is using.
367 *
368 * Return value: The port
369 **/
370 guint
lm_proxy_get_port(LmProxy * proxy)371 lm_proxy_get_port (LmProxy *proxy)
372 {
373 g_return_val_if_fail (proxy != NULL, 0);
374
375 return proxy->port;
376 }
377
378 /**
379 * lm_proxy_set_port:
380 * @proxy: an #LmProxy
381 * @port: proxy server port
382 *
383 * Sets the server port that @proxy will be using.
384 **/
385 void
lm_proxy_set_port(LmProxy * proxy,guint port)386 lm_proxy_set_port (LmProxy *proxy, guint port)
387 {
388 g_return_if_fail (proxy != NULL);
389
390 proxy->port = port;
391 }
392
393 /**
394 * lm_proxy_get_username:
395 * @proxy: an #LmProxy
396 *
397 * Fetches the username that @proxy is using.
398 *
399 * Return value: the username
400 **/
401 const gchar *
lm_proxy_get_username(LmProxy * proxy)402 lm_proxy_get_username (LmProxy *proxy)
403 {
404 g_return_val_if_fail (proxy != NULL, NULL);
405
406 return proxy->username;
407 }
408
409 /**
410 * lm_proxy_set_username:
411 * @proxy: an #LmProxy
412 * @username: Username
413 *
414 * Sets the username for @proxy to @username or %NULL to unset.
415 **/
416 void
lm_proxy_set_username(LmProxy * proxy,const gchar * username)417 lm_proxy_set_username (LmProxy *proxy, const gchar *username)
418 {
419 g_return_if_fail (proxy != NULL);
420
421 g_free (proxy->username);
422
423 if (username) {
424 proxy->username = g_strdup (username);
425 } else {
426 proxy->username = NULL;
427 }
428 }
429 /**
430 * lm_proxy_get_password:
431 * @proxy: an #LmProxy
432 *
433 * Fetches the password that @proxy is using.
434 *
435 * Return value: the proxy password
436 **/
437 const gchar *
lm_proxy_get_password(LmProxy * proxy)438 lm_proxy_get_password (LmProxy *proxy)
439 {
440 g_return_val_if_fail (proxy != NULL, NULL);
441
442 return proxy->password;
443 }
444
445 /**
446 * lm_proxy_set_password:
447 * @proxy: an #LmProxy
448 * @password: Password
449 *
450 * Sets the password for @proxy to @password or %NULL to unset.
451 **/
452 void
lm_proxy_set_password(LmProxy * proxy,const gchar * password)453 lm_proxy_set_password (LmProxy *proxy, const gchar *password)
454 {
455 g_return_if_fail (proxy != NULL);
456
457 g_free (proxy->password);
458
459 if (password) {
460 proxy->password = g_strdup (password);
461 } else {
462 proxy->password = NULL;
463 }
464 }
465
466 /**
467 * lm_proxy_ref:
468 * @proxy: an #LmProxy
469 *
470 * Adds a reference to @proxy.
471 *
472 * Return value: the proxy
473 **/
474 LmProxy *
lm_proxy_ref(LmProxy * proxy)475 lm_proxy_ref (LmProxy *proxy)
476 {
477 g_return_val_if_fail (proxy != NULL, NULL);
478
479 proxy->ref_count++;
480 return proxy;
481 }
482
483 /**
484 * lm_proxy_unref
485 * @proxy: an #LmProxy
486 *
487 * Removes a reference from @proxy. When no more references are present
488 * @proxy is freed.
489 **/
490 void
lm_proxy_unref(LmProxy * proxy)491 lm_proxy_unref (LmProxy *proxy)
492 {
493 g_return_if_fail (proxy != NULL);
494
495 proxy->ref_count--;
496
497 if (proxy->ref_count == 0) {
498 proxy_free (proxy);
499 }
500 }
501