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