root/src/noit_listener.c

Revision 8fd52de5ff1b33c4e303bd9c73fcd27898353757, 14.8 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

fixes #134

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34
35 #include <unistd.h>
36 #include <errno.h>
37 #include <sys/time.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/ioctl.h>
41 #include <netinet/in.h>
42 #include <sys/un.h>
43 #include <arpa/inet.h>
44
45 #include "eventer/eventer.h"
46 #include "utils/noit_log.h"
47 #include "noit_listener.h"
48 #include "noit_conf.h"
49
50 static noit_hash_table listener_commands = NOIT_HASH_EMPTY;
51 noit_hash_table *
52 noit_listener_commands() {
53   return &listener_commands;
54 }
55
56 void
57 acceptor_closure_free(acceptor_closure_t *ac) {
58   if(ac->remote_cn) free(ac->remote_cn);
59   free(ac);
60 }
61
62 static int
63 noit_listener_accept_ssl(eventer_t e, int mask,
64                          void *closure, struct timeval *tv) {
65   int rv;
66   listener_closure_t listener_closure = (listener_closure_t)closure;
67   acceptor_closure_t *ac = NULL;
68   if(!closure) goto socketfail;
69   ac = listener_closure->dispatch_closure;
70
71   rv = eventer_SSL_accept(e, &mask);
72   if(rv > 0) {
73     eventer_ssl_ctx_t *sslctx;
74     e->callback = listener_closure->dispatch_callback;
75     /* We must make a copy of the acceptor_closure_t for each new
76      * connection.
77      */
78     if((sslctx = eventer_get_eventer_ssl_ctx(e)) != NULL) {
79       char *cn, *end;
80       cn = eventer_ssl_get_peer_subject(sslctx);
81       if(cn && (cn = strstr(cn, "CN=")) != NULL) {
82         cn += 3;
83         end = cn;
84         while(*end && *end != '/') end++;
85         ac->remote_cn = malloc(end - cn + 1);
86         memcpy(ac->remote_cn, cn, end - cn);
87         ac->remote_cn[end-cn] = '\0';
88       }
89     }
90     e->closure = ac;
91     return e->callback(e, mask, e->closure, tv);
92   }
93   if(errno == EAGAIN) return mask|EVENTER_EXCEPTION;
94
95  socketfail:
96   if(listener_closure) free(listener_closure);
97   if(ac) acceptor_closure_free(ac);
98   eventer_remove_fd(e->fd);
99   e->opset->close(e->fd, &mask, e);
100   return 0;
101 }
102
103 static int
104 noit_listener_acceptor(eventer_t e, int mask,
105                        void *closure, struct timeval *tv) {
106   int conn, newmask = EVENTER_READ;
107   socklen_t salen;
108   listener_closure_t listener_closure = (listener_closure_t)closure;
109   acceptor_closure_t *ac = NULL;
110
111   if(mask & EVENTER_EXCEPTION) {
112  socketfail:
113     if(ac) acceptor_closure_free(ac);
114     /* We don't shut down the socket, it's out listener! */
115     return EVENTER_READ | EVENTER_WRITE | EVENTER_EXCEPTION;
116   }
117
118   ac = malloc(sizeof(*ac));
119   memcpy(ac, listener_closure->dispatch_closure, sizeof(*ac));
120   salen = sizeof(ac->remote);
121   conn = e->opset->accept(e->fd, &ac->remote.remote_addr, &salen, &newmask, e);
122   if(conn >= 0) {
123     socklen_t on = 1;
124     eventer_t newe;
125     if(ioctl(conn, FIONBIO, &on)) {
126       close(conn);
127       goto accept_bail;
128     }
129     newe = eventer_alloc();
130     newe->fd = conn;
131     newe->mask = EVENTER_READ | EVENTER_WRITE | EVENTER_EXCEPTION;
132
133     if(listener_closure->sslconfig->size) {
134       const char *cert, *key, *ca, *ciphers;
135       eventer_ssl_ctx_t *ctx;
136       /* We have an SSL configuration.  While our socket accept is
137        * complete, we now have to SSL_accept, which could require
138        * several reads and writes and needs its own event callback.
139        */
140 #define SSLCONFGET(var,name) do { \
141   if(!noit_hash_retr_str(listener_closure->sslconfig, name, strlen(name), \
142                          &var)) var = NULL; } while(0)
143       SSLCONFGET(cert, "certificate_file");
144       SSLCONFGET(key, "key_file");
145       SSLCONFGET(ca, "ca_chain");
146       SSLCONFGET(ciphers, "ciphers");
147       ctx = eventer_ssl_ctx_new(SSL_SERVER, cert, key, ca, ciphers);
148       if(!ctx) {
149         newe->opset->close(newe->fd, &newmask, e);
150         eventer_free(newe);
151         goto socketfail;
152       }
153       eventer_ssl_ctx_set_verify(ctx, eventer_ssl_verify_cert,
154                                  listener_closure->sslconfig);
155       EVENTER_ATTACH_SSL(newe, ctx);
156       newe->callback = noit_listener_accept_ssl;
157       newe->closure = malloc(sizeof(*listener_closure));
158       memcpy(newe->closure, listener_closure, sizeof(*listener_closure));
159       ((listener_closure_t)newe->closure)->dispatch_closure = ac;
160     }
161     else {
162       newe->callback = listener_closure->dispatch_callback;
163       /* We must make a copy of the acceptor_closure_t for each new
164        * connection.
165        */
166       newe->closure = ac;
167     }
168     eventer_add(newe);
169   }
170  accept_bail:
171   return newmask | EVENTER_EXCEPTION;
172 }
173
174 int
175 noit_listener(char *host, unsigned short port, int type,
176               int backlog, noit_hash_table *sslconfig,
177               noit_hash_table *config,
178               eventer_func_t handler, void *service_ctx) {
179   int rv, fd;
180   int8_t family;
181   int sockaddr_len;
182   socklen_t on, reuse;
183   listener_closure_t listener_closure;
184   eventer_t event;
185   union {
186     struct in_addr addr4;
187     struct in6_addr addr6;
188   } a;
189   union {
190     struct sockaddr_in addr4;
191     struct sockaddr_in6 addr6;
192     struct sockaddr_un addru;
193   } s;
194   const char *event_name;
195
196   noitL(noit_debug, "noit_listener(%s, %d, %d, %d, %s, %p)\n",
197         host, port, type, backlog,
198         (event_name = eventer_name_for_callback(handler))?event_name:"??",
199         service_ctx);
200   if(host[0] == '/') {
201     family = AF_UNIX;
202   }
203   else {
204     family = AF_INET;
205     rv = inet_pton(family, host, &a);
206     if(rv != 1) {
207       family = AF_INET6;
208       rv = inet_pton(family, host, &a);
209       if(rv != 1) {
210         if(!strcmp(host, "*")) {
211           family = AF_INET;
212           a.addr4.s_addr = INADDR_ANY;
213         } else {
214           noitL(noit_stderr, "Cannot translate '%s' to IP\n", host);
215           return -1;
216         }
217       }
218     }
219   }
220
221   fd = socket(family, type, 0);
222   if(fd < 0) {
223     noitL(noit_stderr, "Cannot create socket: %s\n", strerror(errno));
224     return -1;
225   }
226
227   on = 1;
228   if(ioctl(fd, FIONBIO, &on)) {
229     close(fd);
230     noitL(noit_stderr, "Cannot make socket non-blocking: %s\n",
231           strerror(errno));
232     return -1;
233   }
234
235   reuse = 1;
236   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
237                  (void*)&reuse, sizeof(reuse)) != 0) {
238     close(fd);
239     noitL(noit_stderr, "Cannot set SO_REUSEADDR: %s\n", strerror(errno));
240     return -1;
241   }
242
243   memset(&s, 0, sizeof(s));
244   if(family == AF_UNIX) {
245     struct stat sb;
246     /* unlink the path iff it is a socket */
247     if(stat(host, &sb) == -1) {
248       if(errno != ENOENT) {
249         noitL(noit_stderr, "%s: %s\n", host, strerror(errno));
250         close(fd);
251         return -1;
252       }
253     }
254     else {
255       if(sb.st_mode & S_IFSOCK)
256         unlink(host);
257       else {
258         noitL(noit_stderr, "unlink %s failed: %s\n", host, strerror(errno));
259         close(fd);
260         return -1;
261       }
262     }
263     strncpy(s.addru.sun_path, host, sizeof(s.addru.sun_path)-1);
264     sockaddr_len = sizeof(s.addru);
265   }
266   else {
267     s.addr6.sin6_family = family;
268     s.addr6.sin6_port = htons(port);
269     memcpy(&s.addr6.sin6_addr, &a, sizeof(a));
270     sockaddr_len = (family == AF_INET) ?  sizeof(s.addr4) : sizeof(s.addr6);
271   }
272   if(bind(fd, (struct sockaddr *)&s, sockaddr_len) < 0) {
273     noitL(noit_stderr, "bind failed[%s]: %s\n", host, strerror(errno));
274     close(fd);
275     return -1;
276   }
277
278   if(type == SOCK_STREAM) {
279     if(listen(fd, backlog) < 0) {
280       close(fd);
281       return -1;
282     }
283   }
284
285   listener_closure = calloc(1, sizeof(*listener_closure));
286   listener_closure->family = family;
287   listener_closure->port = htons(port);
288   listener_closure->sslconfig = calloc(1, sizeof(noit_hash_table));
289   noit_hash_merge_as_dict(listener_closure->sslconfig, sslconfig);
290   listener_closure->dispatch_callback = handler;
291
292   listener_closure->dispatch_closure =
293     calloc(1, sizeof(*listener_closure->dispatch_closure));
294   listener_closure->dispatch_closure->config = config;
295   listener_closure->dispatch_closure->dispatch = handler;
296   listener_closure->dispatch_closure->service_ctx = service_ctx;
297
298   event = eventer_alloc();
299   event->fd = fd;
300   event->mask = EVENTER_READ | EVENTER_EXCEPTION;
301   event->callback = noit_listener_acceptor;
302   event->closure = listener_closure;
303
304   eventer_add(event);
305   return 0;
306 }
307
308 void
309 noit_listener_reconfig(const char *toplevel) {
310   int i, cnt = 0;
311   noit_conf_section_t *listener_configs;
312   char path[256];
313
314   snprintf(path, sizeof(path), "/%s/listeners//listener",
315            toplevel ? toplevel : "*");
316   listener_configs = noit_conf_get_sections(NULL, path, &cnt);
317   noitL(noit_stderr, "Found %d %s stanzas\n", cnt, path);
318   for(i=0; i<cnt; i++) {
319     char address[256];
320     char type[256];
321     unsigned short port;
322     int portint;
323     int backlog;
324     eventer_func_t f;
325     noit_boolean ssl;
326     noit_hash_table *sslconfig, *config;
327
328     if(!noit_conf_get_stringbuf(listener_configs[i],
329                                 "ancestor-or-self::node()/@type",
330                                 type, sizeof(type))) {
331       noitL(noit_stderr, "No type specified in listener stanza %d\n", i+1);
332       continue;
333     }
334     f = eventer_callback_for_name(type);
335     if(!f) {
336       noitL(noit_stderr,
337             "Cannot find handler for listener type: '%s'\n", type);
338       continue;
339     }
340     if(!noit_conf_get_stringbuf(listener_configs[i],
341                                 "ancestor-or-self::node()/@address",
342                                 address, sizeof(address))) {
343       address[0] = '*';
344       address[1] = '\0';
345     }
346     if(!noit_conf_get_int(listener_configs[i],
347                           "ancestor-or-self::node()/@port", &portint))
348       portint = 0;
349     port = (unsigned short) portint;
350     if(address[0] != '/' && (portint == 0 || (port != portint))) {
351       /* UNIX sockets don't require a port (they'll ignore it if specified */
352       noitL(noit_stderr,
353             "Invalid port [%d] specified in stanza %d\n", port, i+1);
354       continue;
355     }
356     if(!noit_conf_get_int(listener_configs[i],
357                           "ancestor-or-self::node()/@backlog", &backlog))
358       backlog = 5;
359
360     if(!noit_conf_get_boolean(listener_configs[i],
361                               "ancestor-or-self::node()/@ssl", &ssl))
362      ssl = noit_false;
363
364     sslconfig = ssl ?
365                   noit_conf_get_hash(listener_configs[i], "sslconfig") :
366                   NULL;
367     config = noit_conf_get_hash(listener_configs[i], "config");
368
369     if(noit_listener(address, port, SOCK_STREAM, backlog,
370                      sslconfig, config, f, NULL) != 0) {
371       if(sslconfig) {
372         noit_hash_destroy(sslconfig,free,free);
373         free(sslconfig);
374       }
375       noit_hash_destroy(config,free,free);
376       free(config);
377     }
378   }
379   free(listener_configs);
380 }
381 int
382 noit_control_dispatch(eventer_t e, int mask, void *closure,
383                       struct timeval *now) {
384   u_int32_t cmd;
385   int len;
386   void *vdelegation_table;
387   noit_hash_table *delegation_table = NULL;
388   acceptor_closure_t *ac = closure;
389
390   len = e->opset->read(e->fd, &cmd, sizeof(cmd), &mask, e);
391
392   if(len == -1 && errno == EAGAIN)
393     return EVENTER_READ | EVENTER_EXCEPTION;
394
395   if(mask & EVENTER_EXCEPTION || len != sizeof(cmd)) {
396     int newmask;
397 socket_error:
398     /* Exceptions cause us to simply snip the connection */
399     eventer_remove_fd(e->fd);
400     e->opset->close(e->fd, &newmask, e);
401     if(ac) acceptor_closure_free(ac);
402     return 0;
403   }
404
405   ac->cmd = ntohl(cmd);
406   /* Lookup cmd and dispatch */
407   if(noit_hash_retrieve(&listener_commands,
408                         (char *)&ac->dispatch, sizeof(ac->dispatch),
409                         (void **)&vdelegation_table)) {
410     void *vfunc;
411     delegation_table = (noit_hash_table *)vdelegation_table;
412     if(noit_hash_retrieve(delegation_table,
413                           (char *)&ac->cmd, sizeof(ac->cmd), &vfunc)) {
414       e->callback = *((eventer_func_t *)vfunc);
415       return e->callback(e, mask, closure, now);
416     }
417     else {
418     const char *event_name;
419       noitL(noit_error, "listener (%s %p) has no command: 0x%8x\n",
420             (event_name = eventer_name_for_callback(ac->dispatch))?event_name:"???",
421             delegation_table, cmd);
422     }
423   }
424   else {
425     const char *event_name;
426     noitL(noit_error, "No delegation table for listener (%s %p)\n",
427           (event_name = eventer_name_for_callback(ac->dispatch))?event_name:"???",
428           delegation_table);
429   }
430   goto socket_error;
431 }
432 void
433 noit_control_dispatch_delegate(eventer_func_t listener_dispatch,
434                                u_int32_t cmd,
435                                eventer_func_t delegate_dispatch) {
436   u_int32_t *cmd_copy;
437   eventer_func_t *handler_copy;
438   void *vdelegation_table;
439   noit_hash_table *delegation_table;
440   if(!noit_hash_retrieve(&listener_commands,
441                          (char *)&listener_dispatch, sizeof(listener_dispatch),
442                          &vdelegation_table)) {
443     delegation_table = calloc(1, sizeof(*delegation_table));
444     handler_copy = malloc(sizeof(*handler_copy));
445     *handler_copy = listener_dispatch;
446     noit_hash_store(&listener_commands,
447                     (char *)handler_copy, sizeof(*handler_copy),
448                     delegation_table);
449   }
450   else
451     delegation_table = (noit_hash_table *)vdelegation_table;
452
453   cmd_copy = malloc(sizeof(*cmd_copy));
454   *cmd_copy = cmd;
455   handler_copy = malloc(sizeof(*handler_copy));
456   *handler_copy = delegate_dispatch;
457   noit_hash_replace(delegation_table,
458                     (char *)cmd_copy, sizeof(*cmd_copy),
459                     handler_copy,
460                     free, free);
461 }
462
463 void
464 noit_listener_init(const char *toplevel) {
465   eventer_name_callback("noit_listener_acceptor", noit_listener_acceptor);
466   eventer_name_callback("noit_listener_accept_ssl", noit_listener_accept_ssl);
467   eventer_name_callback("control_dispatch", noit_control_dispatch);
468   noit_listener_reconfig(toplevel);
469 }
470
Note: See TracBrowser for help on using the browser.