root/src/noit_listener.c

Revision 730f9ad3be25f3cd31c49dcc5613df7f0aa6bc08, 14.8 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

const warning, refs #34

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