root/src/noit_listener.c

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

support unix domain sockets as well

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  */
5
6 #include "noit_defines.h"
7
8 #include <unistd.h>
9 #include <errno.h>
10 #include <sys/time.h>
11 #include <sys/socket.h>
12 #include <sys/ioctl.h>
13 #include <netinet/in.h>
14 #include <sys/un.h>
15 #include <arpa/inet.h>
16
17 #include "eventer/eventer.h"
18 #include "utils/noit_log.h"
19 #include "noit_listener.h"
20 #include "noit_conf.h"
21
22 static int
23 noit_listener_acceptor(eventer_t e, int mask,
24                        void *closure, struct timeval *tv) {
25   int conn, newmask = EVENTER_READ;
26   socklen_t salen;
27   listener_closure_t listener_closure = (listener_closure_t)closure;
28   union {
29     struct sockaddr_in addr4;
30     struct sockaddr_in6 addr6;
31     struct sockaddr_un unix;
32   } s;
33
34   if(mask & EVENTER_EXCEPTION) {
35     eventer_remove_fd(e->fd);
36     close(e->fd);
37     return 0;
38   }
39
40   conn = e->opset->accept(e->fd, (struct sockaddr *)&s, &salen, &newmask, e);
41   if(conn >= 0) {
42     socklen_t on = 1;
43     eventer_t newe;
44     if(ioctl(conn, FIONBIO, &on)) {
45       close(conn);
46       goto accept_bail;
47     }
48     newe = eventer_alloc();
49     newe->fd = conn;
50     newe->mask = EVENTER_READ | EVENTER_WRITE | EVENTER_EXCEPTION;
51     newe->callback = listener_closure->dispatch_callback;
52     newe->closure = listener_closure->dispatch_closure;
53     eventer_add(newe);
54   }
55  accept_bail:
56   return newmask | EVENTER_EXCEPTION;
57 }
58
59 int
60 noit_listener(char *host, unsigned short port, int type,
61               int backlog, eventer_func_t handler, void *closure) {
62   int rv, fd;
63   int8_t family;
64   int sockaddr_len;
65   socklen_t on;
66   long reuse;
67   listener_closure_t listener_closure;
68   eventer_t event;
69   union {
70     struct in_addr addr4;
71     struct in6_addr addr6;
72   } a;
73   union {
74     struct sockaddr_in addr4;
75     struct sockaddr_in6 addr6;
76     struct sockaddr_un unix;
77   } s;
78   const char *event_name;
79
80   noitL(noit_debug, "noit_listener(%s, %d, %d, %d, %s, %p)\n",
81         host, port, type, backlog,
82         (event_name = eventer_name_for_callback(handler))?event_name:"??",
83         closure);
84   if(host[0] == '/') {
85     family = AF_UNIX;
86   }
87   else {
88     family = AF_INET;
89     rv = inet_pton(family, host, &a);
90     if(rv != 1) {
91       family = AF_INET6;
92       rv = inet_pton(family, host, &a);
93       if(rv != 1) {
94         if(!strcmp(host, "*")) {
95           family = AF_INET;
96           a.addr4.s_addr = INADDR_ANY;
97         } else {
98           noitL(noit_stderr, "Cannot translate '%s' to IP\n", host);
99           return -1;
100         }
101       }
102     }
103   }
104
105   fd = socket(family, type, 0);
106   if(fd < 0) return -1;
107
108   on = 1;
109   if(ioctl(fd, FIONBIO, &on)) {
110     close(fd);
111     return -1;
112   }
113
114   reuse = 1;
115   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
116                  (void*)&reuse, sizeof(reuse)) != 0) {
117
118     close(fd);
119     return -1;
120   }
121
122   memset(&s, 0, sizeof(s));
123   if(family == AF_UNIX) {
124     struct stat sb;
125     /* unlink the path iff it is a socket */
126     if(stat(host, &sb) == -1) {
127       if(errno != ENOENT) {
128         noitL(noit_stderr, "%s: %s\n", host, strerror(errno));
129         close(fd);
130         return -1;
131       }
132     }
133     else {
134       if(sb.st_mode & S_IFSOCK)
135         unlink(host);
136       else {
137         noitL(noit_stderr, "unlink %s failed: %s\n", host, strerror(errno));
138         close(fd);
139         return -1;
140       }
141     }
142     strncpy(s.unix.sun_path, host, sizeof(s.unix.sun_path)-1);
143     sockaddr_len = sizeof(s.unix);
144   }
145   else {
146     s.addr6.sin6_family = family;
147     s.addr6.sin6_port = htons(port);
148     memcpy(&s.addr6.sin6_addr, &a, sizeof(a));
149     sockaddr_len = (family == AF_INET) ?  sizeof(s.addr4) : sizeof(s.addr6);
150   }
151   if(bind(fd, (struct sockaddr *)&s, sockaddr_len) < 0) {
152     noitL(noit_stderr, "bind failed[%s]: %s\n", host, strerror(errno));
153     close(fd);
154     return -1;
155   }
156
157   if(type == SOCK_STREAM) {
158     if(listen(fd, backlog) < 0) {
159       close(fd);
160       return -1;
161     }
162   }
163
164   listener_closure = calloc(1, sizeof(*listener_closure));
165   listener_closure->family = family;
166   listener_closure->port = htons(port);
167   listener_closure->dispatch_callback = handler;
168   listener_closure->dispatch_closure = closure;
169
170   event = eventer_alloc();
171   event->fd = fd;
172   event->mask = EVENTER_READ | EVENTER_EXCEPTION;
173   event->callback = noit_listener_acceptor;
174   event->closure = listener_closure;
175
176   eventer_add(event);
177   return 0;
178 }
179
180 void
181 noit_listener_init() {
182   int i, cnt = 0;
183   noit_conf_section_t *listener_configs;
184
185   listener_configs = noit_conf_get_sections(NULL, "/noit/listeners/listener",
186                                             &cnt);
187   noitL(noit_stderr, "Found %d /noit/listeners/listener stanzas\n", cnt);
188   for(i=0; i<cnt; i++) {
189     char address[256];
190     char type[256];
191     unsigned short port;
192     int portint;
193     int backlog;
194     eventer_func_t f;
195
196     if(!noit_conf_get_stringbuf(listener_configs[i],
197                                 "type", type, sizeof(type))) {
198       noitL(noit_stderr, "No type specified in listener stanza %d\n", i+1);
199       continue;
200     }
201     f = eventer_callback_for_name(type);
202     if(!f) {
203       noitL(noit_stderr,
204             "Cannot find handler for listener type: '%s'\n", type);
205       continue;
206     }
207     if(!noit_conf_get_stringbuf(listener_configs[i],
208                                 "address", address, sizeof(address))) {
209       address[0] = '*';
210       address[1] = '\0';
211     }
212     if(!noit_conf_get_int(listener_configs[i], "port", &portint))
213       portint = 0;
214     port = (unsigned short) portint;
215     if(address[0] != '/' && (portint == 0 || (port != portint))) {
216       /* UNIX sockets don't require a port (they'll ignore it if specified */
217       noitL(noit_stderr,
218             "Invalid port [%d] specified in stanza %d\n", port, i+1);
219       continue;
220     }
221     if(!noit_conf_get_int(listener_configs[i], "backlog", &backlog))
222       backlog = 5;
223
224     noit_listener(address, port, SOCK_STREAM, backlog, f, NULL);
225   }
226 }
Note: See TracBrowser for help on using the browser.