ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/modules/httpd/main.c
Revision: 3389
Committed: Fri Apr 25 14:12:15 2014 UTC (11 years, 4 months ago) by michael
Content type: text/x-csrc
File size: 30720 byte(s)
Log Message:
- Imported ircservices-5.1.24

File Contents

# Content
1 /* Main HTTP server module.
2 *
3 * IRC Services is copyright (c) 1996-2009 Andrew Church.
4 * E-mail: <achurch@achurch.org>
5 * Parts written by Andrew Kempe and others.
6 * This program is free but copyrighted software; see the file GPL.txt for
7 * details.
8 */
9
10 #include "services.h"
11 #include "modules.h"
12 #include "conffile.h"
13 #include "timeout.h"
14 #include "http.h"
15
16 #include <netdb.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19
20 /*************************************************************************/
21
22 static int cb_auth = -1;
23 static int cb_request = -1;
24
25
26 static int32 ListenBacklog;
27 static int32 RequestBufferSize;
28 static int32 MaxConnections;
29 static int32 MaxRequests;
30 static time_t IdleTimeout;
31 static int LogConnections;
32
33 /* List of ports to listen to (set by ListenTo configuration directive) */
34 static struct listento_ {
35 char ip[16]; /* aaa.bbb.ccc.ddd\0 */
36 uint16 port;
37 } *ListenTo;
38 static int ListenTo_count;
39 #define MAX_LISTENTO 32767
40
41
42 /* Array of listen sockets (corresponding to ListenTo[] entries) */
43 static Socket **listen_sockets;
44
45 /* Array of clients */
46 static Client *clients;
47 int clients_count;
48
49 /*************************************************************************/
50
51 static void do_accept(Socket *listener, void *param);
52 static void do_disconnect(Socket *socket, void *param_unused);
53 static void do_readline(Socket *s, void *param_unused);
54 static void do_readdata(Socket *s, void *param);
55 static void do_timeout(Timeout *t);
56
57 static Client *find_client(Socket *s);
58 static void set_timeout(Client *c);
59 static void clear_timeout(Client *c);
60 static void parse_header(Client *c, char *linestart);
61 static void handle_request(Client *c);
62
63 /*************************************************************************/
64 /*************************** HTTP server core ****************************/
65 /*************************************************************************/
66
67 /* Accept a connection and perform IP address checks on it; if it doesn't
68 * pass, disconnect it.
69 */
70
71 static void do_accept(Socket *listener, void *param)
72 {
73 Socket *new = param;
74 struct sockaddr_in sin;
75 int sin_len = sizeof(sin);
76
77 if (sock_remote(new, (struct sockaddr *)&sin, &sin_len) < 0) {
78 module_log_perror("sock_remote() failed");
79 } else if (sin_len > sizeof(sin)) {
80 module_log("sock_remote() returned oversize address (%d)", sin_len);
81 } else if (sin.sin_family != AF_INET) {
82 module_log("sock_remote() returned bad address family (%d)",
83 sin.sin_family);
84 } else {
85 int i = clients_count;
86 ARRAY_EXTEND(clients);
87 snprintf(clients[i].address, sizeof(clients[i].address), "%s:%u",
88 unpack_ip((uint8 *)&sin.sin_addr), ntohs(sin.sin_port));
89 clients[i].socket = new;
90 clients[i].ip = sin.sin_addr.s_addr;
91 clients[i].port = sin.sin_port;
92 clients[i].timeout = NULL;
93 clients[i].request_count = 0;
94 clients[i].in_request = 0;
95 clients[i].request_buf = smalloc(RequestBufferSize);
96 clients[i].request_len = 0;
97 clients[i].version_major = 0;
98 clients[i].version_minor = 0;
99 clients[i].method = -1;
100 clients[i].url = NULL;
101 clients[i].data = NULL;
102 clients[i].data_len = 0;
103 clients[i].headers = NULL;
104 clients[i].headers_count = 0;
105 clients[i].variables = NULL;
106 clients[i].variables_count = 0;
107 if (clients_count >= MaxConnections) {
108 module_log("Dropping connection (exceeded MaxConnections: %d)"
109 " from %s", MaxConnections, clients[i].address);
110 http_error(&clients[i], HTTP_F_SERVICE_UNAVAILABLE, NULL);
111 /* http_error() closes socket for us, so don't try to
112 * disconnect it below */
113 } else {
114 set_timeout(&clients[i]);
115 sock_setcb(new, SCB_READLINE, do_readline);
116 sock_setcb(new, SCB_DISCONNECT, do_disconnect);
117 sock_set_blocking(new, 1);
118 if (LogConnections)
119 module_log("Accepted connection from %s",
120 clients[i].address);
121 }
122 return;
123 }
124 disconn(new);
125 }
126
127 /*************************************************************************/
128
129 static void do_disconnect(Socket *socket, void *param_unused)
130 {
131 Client *c = find_client(socket);
132 int index = c - clients;
133
134 if (!c) {
135 module_log("BUG: unexpected disconnect callback for socket %p",socket);
136 return;
137 }
138 clear_timeout(c);
139 free(c->headers);
140 free(c->variables);
141 free(c->request_buf);
142 ARRAY_REMOVE(clients, index);
143 }
144
145 /*************************************************************************/
146
147 /* Read a line from a client socket. If the end of the request data is
148 * reached, the request is passed to handle_request(); if the request data
149 * length exceeds the buffer size, an error is sent to the client and the
150 * connection is closed.
151 */
152
153 static void do_readline(Socket *socket, void *param_unused)
154 {
155 Client *c = find_client(socket);
156 char line[HTTP_LINEMAX], *linestart, *s;
157 int32 i;
158
159 if (!c) {
160 module_log("BUG: unexpected readline callback for socket %p", socket);
161 disconn(socket);
162 return;
163 }
164
165 if (!sgets(line, sizeof(line), socket) || *line == 0) {
166 module_log("BUG: sgets() failed in readline callback for socket %p",
167 socket);
168 return;
169 }
170 i = strlen(line);
171 if (line[i-1] != '\n') {
172 module_log("%s: Request/header line too long, closing connection",
173 c->address);
174 http_error(c, HTTP_E_REQUEST_ENTITY_TOO_LARGE, NULL);
175 return;
176 }
177 line[--i] = 0;
178 if (i > 0 && line[i-1] == '\r')
179 line[--i] = 0;
180 i++; /* include trailing \0 */
181 if (c->request_len + i > RequestBufferSize) {
182 module_log("%s: Request too large, closing connection", c->address);
183 http_error(c, HTTP_E_REQUEST_ENTITY_TOO_LARGE, NULL);
184 return;
185 }
186 linestart = c->request_buf + c->request_len;
187 memcpy(linestart, line, i);
188 c->request_len += i;
189
190 if (!c->url) {
191 char *method, *url, *version;
192 if (!*linestart) {
193 /* RFC2616 4.2: servers SHOULD ignore initial empty lines (OK) */
194 c->request_len = 0;
195 set_timeout(c);
196 return;
197 }
198 method = strtok(linestart, " ");
199 url = strtok(NULL, " ");
200 version = strtok(NULL, " ");
201 if (!method || !url || !version) {
202 /* Note that we don't support REALLY old clients (HTTP 0.9)
203 * which don't send version strings */
204 /* RFC2616 10.4: SHOULD ensure client has received error before
205 * closing connection (NG) -- in this case the client is so
206 * broken that it doesn't deserve to be worried about */
207 module_log("%s: Invalid HTTP request", c->address);
208 http_error(c, HTTP_E_BAD_REQUEST, NULL);
209 return;
210 }
211 if (strcmp(method, "GET") == 0) {
212 c->method = METHOD_GET;
213 } else if (strcmp(method, "HEAD") == 0) {
214 c->method = METHOD_HEAD;
215 } else if (strcmp(method, "POST") == 0) {
216 c->method = METHOD_POST;
217 } else {
218 module_log("%s: Unimplemented/unsupported method `%s' requested",
219 c->address, method);
220 http_error(c, HTTP_F_NOT_IMPLEMENTED, NULL);
221 return;
222 }
223 if (strncmp(version, "HTTP/", 5) != 0 || !(s = strchr(version+5,'.'))){
224 module_log("%s: Bad HTTP version string: %s", c->address, version);
225 http_error(c, HTTP_E_BAD_REQUEST, NULL);
226 return;
227 }
228 *s++ = 0;
229 c->version_major = (int)atolsafe(version+5, 0, INT_MAX);
230 c->version_minor = (int)atolsafe(s, 0, INT_MAX);
231 if (c->version_major < 0 || c->version_minor < 0) {
232 module_log("%s: Bad HTTP version string: %s.%s",
233 c->address, version, s);
234 http_error(c, HTTP_E_BAD_REQUEST, NULL);
235 return;
236 }
237 if (c->version_major != 1) {
238 module_log("%s: Unsupported HTTP version: %d.%d",
239 c->address, c->version_major, c->version_minor);
240 http_error(c, HTTP_F_HTTP_VER_NOT_SUPPORTED, NULL);
241 return;
242 }
243 if (strnicmp(url, "http://", 7) == 0) {
244 /* RFC2616 5.1.2: MUST accept absolute URIs (OK, but we ignore
245 * the hostname and just pretend it's us) */
246 s = strchr(url+7, '/');
247 if (s) {
248 strmove(url, s);
249 } else {
250 url[0] = '/';
251 url[1] = 0;
252 }
253 }
254 c->url = url;
255 set_timeout(c);
256 return;
257 } /* if (!url) */
258
259 /* We already have the URL, so this must be a header or blank line. */
260 if (*linestart) {
261 /* Header line: process it */
262 parse_header(c, linestart);
263 set_timeout(c);
264 return;
265 }
266
267 /* End of headers. For GET/HEAD, handle any query string present in
268 * the URL and process the request immediately. For POST, handle
269 * Expect: 100-continue, then deal with the body. */
270
271 if (c->method == METHOD_GET || c->method == METHOD_HEAD) {
272 char *s = strchr(c->url, '?');
273 if (s) {
274 *s++ = 0;
275 c->data = s;
276 c->data_len = strlen(s);
277 }
278 handle_request(c);
279 } else if (c->method == METHOD_POST) {
280 long length;
281 s = http_get_header(c, "Content-Length");
282 if (!s) {
283 module_log("%s: Missing Content-Length header for POST",
284 c->address);
285 http_error(c, HTTP_E_LENGTH_REQUIRED, NULL);
286 return;
287 }
288 errno = 0;
289 length = atolsafe(s, 0, LONG_MAX);
290 if (length < 0) {
291 module_log("%s: Invalid Content-Length header: %s", c->address, s);
292 http_error(c, HTTP_E_BAD_REQUEST, NULL);
293 return;
294 } else if (c->request_len+length>RequestBufferSize) {
295 module_log("%s: Request too large, closing connection",c->address);
296 http_error(c, HTTP_E_REQUEST_ENTITY_TOO_LARGE, NULL);
297 return;
298 }
299 c->data = c->request_buf + c->request_len;
300 c->data_len = (int32)length;
301 if (length > 0) {
302 s = http_get_header(c, "Expect");
303 for (s = strtok(s, ", \t"); s; s = strtok(NULL, ", \t")) {
304 if (strcmp(s, "100-continue") == 0) {
305 sockprintf(socket, "HTTP/1.1 100 Continue\r\n\r\n");
306 break;
307 }
308 }
309 /* Set up to read POST data */
310 sock_setcb(socket, SCB_READ, do_readdata);
311 sock_setcb(socket, SCB_READLINE, NULL);
312 set_timeout(c);
313 } else {
314 /* length == 0: do it just like GET */
315 handle_request(c);
316 }
317 } else {
318 module_log("BUG: do_readline(): unsupported method %d", c->method);
319 http_error(c, HTTP_F_INTERNAL_SERVER_ERROR, NULL);
320 }
321
322 }
323
324 /*************************************************************************/
325
326 /* Read data from a client socket. When the end of the data is reached,
327 * reached, the request is passed to handle_request(). The request is
328 * assumed to have already been checked for exceeding the buffer size.
329 */
330
331 static void do_readdata(Socket *socket, void *param)
332 {
333 Client *c = find_client(socket);
334 int32 available = (int32)(long)param, needed, nread;
335
336 if (!c) {
337 module_log("BUG: unexpected readdata callback for socket %p", socket);
338 disconn(socket);
339 return;
340 }
341
342 needed = c->data_len - (c->request_len - (c->data - c->request_buf));
343 if (available > needed)
344 available = needed;
345 if (c->request_len + available > RequestBufferSize) {
346 module_log("BUG: do_readdata(%s[%s]): data size exceeded buffer limit",
347 c->address, c->url);
348 http_error(c, HTTP_F_INTERNAL_SERVER_ERROR, NULL);
349 return;
350 }
351 nread = sread(socket, c->request_buf + c->request_len, available);
352 if (nread != available) {
353 module_log("BUG: do_readdata(%s[%s]): nread (%d) != available (%d)",
354 c->address, c->url, nread, available);
355 }
356 c->request_len += nread;
357 needed -= nread;
358 if (needed <= 0) {
359 /* Prepare for next request, if any */
360 sock_setcb(socket, SCB_READ, NULL);
361 sock_setcb(socket, SCB_READLINE, do_readline);
362 /* Process this request */
363 handle_request(c);
364 }
365 }
366
367 /*************************************************************************/
368
369 /* Handle an idle timeout on a client. */
370
371 static void do_timeout(Timeout *t)
372 {
373 Client *c = find_client(t->data);
374 if (!c) {
375 module_log("BUG: do_timeout(): client not found for timeout %p!", t);
376 return;
377 }
378 c->timeout = NULL;
379 disconn(c->socket);
380 }
381
382 /*************************************************************************/
383 /*************************************************************************/
384
385 /* Return the client structure corresponding to the given socket, or NULL
386 * if not found.
387 */
388
389 static Client *find_client(Socket *s)
390 {
391 int i;
392
393 ARRAY_FOREACH (i, clients) {
394 if (clients[i].socket == s)
395 return &clients[i];
396 }
397 return NULL;
398 }
399
400 /*************************************************************************/
401
402 /* Start an idle timer running on the given client. If a timer was already
403 * running, clear it and start a new one.
404 */
405
406 static void set_timeout(Client *c)
407 {
408 if (!c->socket) {
409 module_log("BUG: attempt to set timeout for client %d with no"
410 " socket!", (int)(c-clients));
411 return;
412 }
413 if (IdleTimeout) {
414 clear_timeout(c);
415 c->timeout = add_timeout(IdleTimeout, do_timeout, 0);
416 c->timeout->data = c->socket;
417 }
418 }
419
420 /*************************************************************************/
421
422 /* Cancel the idle timer for the given client. */
423
424 static void clear_timeout(Client *c)
425 {
426 if (c->timeout) {
427 del_timeout(c->timeout);
428 c->timeout = NULL;
429 }
430 }
431
432 /*************************************************************************/
433
434 /* Do appropriate things with the given header line. */
435
436 static void parse_header(Client *c, char *linestart)
437 {
438 char *s;
439
440 /* Check for whitespace-started line and no headers yet */
441 if ((*linestart == ' ' || *linestart == '\t') && !c->headers_count) {
442 http_error(c, HTTP_E_BAD_REQUEST, NULL);
443 return;
444 }
445
446 /* Remove all trailing whitespace */
447 s = linestart + strlen(linestart) - 1;
448 while (s > linestart && (*s == ' ' || *s == '\t')) {
449 *s-- = 0;
450 c->request_len--;
451 }
452
453 if (*linestart == ' ' || *linestart == '\t') {
454 /* If it starts with whitespace, just tack it onto the end of the
455 * previous line (convert all leading whitespace to a single space) */
456 linestart[-1] = ' ';
457 s = linestart;
458 while (*s == ' ' || *s == '\t')
459 s++;
460 strmove(linestart, s);
461 c->request_len -= s-linestart;
462 } else {
463 /* New header: split into name and value, and create a new
464 * headers[] entry */
465 int i = c->headers_count;
466 ARRAY_EXTEND(c->headers);
467 c->headers[i] = linestart;
468 s = strchr(linestart, ':');
469 if (!s) {
470 http_error(c, HTTP_E_BAD_REQUEST, NULL);
471 return;
472 }
473 *s++ = 0;
474 linestart = s;
475 while (*s == ' ' || *s == '\t')
476 s++;
477 strmove(linestart, s);
478 c->request_len -= s - linestart;
479 }
480 }
481
482 /*************************************************************************/
483
484 /* Parse the data given by `buf' (null-terminated) into variables and
485 * create an array of them in c->variables. Assume standard
486 * x-www-form-urlencoding encoding (for multipart data, use
487 * parse_data_multipart() below).
488 */
489
490 static void parse_data(Client *c, char *buf)
491 {
492 char *start;
493 int found_equals = 0;
494 char hexbuf[3];
495
496 hexbuf[2] = 0;
497 free(c->variables);
498 c->variables = NULL;
499 c->variables_count = 0;
500 start = buf;
501
502 ARRAY_EXTEND(c->variables);
503 c->variables[0] = start;
504 while (*buf) {
505 switch (*buf) {
506 case '=':
507 if (!found_equals) {
508 *buf = 0;
509 http_unquote_url(start);
510 found_equals = 1;
511 start = buf+1;
512 }
513 break;
514 case '&':
515 *buf = 0;
516 http_unquote_url(start);
517 found_equals = 0;
518 start = buf+1;
519 ARRAY_EXTEND(c->variables);
520 c->variables[c->variables_count-1] = start;
521 break;
522 }
523 buf++;
524 }
525 }
526
527 /*************************************************************************/
528
529 /* Parse the data given by `buf' (null-terminated) into variables and
530 * create an array of them in c->variables, using the boundary string
531 * given by `boundary'.
532 */
533
534 static void parse_data_multipart(Client *c, char *buf, const char *boundary)
535 {
536 char *dest = buf;
537 int boundarylen = strlen(boundary);
538 char *varname = NULL;
539
540 free(c->variables);
541 c->variables = NULL;
542 c->variables_count = 0;
543
544 buf = strstr(buf, boundary);
545 if (!buf)
546 return; /* boundary string not found */
547
548 while (*buf && (buf[boundarylen+2] != '-' || buf[boundarylen+3] != '-')) {
549 char *s;
550
551 /* Read in header for this part */
552 s = buf + strcspn(buf, "\r\n");
553 if (!*s)
554 return;
555 buf = s + strspn(s, "\r") + 1;
556 while (*buf != '\r' && *buf != '\n') {
557 s = buf + strcspn(buf, "\r\n");
558 if (!*s)
559 return;
560 if (*s == '\r')
561 *s++ = 0;
562 *s++ = 0;
563 if (strnicmp(buf,"Content-Disposition:",20) == 0) {
564 buf += 20;
565 while (*buf && isspace(*buf))
566 buf++;
567 if (*buf && strnicmp(buf,"form-data;",10) == 0) {
568 buf += 10;
569 while (*buf && isspace(*buf))
570 buf++;
571 if (*buf && strnicmp(buf,"name=",5) == 0) {
572 buf += 5;
573 if (*buf == '"') {
574 char *t = strchr(++buf, '"');
575 if (t)
576 *t = 0;
577 } else {
578 char *t = strchr(buf, ';');
579 if (t)
580 *t = 0;
581 }
582 varname = dest;
583 strmove(dest, buf);
584 dest += strlen(buf)+1;
585 }
586 }
587 }
588 buf = s;
589 } /* while (*buf != '\r' && *buf != '\n') */
590 if (*buf == '\r')
591 buf++;
592 buf++;
593
594 /* Read in data (variable contents) */
595 if (varname) {
596 ARRAY_EXTEND(c->variables);
597 c->variables[c->variables_count-1] = varname;
598 varname = NULL;
599 }
600 s = buf + strcspn(buf, "\r\n");
601 if (s > buf) {
602 memmove(dest, buf, s-buf);
603 dest += s-buf;
604 }
605 if (*s == '\r')
606 s++;
607 buf = s;
608 /* *buf is always pointing to a \n or \0 at the top of this loop */
609 while (*buf && (buf[1] != '-' || buf[2] != '-'
610 || strncmp(buf+3, boundary, boundarylen) != 0)) {
611 s = buf+1 + strcspn(buf+1, "\r\n");
612 if (!s)
613 s = buf + strlen(buf);
614 memmove(dest, buf, s-buf);
615 dest += s-buf;
616 if (*s == '\r')
617 s++;
618 buf = s;
619 }
620 /* Null-terminate variable contents */
621 *dest++ = 0;
622 /* Skip over newline */
623 if (*buf)
624 buf++;
625 } /* while not final boundary line */
626 }
627
628 /*************************************************************************/
629
630 static void handle_request(Client *c)
631 {
632 int res;
633 int close = 0;
634
635 /* Parse GET query string or POST data into variables */
636 if (c->data && c->data_len) {
637 char *s;
638 if (c->method == METHOD_POST) {
639 /* There were at least two newlines before the beginning of the
640 * data, so it's safe to move it back a byte (to add a trailing
641 * null) */
642 memmove(c->data-1, c->data, c->data_len);
643 c->data--;
644 c->data[c->data_len] = 0;
645 }
646 /* Check the content type, and extract the boundary string if it's
647 * multipart data */
648 s = http_get_header(c, "Content-Type");
649 if (s && strnicmp(s, "multipart/form-data;", 20) == 0) {
650 s += 20;
651 while (isspace(*s))
652 s++;
653 if (strnicmp(s, "boundary=", 9) == 0) {
654 s += 9;
655 if (*s == '"') {
656 char *t = strchr(++s, '"');
657 if (t)
658 *t = 0;
659 }
660 } else {
661 s = NULL;
662 }
663 } else {
664 s = NULL;
665 }
666 /* Parse data into variables */
667 if (s)
668 parse_data_multipart(c, c->data, s);
669 else
670 parse_data(c, c->data);
671 }
672
673 c->request_count++;
674 c->in_request = 1;
675
676 if (c->version_major == 1 && c->version_minor == 0) {
677 close = 1;
678 } else {
679 const char *s = http_get_header(c, "Connection");
680 if (s && strstr(s, "close")) {
681 /* This might accidentally trigger on something like "abcloseyz",
682 * but that's okay; all it means is the client has to reconnect */
683 close = 1;
684 }
685 }
686
687 res = call_callback_2(cb_auth, c, &close);
688 if (res < 0) {
689 module_log("handle_request(): call_callback(cb_request) failed");
690 http_error(c, HTTP_F_INTERNAL_SERVER_ERROR, NULL);
691 close = 1;
692 } else if (res != HTTP_AUTH_DENY) {
693 res = call_callback_2(cb_request, c, &close);
694 if (res < 0) {
695 module_log("handle_request(): call_callback(cb_request) failed");
696 http_error(c, HTTP_F_INTERNAL_SERVER_ERROR, NULL);
697 close = 1;
698 } else if (res == 0) {
699 http_error(c, HTTP_E_NOT_FOUND, NULL);
700 }
701 }
702
703 if (close || (MaxRequests && c->request_count >= MaxRequests)
704 || c->in_request < 0 /* flag from http_error */
705 ) {
706 disconn(c->socket);
707 } else {
708 free(c->headers);
709 free(c->variables);
710 c->in_request = 0;
711 c->request_len = 0;
712 c->version_major = 0;
713 c->version_minor = 0;
714 c->method = -1;
715 c->url = NULL;
716 c->data = NULL;
717 c->data_len = 0;
718 c->headers = NULL;
719 c->headers_count = 0;
720 c->variables = NULL;
721 c->variables_count = 0;
722 set_timeout(c);
723 }
724 }
725
726 /*************************************************************************/
727 /***************************** Module stuff ******************************/
728 /*************************************************************************/
729
730 static int do_ListenTo(const char *filename, int linenum, char *param);
731
732 ConfigDirective module_config[] = {
733 { "IdleTimeout", { { CD_TIME, 0, &IdleTimeout } } },
734 { "ListenBacklog", { { CD_POSINT, CF_DIRREQ, &ListenBacklog } } },
735 { "ListenTo", { { CD_FUNC, CF_DIRREQ, do_ListenTo } } },
736 { "LogConnections", { { CD_SET, 0, &LogConnections } } },
737 { "MaxConnections", { { CD_POSINT, 0, &MaxConnections } } },
738 { "MaxRequests", { { CD_POSINT, 0, &MaxRequests } } },
739 { "RequestBufferSize",{ { CD_POSINT, 0, &RequestBufferSize } } },
740 { NULL }
741 };
742
743 /*************************************************************************/
744
745 static int do_ListenTo(const char *filename, int linenum, char *param)
746 {
747 char *s;
748 int port;
749 uint8 *ip;
750 char *ipstr;
751 char ipbuf[15+1]; /* aaa.bbb.ccc.ddd\0 */
752 int recursing = 0, i;
753 static struct listento_ *new_ListenTo;
754 static int new_ListenTo_count;
755
756 if (!filename) {
757 /* filename == NULL, perform special operations */
758 switch (linenum) {
759 case CDFUNC_INIT: /* prepare to read new data */
760 free(new_ListenTo);
761 new_ListenTo = NULL;
762 new_ListenTo_count = 0;
763 break;
764 case CDFUNC_SET: /* store new data in config variable */
765 free(ListenTo);
766 ListenTo = new_ListenTo;
767 ListenTo_count = new_ListenTo_count;
768 new_ListenTo = NULL;
769 new_ListenTo_count = 0;
770 break;
771 case CDFUNC_DECONFIG: /* clear any stored data */
772 free(ListenTo);
773 ListenTo = NULL;
774 ListenTo_count = 0;
775 break;
776 } /* switch (linenum) */
777 return 1;
778 } /* if (!filename) */
779
780 /* filename != NULL, process directive */
781
782 if (linenum < 0) {
783 recursing = 1;
784 linenum = -linenum;
785 }
786
787 if (ListenTo_count >= MAX_LISTENTO) {
788 config_error(filename, linenum,
789 "Too many ListenTo addresses (maximum %d)",
790 MAX_LISTENTO);
791 return 0;
792 }
793
794 s = strchr(param, ':');
795 if (!s) {
796 config_error(filename, linenum,
797 "ListenTo address requires both address and port");
798 return 0;
799 }
800
801 *s++ = 0;
802 port = atolsafe(s, 1, 65535);
803 if (port < 1) {
804 config_error(filename, linenum, "Invalid port number `%s'", s);
805 return 0;
806 }
807
808 if (strcmp(param, "*") == 0) {
809 /* "*" -> all addresses (NULL string) */
810 ipstr = NULL;
811 } else if ((ip = pack_ip(param)) != NULL) {
812 /* IP address -> normalize (no leading zeros, etc.) */
813 snprintf(ipbuf, sizeof(ipbuf), "%u.%u.%u.%u",
814 ip[0], ip[1], ip[2], ip[3]);
815 if (strlen(ipbuf) > 15) {
816 config_error(filename, linenum, "BUG: strlen(ipbuf) > 15 [%s]",
817 ipbuf);
818 return 0;
819 }
820 ipstr = ipbuf;
821 } else {
822 /* hostname -> check for double recursion, then look up and
823 * recursively add addresses */
824 #ifdef HAVE_GETHOSTBYNAME
825 struct hostent *hp;
826 #endif
827 if (recursing) {
828 config_error(filename, linenum, "BUG: double recursion (param=%s)",
829 param);
830 return 0;
831 }
832 #ifdef HAVE_GETHOSTBYNAME
833 if ((hp = gethostbyname(param)) != NULL) {
834 if (hp->h_addrtype == AF_INET) {
835 for (i = 0; hp->h_addr_list[i]; i++) {
836 ip = (uint8 *)hp->h_addr_list[i];
837 snprintf(ipbuf, sizeof(ipbuf), "%u.%u.%u.%u",
838 ip[0], ip[1], ip[2], ip[3]);
839 if (strlen(ipbuf) > 15) {
840 config_error(filename, linenum,
841 "BUG: strlen(ipbuf) > 15 [%s]", ipbuf);
842 return 0;
843 }
844 if (!do_ListenTo(filename, -linenum, ipbuf))
845 return 0;
846 }
847 return 1; /* Success */
848 } else {
849 config_error(filename, linenum, "%s: no IPv4 addresses found",
850 param);
851 }
852 } else {
853 config_error(filename, linenum, "%s: %s", param,
854 hstrerror(h_errno));
855 }
856 #else
857 config_error(filename, linenum,
858 "gethostbyname() not available, hostnames may not be"
859 " used");
860 #endif
861 return 0;
862 }
863
864 i = new_ListenTo_count;
865 ARRAY_EXTEND(new_ListenTo);
866 if (ipstr)
867 strcpy(new_ListenTo[i].ip, ipstr);/*safe: strlen(ip)<16 checked above*/
868 else
869 memset(new_ListenTo[i].ip, 0, sizeof(new_ListenTo[i].ip));
870 new_ListenTo[i].port = port;
871 return 1;
872 }
873
874 /*************************************************************************/
875
876 int init_module()
877 {
878 int i, opencount;
879
880 cb_auth = register_callback("auth");
881 cb_request = register_callback("request");
882 if (cb_auth < 0 || cb_request < 0) {
883 module_log("Unable to register callbacks");
884 exit_module(0);
885 return 0;
886 }
887
888 listen_sockets = smalloc(sizeof(*listen_sockets) * ListenTo_count);
889 opencount = 0;
890 ARRAY_FOREACH (i, ListenTo) {
891 listen_sockets[i] = sock_new();
892 if (listen_sockets[i]) {
893 if (open_listener(listen_sockets[i],
894 *ListenTo[i].ip ? ListenTo[i].ip : NULL,
895 ListenTo[i].port, ListenBacklog) == 0) {
896 sock_setcb(listen_sockets[i], SCB_ACCEPT, do_accept);
897 module_log("Listening on %s:%u",
898 ListenTo[i].ip, ListenTo[i].port);
899 opencount++;
900 } else {
901 module_log_perror("Failed to open listen socket for %s:%u",
902 ListenTo[i].ip, ListenTo[i].port);
903 }
904 } else {
905 module_log("Failed to create listen socket for %s:%u",
906 *ListenTo[i].ip ? ListenTo[i].ip : "*",
907 ListenTo[i].port);
908 }
909 }
910 if (!opencount) {
911 module_log("No ports could be opened, aborting");
912 return 0;
913 }
914
915 return 1;
916 }
917
918 /*************************************************************************/
919
920 int exit_module(int shutdown_unused)
921 {
922 int i;
923
924 ARRAY_FOREACH (i, ListenTo) {
925 if (listen_sockets[i]) {
926 close_listener(listen_sockets[i]);
927 sock_free(listen_sockets[i]);
928 }
929 }
930 free(ListenTo);
931 ListenTo = NULL;
932 ListenTo_count = 0;
933 free(listen_sockets);
934 listen_sockets = NULL;
935
936 unregister_callback(cb_request);
937 unregister_callback(cb_auth);
938
939 return 1;
940 }
941
942 /*************************************************************************/
943
944 /*
945 * Local variables:
946 * c-file-style: "stroustrup"
947 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
948 * indent-tabs-mode: nil
949 * End:
950 *
951 * vim: expandtab shiftwidth=4:
952 */