Provide access to more Network attributes.
[jelmer/ctrlproxy.git] / src / listener.c
1 /* 
2         ctrlproxy: A modular IRC proxy
3         (c) 2005-2006 Jelmer Vernooij <jelmer@nl.linux.org>
4         
5         Manual listen on ports
6
7         This program is free software; you can redistribute it and/or modify
8         it under the terms of the GNU General Public License as published by
9         the Free Software Foundation; either version 3 of the License, or
10         (at your option) any later version.
11
12         This program is distributed in the hope that it will be useful,
13         but WITHOUT ANY WARRANTY; without even the implied warranty of
14         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15         GNU General Public License for more details.
16
17         You should have received a copy of the GNU General Public License
18         along with this program; if not, write to the Free Software
19         Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "internals.h"
23 #include "irc.h"
24 #include "listener.h"
25 #include "socks.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <glib.h>
30 #include <sys/un.h>
31
32 #ifdef HAVE_SYS_SOCKET_H
33 #include <sys/socket.h>
34 #endif
35
36
37 void default_listener_log_fn(enum log_level l, const struct irc_listener *listener, const char *ret)
38 {
39         if (listener->network != NULL)
40                 network_log(l, listener->network, "%s", ret);
41         else
42                 log_global(l, "%s", ret);
43 }
44
45 #ifdef HAVE_GSSAPI
46 gboolean default_socks_gssapi(struct pending_client *cl, gss_name_t authn_name)
47 {
48         /* FIXME: Check if principal matches own user name */
49         return FALSE;
50 }
51 #endif
52
53 static gboolean listener_check_username_password(struct irc_listener *listener, 
54                                                                                                  const char *username, const char *password)
55 {
56         GList *gl;
57
58         if (listener->config != NULL) {
59                 for (gl = listener->config->allow_rules; gl; gl = gl->next)
60                 {
61                         struct allow_rule *r = gl->data;
62
63                         if (r->password == NULL || r->username == NULL) 
64                                 continue;
65
66                         if (strcmp(r->username, username)) 
67                                 continue;
68
69                         if (strcmp(r->password, password))
70                                 continue;
71
72                         return TRUE;
73                 }
74
75                 if (listener->config->password != NULL) {
76                         return (strcmp(listener->config->password, password) == 0);
77                 }
78         }
79
80         if (listener->global->config->password != NULL) {
81                 return (strcmp(listener->global->config->password, password) == 0);
82         }
83
84         listener_log(LOG_WARNING, listener, "No password set, allowing client _without_ authentication!");
85         return TRUE;
86 }
87
88
89 gboolean default_socks_auth_simple(struct pending_client *cl, const char *username, const char *password,
90                                                                    gboolean (*on_known)(struct pending_client *, gboolean))
91 {
92         if (listener_check_username_password(cl->listener, username, password))
93                 return on_known(cl, TRUE);
94
95         return on_known(cl, FALSE);
96 }
97
98 gboolean default_socks_connect_fqdn (struct pending_client *cl, const char *hostname, uint16_t port)
99 {
100         char *desc;
101         struct irc_network *result;
102         struct network_config *nc;
103         
104         listener_log(LOG_INFO, cl->listener, "Request to connect to %s:%d", hostname, port);
105
106         result = find_network_by_hostname(cl->listener->global, hostname, port, 
107                                                                           cl->listener->global->config->create_implicit, NULL);
108
109         if (result == NULL) {
110                 listener_log(LOG_WARNING, cl->listener, "Unable to return network matching %s:%d", hostname, port);
111                 return listener_socks_error(cl, REP_NET_UNREACHABLE);
112         } 
113
114         if (result->connection.state == NETWORK_CONNECTION_STATE_NOT_CONNECTED && 
115                 !connect_network(result)) {
116                 network_log(LOG_ERROR, result, "Unable to connect");
117                 return listener_socks_error(cl, REP_NET_UNREACHABLE);
118         }
119
120         nc = result->private_data;
121
122         if (nc->type == NETWORK_TCP) {
123                 struct sockaddr *name; 
124                 int atyp, len, port;
125                 gchar *data;
126
127                 name = (struct sockaddr *)result->connection.data.tcp.local_name;
128
129                 if (name != NULL && name->sa_family == AF_INET) {
130                         struct sockaddr_in *name4 = (struct sockaddr_in *)name;
131                         atyp = ATYP_IPV4;
132                         data = (gchar *)&name4->sin_addr;
133                         len = 4;
134                         port = name4->sin_port;
135                 } else if (name != NULL && name->sa_family == AF_INET6) {
136                         struct sockaddr_in6 *name6 = (struct sockaddr_in6 *)name;
137                         atyp = ATYP_IPV6;
138                         data = (gchar *)&name6->sin6_addr;
139                         len = 16;
140                         port = name6->sin6_port;
141                 } else {
142                         network_log(LOG_ERROR, result, "Unable to obtain local address for connection to server");
143                         return listener_socks_error(cl, REP_NET_UNREACHABLE);
144                 }
145                         
146                 listener_socks_reply(cl, REP_OK, atyp, len, data, port); 
147                 
148         } else {
149                 gchar *data = g_strdup("xlocalhost");
150                 data[0] = strlen(data+1);
151                 
152                 listener_socks_reply(cl, REP_OK, ATYP_FQDN, data[0]+1, data, 1025);
153         }
154
155         desc = g_io_channel_ip_get_description(cl->connection);
156         if (desc == NULL) {
157                 if (cl->listener == cl->listener->global->unix_domain_socket_listener) 
158                         desc = g_strdup("Unix domain socket socks client");
159                 else
160                         desc = g_strdup("socks client");
161         }
162         client_init_iochannel(result, cl->connection, desc);
163         g_free(desc);
164
165         return FALSE;
166 }
167
168 void listener_log(enum log_level l, const struct irc_listener *listener,
169                                  const char *fmt, ...)
170 {
171         char *ret;
172         va_list ap;
173
174         if (listener->log_fn == NULL)
175                 return;
176
177         g_assert(listener);
178         g_assert(fmt);
179
180         va_start(ap, fmt);
181         ret = g_strdup_vprintf(fmt, ap);
182         va_end(ap);
183
184         listener->log_fn(l, listener, ret);
185
186         g_free(ret);
187 }
188
189 static gboolean handle_client_line(struct pending_client *pc, const struct irc_line *l)
190 {
191         struct irc_listener *listener = pc->listener;
192
193         if (l == NULL || l->args[0] == NULL) { 
194                 return TRUE;
195         }
196
197         if (!g_strcasecmp(l->args[0], "PASS")) {
198                 const char *networkname = NULL;
199                 struct irc_network *n = listener->network;
200                 gboolean authenticated = FALSE;
201                 char *actual_password;
202
203                 if (strchr(l->args[1], ':') != NULL) {
204                         char *p = strchr(l->args[1], ':');
205                         actual_password = g_strndup(l->args[1], p-l->args[1]);
206                         networkname = p+1;
207                 } else {
208                         actual_password = g_strdup(l->args[1]);
209                         networkname = NULL;
210                 }
211
212                 authenticated = listener_check_username_password(listener, NULL, actual_password);
213
214                 g_free(actual_password);
215
216                 if (authenticated) {
217                         listener_log(LOG_INFO, listener, "Client successfully authenticated");
218
219                         if (networkname != NULL) {
220                                 n = find_network_by_hostname(listener->global, 
221                                                                                          networkname, 6667, listener->global->config->create_implicit,
222                                                                                          NULL);
223                                 if (n == NULL) {
224                                         irc_sendf(pc->connection, listener->iconv, NULL, 
225                                                           ":%s %d %s :Password error: unable to find network", 
226                                                           get_my_hostname(), ERR_PASSWDMISMATCH, "*");
227                                         g_io_channel_flush(pc->connection, NULL);
228                                         return FALSE;
229                                 }
230
231                                 if (n->connection.state == NETWORK_CONNECTION_STATE_NOT_CONNECTED && 
232                                         !connect_network(n)) {
233                                         irc_sendf(pc->connection, listener->iconv, NULL, 
234                                                           ":%s %d %s :Password error: unable to connect", 
235                                                           get_my_hostname(), ERR_PASSWDMISMATCH, "*");
236                                         g_io_channel_flush(pc->connection, NULL);
237                                         return FALSE;
238                                 }
239                         }
240
241                         irc_sendf(pc->connection, listener->iconv, NULL, 
242                                           "NOTICE AUTH :PASS OK");
243                         g_io_channel_flush(pc->connection, NULL);
244
245
246                         {
247                                 char *desc = g_io_channel_ip_get_description(pc->connection);
248                                 if (pc->listener == pc->listener->global->unix_domain_socket_listener) 
249                                         desc = g_strdup("Unix domain socket client");
250                                 else if (desc == NULL)
251                                         desc = g_strdup("");
252                                 client_init_iochannel(n, pc->connection, desc);
253                                 g_free(desc);
254                         }
255
256                         return FALSE;
257                 } else {
258                         GIOStatus status;
259                         listener_log(LOG_WARNING, listener, 
260                                                  "User tried to log in with incorrect password!");
261
262                         status = irc_sendf(pc->connection, listener->iconv, NULL, 
263                                                            ":%s %d %s :Password mismatch", 
264                                                            get_my_hostname(), ERR_PASSWDMISMATCH, "*");
265                         g_io_channel_flush(pc->connection, NULL);
266
267                         if (status != G_IO_STATUS_NORMAL) {
268                                 return FALSE;
269                         }
270
271                         return TRUE;
272                 }
273         } else {
274                 irc_sendf(pc->connection, listener->iconv, NULL, ":%s %d %s :You are not registered. Did you specify a password?", get_my_hostname(), ERR_NOTREGISTERED, "*");
275                 g_io_channel_flush(pc->connection, NULL);
276         }
277
278         return TRUE;
279 }
280
281
282 static int next_autoport(struct global *global)
283 {
284         return ++global->config->listener_autoport;
285 }
286
287 void free_listener(struct irc_listener *l)
288 {
289         l->global->listeners = g_list_remove(l->global->listeners, l);
290
291         irc_network_unref(l->network);
292         
293         g_free(l);
294 }
295
296 static struct irc_listener_ops default_listener_ops = {
297         .handle_client_line = handle_client_line,
298         .socks_auth_simple = default_socks_auth_simple,
299 #ifdef HAVE_GSSAPI
300         .socks_gssapi = default_socks_gssapi,
301 #endif
302         .socks_connect_fqdn = default_socks_connect_fqdn,
303 };
304
305 struct irc_listener *listener_init(struct global *global, struct listener_config *cfg)
306 {
307         struct irc_listener *l = g_new0(struct irc_listener, 1);
308
309         l->config = cfg;
310         l->ssl = cfg->ssl;
311         l->ssl_credentials = cfg->ssl_credentials;
312         g_assert(!l->ssl || l->ssl_credentials != NULL);
313         l->global = global;
314         l->ops = &default_listener_ops;
315         l->iconv = (GIConv)-1;
316         l->log_fn = default_listener_log_fn;
317
318         if (l->config->network != NULL) {
319                 l->network = irc_network_ref(find_network(global->networks, l->config->network));
320                 if (l->network == NULL) {
321                         listener_log(LOG_WARNING, l, "Network `%s' for listener not found", l->config->network);
322                 }
323         }
324
325         l->global->listeners = g_list_append(l->global->listeners, l);
326
327         return l;
328 }
329
330 static void auto_add_listener(struct irc_network *n, void *private_data)
331 {
332         GList *gl;
333         struct irc_listener *l;
334         struct listener_config *cfg;
335         struct network_config *nc = n->private_data;
336         
337         /* See if there is already a listener for n */
338         for (gl = n->global->listeners; gl; gl = gl->next) {
339                 l = gl->data;
340
341                 if (l->network == n || l->network == NULL)
342                         return;
343         }
344
345         cfg = g_new0(struct listener_config, 1);
346         cfg->network = g_strdup(nc->name);
347         cfg->port = g_strdup_printf("%d", next_autoport(n->global));
348         l = listener_init(n->global, cfg);
349         listener_start_tcp(l, NULL, cfg->port);
350 }
351
352 gboolean init_listeners(struct global *global)
353 {
354         GList *gl;
355         gboolean ret = TRUE;
356
357         if (global->config->auto_listener)
358                 register_new_network_notify(global, auto_add_listener, NULL);
359
360         for (gl = global->config->listeners; gl; gl = gl->next) {
361                 struct listener_config *cfg = gl->data;
362                 struct irc_listener *l = listener_init(global, cfg);
363
364                 if (l != NULL) {
365                         ret &= listener_start_tcp(l, cfg->address, cfg->port);
366                 }
367         }
368         return ret;
369 }
370
371 void fini_listeners(struct global *global)
372 {
373         GList *gl;
374         for(gl = global->listeners; gl; gl = gl->next) {
375                 struct irc_listener *l = gl->data;
376
377                 if (l->active) 
378                         listener_stop(l);
379         }
380 }
381
382 gboolean start_unix_domain_socket_listener(struct global *global)
383 {
384         int sock;
385         struct sockaddr_un un;
386         struct irc_listener *l = g_new0(struct irc_listener, 1);
387         GIOChannel *ioc;
388
389         sock = socket(PF_UNIX, SOCK_STREAM, 0);
390         if (sock < 0) {
391                 log_global(LOG_ERROR, "error creating unix socket: %s", strerror(errno));
392                 g_free(l);
393                 return FALSE;
394         }
395         
396         un.sun_family = AF_UNIX;
397         strncpy(un.sun_path, global->config->network_socket, sizeof(un.sun_path));
398         unlink(un.sun_path);
399
400         if (bind(sock, (struct sockaddr *)&un, sizeof(un)) < 0) {
401                 log_global(LOG_ERROR, "unable to bind to %s: %s", un.sun_path, strerror(errno));
402                 g_free(l);
403                 return FALSE;
404         }
405         
406         if (listen(sock, 5) < 0) {
407                 log_global(LOG_ERROR, "error listening on socket: %s", strerror(errno));
408                 g_free(l);
409                 return FALSE;
410         }
411
412         l->ops = &default_listener_ops;
413         l->config = NULL;
414         l->iconv = (GIConv)-1;
415         l->ssl = FALSE;
416         l->ssl_credentials = NULL;
417         l->global = global;
418         l->log_fn = default_listener_log_fn;
419
420         ioc = g_io_channel_unix_new(sock);
421
422         if (ioc == NULL) {
423                 log_global(LOG_ERROR, "Unable to create GIOChannel for unix server socket");
424                 return FALSE;
425         }
426
427         listener_add_iochannel(l, ioc, NULL, NULL);
428
429         global->unix_domain_socket_listener = l;
430
431         return TRUE;
432 }
433
434 gboolean stop_unix_domain_socket_listener(struct global *global)
435 {
436         if (global->unix_domain_socket_listener != NULL)
437                 listener_stop(global->unix_domain_socket_listener);
438         unlink(global->config->network_socket);
439         return TRUE;
440 }