2 MiddleMan filtering proxy server
3 Copyright (C) 2002 Jason McLaughlin
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include <sys/types.h>
31 load <external> section from XML config into a linked list
33 EXTERNAL *external_load(EXTERNAL * external, XML_LIST * xml_list)
35 EXTERNAL *tmp_list = external;
36 struct EXTERNAL_LIST_LIST *external_list = NULL;
38 if (tmp_list == NULL) {
39 tmp_list = xmalloc(sizeof(EXTERNAL));
40 tmp_list->external_list = NULL;
42 tmp_list->enabled = TRUE;
46 pthread_rwlock_init(&tmp_list->lock, NULL);
48 external_list = tmp_list->external_list;
50 while ((xml_list = xml_section(xml_list, "<external>"))) {
51 XML_LIST_LOOP(xml_list, "<external>") {
52 XML_LIST_CMP(xml_list, "<item>") {
53 external_list = external_list_new(external_list);
54 external_list->id = external->id++;
56 if (tmp_list->external_list == NULL)
57 tmp_list->external_list = external_list;
58 XML_LIST_LOOP(xml_list, "<item>") {
59 XML_LIST_CMP(xml_list, "<enabled>") {
60 xml_list = xml_list->next;
61 if (xml_list->type == XML_VALUE) {
62 if (!strcasecmp(xml_list->item, "false"))
63 external_list->enabled = FALSE;
65 external_list->enabled = TRUE;
68 XML_LIST_CMP(xml_list, "<comment>") {
69 xml_list = xml_list->next;
70 if (xml_list->type == XML_VALUE)
71 external_list_insert(external_list, xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL);
73 XML_LIST_CMP(xml_list, "<mime>") {
74 xml_list = xml_list->next;
75 if (xml_list->type == XML_VALUE)
76 external_list_insert(external_list, NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL);
78 XML_LIST_CMP(xml_list, "<host>") {
79 xml_list = xml_list->next;
80 if (xml_list->type == XML_VALUE)
81 external_list_insert(external_list, NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL);
83 XML_LIST_CMP(xml_list, "<file>") {
84 xml_list = xml_list->next;
85 if (xml_list->type == XML_VALUE)
86 external_list_insert(external_list, NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL);
88 XML_LIST_CMP(xml_list, "<exec>") {
89 xml_list = xml_list->next;
90 if (xml_list->type == XML_VALUE)
91 external_list_insert(external_list, NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL);
93 XML_LIST_CMP(xml_list, "<newmime>") {
94 xml_list = xml_list->next;
95 if (xml_list->type == XML_VALUE)
96 external_list_insert(external_list, NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL);
98 XML_LIST_CMP(xml_list, "<type>") {
99 xml_list = xml_list->next;
100 if (xml_list->type == XML_VALUE)
101 external_list_insert(external_list, NULL, NULL, NULL, NULL, NULL, NULL, xml_list->item);
105 XML_LIST_CMP(xml_list, "<enabled>") {
106 xml_list = xml_list->next;
107 if (xml_list->type == XML_VALUE) {
108 if (!strcasecmp(xml_list->item, "false"))
109 tmp_list->enabled = FALSE;
111 tmp_list->enabled = TRUE;
120 XML_LIST *external_xml(EXTERNAL * external, XML_LIST * xml_list)
123 struct EXTERNAL_LIST_LIST *el;
125 if (external == NULL)
128 pthread_rwlock_rdlock(&external->lock);
130 xml_list = xml_list_add(xml_list, "<external>", XML_TAG);
132 xml_list = xml_list_add(xml_list, "<enabled>", XML_TAG);
133 xml_list = xml_list_add(xml_list, (external->enabled == TRUE) ? "true" : "false", XML_VALUE);
134 xml_list = xml_list_add(xml_list, "</enabled>", XML_TAG);
136 for (el = external->external_list; el; el = el->next) {
137 xml_list = xml_list_add(xml_list, "<item>", XML_TAG);
139 xml_list = xml_list_add(xml_list, "<enabled>", XML_TAG);
140 xml_list = xml_list_add(xml_list, (el->enabled == TRUE) ? "true" : "false", XML_VALUE);
141 xml_list = xml_list_add(xml_list, "</enabled>", XML_TAG);
143 if (el->comment != NULL) {
144 xml_list = xml_list_add(xml_list, "<comment>", XML_TAG);
145 ptr = string_to_xml(el->comment);
146 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
148 xml_list = xml_list_add(xml_list, "</comment>", XML_TAG);
151 if (el->host != NULL) {
152 xml_list = xml_list_add(xml_list, "<host>", XML_TAG);
153 ptr = string_to_xml(el->host);
154 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
156 xml_list = xml_list_add(xml_list, "</host>", XML_TAG);
159 if (el->file != NULL) {
160 xml_list = xml_list_add(xml_list, "<file>", XML_TAG);
161 ptr = string_to_xml(el->file);
162 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
164 xml_list = xml_list_add(xml_list, "</file>", XML_TAG);
167 if (el->mime != NULL) {
168 xml_list = xml_list_add(xml_list, "<mime>", XML_TAG);
169 ptr = string_to_xml(el->mime);
170 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
172 xml_list = xml_list_add(xml_list, "</mime>", XML_TAG);
175 if (el->newmime != NULL) {
176 xml_list = xml_list_add(xml_list, "<newmime>", XML_TAG);
177 ptr = string_to_xml(el->newmime);
178 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
180 xml_list = xml_list_add(xml_list, "</newmime>", XML_TAG);
183 if (el->exec != NULL) {
184 xml_list = xml_list_add(xml_list, "<exec>", XML_TAG);
185 ptr = string_to_xml(el->exec);
186 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
188 xml_list = xml_list_add(xml_list, "</exec>", XML_TAG);
191 xml_list = xml_list_add(xml_list, "<type>", XML_TAG);
192 xml_list = xml_list_add(xml_list, (el->type == EXTERNAL_FILE) ? "file" : "pipe", XML_VALUE);
193 xml_list = xml_list_add(xml_list, "</type>", XML_TAG);
195 xml_list = xml_list_add(xml_list, "</item>", XML_TAG);
198 xml_list = xml_list_add(xml_list, "</external>", XML_TAG);
200 pthread_rwlock_unlock(&external->lock);
205 void external_list_insert(struct EXTERNAL_LIST_LIST *x, char *a, char *b, char *c, char *d, char *e, char *f, char *g)
208 FREE_AND_NULL(x->comment);
211 x->comment = xstrdup(a);
216 FREE_AND_NULL(x->mime);
219 x->mime = xstrdup(b);
220 x->me = reg_compile(b, REGFLAGS);
227 FREE_AND_NULL(x->host);
230 x->host = xstrdup(c);
231 x->he = reg_compile(c, REGFLAGS);
238 FREE_AND_NULL(x->file);
241 x->file = xstrdup(d);
242 x->fe = reg_compile(d, REGFLAGS);
247 FREE_AND_NULL(x->exec);
250 x->exec = xstrdup(e);
253 FREE_AND_NULL(x->newmime);
256 x->newmime = xstrdup(f);
259 if (!strcasecmp(g, "file"))
260 x->type = EXTERNAL_FILE;
262 x->type = EXTERNAL_PIPE;
266 struct EXTERNAL_LIST_LIST *external_list_new(struct EXTERNAL_LIST_LIST *x)
269 x = xmalloc(sizeof(struct EXTERNAL_LIST_LIST));
272 while (x->next != NULL)
274 x->next = xmalloc(sizeof(struct EXTERNAL_LIST_LIST));
288 x->type = EXTERNAL_PIPE;
294 struct EXTERNAL_LIST_LIST *external_list_delete(struct EXTERNAL_LIST_LIST *x)
296 struct EXTERNAL_LIST_LIST *start = x;
298 while (start->prev != NULL)
302 x->next->prev = x->prev;
304 x->prev->next = x->next;
314 FREE_AND_NULL(x->comment);
315 FREE_AND_NULL(x->mime);
316 FREE_AND_NULL(x->host);
317 FREE_AND_NULL(x->file);
318 FREE_AND_NULL(x->exec);
319 FREE_AND_NULL(x->newmime);
327 free memory used by EXTERNAL-type data structure
329 void external_free(EXTERNAL * external)
333 external_list_free(external->external_list);
335 pthread_rwlock_destroy(&external->lock);
340 void external_list_free(struct EXTERNAL_LIST_LIST *el)
342 struct EXTERNAL_LIST_LIST *tmp;
353 FREE_AND_NULL(el->comment);
354 FREE_AND_NULL(el->mime);
355 FREE_AND_NULL(el->host);
356 FREE_AND_NULL(el->file);
357 FREE_AND_NULL(el->exec);
358 FREE_AND_NULL(el->newmime);
367 return pointer to EXTERNAL list node matching the connection, if found
369 struct EXTERNAL_LIST_LIST *external_find(EXTERNAL * external, CONNECTION * connection)
372 struct EXTERNAL_LIST_LIST *tmp_list;
374 pthread_rwlock_rdlock(&external->lock);
376 if (external->enabled == FALSE || connection->bypass & FEATURE_EXTERNAL)
379 tmp_list = external->external_list;
381 for (; tmp_list != NULL; tmp_list = tmp_list->next) {
382 if (tmp_list->enabled == FALSE)
385 if (tmp_list->me != NULL && connection->rheader->content_type != NULL) {
386 ret = reg_exec(tmp_list->me, connection->rheader->content_type);
389 } else if (tmp_list->me != NULL) {
390 ret = reg_exec(tmp_list->me, "");
394 if (tmp_list->he != NULL) {
395 ret = reg_exec(tmp_list->he, connection->header->host);
400 if (tmp_list->fe != NULL) {
401 ret = reg_exec(tmp_list->fe, connection->header->file);
413 execute an external program, feed stdin the contents of filebuf if available, and
414 place program's output into a filebuf then return
416 FILEBUF *external_exec(CONNECTION * connection, char *exe, FILEBUF * filebuf, int flags)
418 int i = 0, x, ret, pid, ipipe[2], opipe[2], fd;
419 char buf[8096], *ptr, tmpfile[64], **args;
420 FILEBUF *ret_filebuf;
421 struct HTTP_HEADER_LIST *header_list = NULL;
422 struct pollfd pfd[2];
424 putlog(MMLOG_DEBUG, "executing %s", exe);
435 if (flags == EXTERNAL_FILE) {
436 s_strncpy(tmpfile, TEMPFILE, sizeof(tmpfile));
438 fd = mkstemp(tmpfile);
444 while (ret != filebuf->size) {
445 x = write(fd, &filebuf->data[ret], (filebuf->size - ret < BLOCKSIZE) ? filebuf->size - ret : BLOCKSIZE);
463 ret_filebuf = filebuf_new();
465 pfd[0].fd = ipipe[0];
466 pfd[0].events = POLLIN;
468 if (flags == EXTERNAL_PIPE && filebuf != NULL) {
469 pfd[1].fd = opipe[1];
470 pfd[1].events = POLLOUT;
477 fcntl(ipipe[0], F_SETFL, O_NONBLOCK);
478 fcntl(opipe[1], F_SETFL, O_NONBLOCK);
481 ret = p_poll(pfd, 2, -1);
484 if (pfd[0].revents & POLLIN) {
485 ret = read(ipipe[0], buf, sizeof(buf));
487 filebuf_add(ret_filebuf, buf, ret);
490 } else if (pfd[0].revents & (POLLHUP | POLLERR))
493 if (pfd[1].revents & POLLOUT) {
494 ret = write(opipe[1], &filebuf->data[i], (filebuf->size - i < 8096) ? filebuf->size - i : 8096);
503 if (i == filebuf->size) {
509 } else if (pfd[1].revents & (POLLHUP | POLLERR)) {
521 args = string_break(exe, ' ');
522 if (flags == EXTERNAL_FILE) {
523 for (i = 0; args[i]; i++);
525 args = xrealloc(args, sizeof(char *) * (i + 2));
532 if (connection->header != NULL) {
533 p_setenv("HTTP_FILE", connection->header->file, 1);
534 if (connection->header->host != NULL)
535 p_setenv("HTTP_HOST", connection->header->host, 1);
536 p_setenv("HTTP_METHOD", connection->header->method, 1);
537 snprintf(buf, sizeof(buf), "%d", connection->header->port);
538 p_setenv("HTTP_PORT", buf, 1);
540 if (connection->header != NULL)
541 header_list = connection->header->header;
542 while (header_list != NULL) {
543 ptr = env_header_format("CLIENT_", header_list->type);
545 p_setenv(ptr, header_list->value, 1);
549 header_list = header_list->next;
552 if (connection->rheader != NULL)
553 header_list = connection->rheader->header;
554 while (header_list != NULL) {
555 ptr = env_header_format("SERVER_", header_list->type);
557 p_setenv(ptr, header_list->value, 1);
561 header_list = header_list->next;
565 p_setenv("IP", connection->ip, 1);
567 dup2(opipe[0], STDIN_FILENO);
568 dup2(ipipe[1], STDOUT_FILENO);
570 execvp(args[0], args);
591 waitpid(pid, &ret, 0);
593 if (!WIFEXITED(ret) || WEXITSTATUS(ret)) {
594 /* return original content if process exits with an error */
595 filebuf_free(ret_filebuf);
600 if (flags == EXTERNAL_FILE)
607 extract Content-Type header from a filebuf and remove it
609 char *external_getmime(FILEBUF * filebuf)
614 if (filebuf->size < 14 || strncasecmp(filebuf->data, "Content-type: ", 14))
617 ptr = memchr(filebuf->data, '\n', filebuf->size);
621 len = ptr - &filebuf->data[14] - ((*(ptr - 1) == '\r') ? 1 : 0);
623 ret = xmalloc(len + 1);
625 s_strncpy(ret, &filebuf->data[14], len);
627 /* assume \r\n for second newline if first is \r\n */
628 len += 14 + ((filebuf->data[14 + len] == '\r') ? 4 : 2);
630 /* len can be lower if nothing but the header was sent */
631 if (len <= filebuf->size) {
632 memcpy(filebuf->data, &filebuf->data[len], filebuf->size - len);
633 filebuf_resize(filebuf, filebuf->size - len);
635 FREE_AND_NULL(filebuf->data);
639 putlog(MMLOG_DEBUG, "external_getmime: %s", ret);
645 make an http header suitable as an environment variable
647 char *env_header_format(char *prefix, char *type)
656 ret = xmalloc(i + j + 1);
662 memcpy(ret, prefix, j);
664 for (; i >= 0; x--, i--) {
668 ret[x] = toupper(type[i]);