/* MiddleMan filtering proxy server Copyright (C) 2002 Jason McLaughlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include "proto.h" #ifdef HAVE_ZLIB #include #endif /* HAVE_ZLIB */ /* strncpy which always NULL terminates */ char *s_strncpy(char *d, char *s, size_t len) { char *dest = d; for (; len && (*dest = *s); s++, dest++, len--); *dest = '\0'; return d; } /* strncat which always NULL terminates */ char *s_strncat(char *d, char *s, size_t len) { char *dest = d; for (; len && *dest; dest++, len--); for (; len && (*dest = *s); s++, dest++, len--); *dest = '\0'; return d; } /* case-insensitive strstr */ char *s_strcasestr(char *haystack, char *needle) { char *n, *tmp; for (; *haystack; haystack++) { for (tmp = haystack, n = needle; *tmp && *n && tolower(*tmp) == tolower(*n); tmp++, n++); if (!*n) return haystack; } return NULL; } /* free memory used by FILEBUF-type structure */ void filebuf_free(FILEBUF * filebuf) { if (!filebuf) return; if (filebuf->data != NULL) xfree(filebuf->data); if (filebuf->type != NULL) xfree(filebuf->type); xfree(filebuf); } /* create empty filebuf */ FILEBUF *filebuf_new() { FILEBUF *filebuf; filebuf = xmalloc(sizeof(FILEBUF)); filebuf->data = NULL; filebuf->type = NULL; filebuf->size = filebuf->realsize = 0; filebuf->code = -1; return filebuf; } /* reduce filebuf size to actual size of contents */ void filebuf_shorten(FILEBUF * filebuf) { if (filebuf->realsize > filebuf->size) { filebuf->realsize = filebuf->size; filebuf->data = xrealloc(filebuf->data, filebuf->size); } } /* append data to a file buffer */ void filebuf_add(FILEBUF * filebuf, const char *data, int len) { int x; x = filebuf->size; filebuf_resize(filebuf, filebuf->size + len); memcpy(&filebuf->data[x], data, len); } /* add formatted data to filebuf */ void filebuf_addf(FILEBUF * filebuf, const char *fmt, ...) { int ret; char buf[8096]; va_list valist; va_start(valist, fmt); ret = vsnprintf(buf, sizeof(buf), fmt, valist); va_end(valist); filebuf_add(filebuf, buf, ret); } /* resize a filebuf (without adding anything) */ void filebuf_resize(FILEBUF * filebuf, int newsize) { if (filebuf->realsize >= newsize && filebuf->realsize <= newsize + FILEBUF_ALIGNMENT && filebuf->data != NULL) filebuf->size = newsize; else if (newsize != 0) { filebuf->realsize = ALIGN2(newsize, FILEBUF_ALIGNMENT); filebuf->data = xrealloc(filebuf->data, filebuf->realsize); filebuf->size = newsize; } } /* return descriptive error for a code */ char *code_to_error(int code) { switch (code) { case 200: return "OK"; case 301: return "Document Moved"; case 302: return "Found"; case 304: return "Not Modified"; case 400: return "Bad Request"; case 403: return "Forbidden"; case 404: return "Not Found"; case 407: return "Authenticaton required"; case 500: return "Internal Error"; case 501: return "Not Implemented"; case 502: return "Service Temporarily Unavailable"; case 503: return "Connection Failed"; default: return "Unknown"; } return NULL; } /* strdup which uses xmalloc */ char *xstrdup(char *s) { return strcpy(xmalloc(strlen(s) + 1), s); } /* strndup which uses xmalloc */ char *xstrndup(char *s, int len) { return s_strncpy(xmalloc(((strlen(s) < len) ? strlen(s) : len) + 1), s, len); } /* convert an error flag into a template name */ char *error_to_template(int error) { switch (error) { case ERROR_CONNECT: return "noconnect"; case ERROR_DNS: return "nodns"; case ERROR_AUTH: return "badauth"; case ERROR_UNKNOWN: default: return "unknown"; } return NULL; } /* free HOSTENT structure */ void hostent_free(HOSTENT * hostent) { xfree(hostent->addr); xfree(hostent); } /* return TRUE if a string appears to be an IP address */ int isip(char *string) { int dots = 0; for (; *string; string++) { if (*string == '.') dots++; else if (!isdigit((int) *string)) return FALSE; } return (dots == 3) ? TRUE : FALSE; } /* convert string to uppercase */ void string_tolower(char *string) { for (; *string; string++) *string = tolower(*string); } /* replace characters that have a special meaning in html with their respective entity */ char *string_to_html(char *s, int newlines) { char *ret; FILEBUF *filebuf; filebuf = filebuf_new(); for (; *s; s++) { if (*s == '"') filebuf_add(filebuf, """, 6); else if (*s == '<') filebuf_add(filebuf, "<", 4); else if (*s == '>') filebuf_add(filebuf, ">", 4); else if (*s == '&') filebuf_add(filebuf, "&", 5); else if (newlines == TRUE && *s == '\n') filebuf_add(filebuf, "
", 4); else filebuf_add(filebuf, s, 1); } filebuf_add(filebuf, "", 1); filebuf_shorten(filebuf); ret = filebuf->data; filebuf->data = NULL; filebuf_free(filebuf); return ret; } /* decode all hex characters in a url */ char *url_decode(char *url, int len) { char *ret, *u = url, tmp[3]; FILEBUF *filebuf; tmp[2] = '\0'; filebuf = filebuf_new(); while (*u && u - url < len) { if (*u == '%' && u[1] != '\0') { tmp[0] = u[1]; tmp[1] = u[2]; filebuf_addf(filebuf, "%c", strtol(tmp, NULL, 16)); u += 3; } else if (*u == '+') { filebuf_add(filebuf, " ", 1); u++; } else { filebuf_add(filebuf, u, 1); u++; } } filebuf_add(filebuf, "", 1); filebuf_shorten(filebuf); ret = filebuf->data; filebuf->data = NULL; filebuf_free(filebuf); return ret; } /* encode special characters in a url */ char *url_encode(char *url, int len) { char *ret, *u = url; FILEBUF *filebuf; filebuf = filebuf_new(); for (; *u && u - url < len; u++) { if (*u == ' ') filebuf_add(filebuf, "+", 1); else if ((*u >= 0 && *u <= 42) || (*u >= 58 && *u <= 64) || (*u >= 91 && *u <= 96) || *u >= 123) filebuf_addf(filebuf, "%%%.2x", *u); else filebuf_add(filebuf, u, 1); } filebuf_add(filebuf, "", 1); filebuf_shorten(filebuf); ret = filebuf->data; filebuf->data = NULL; filebuf_free(filebuf); return ret; } /* write a filebuf to a file */ int filebuf_save(FILEBUF * filebuf, char *filename) { int fd, ret; fd = open(filename, O_CREAT | O_WRONLY); if (fd == -1) return -1; ret = write(fd, filebuf->data, filebuf->size); close(fd); return ret; } /* escape all <, >, and \'s in a string destined for the xml file */ char *string_to_xml(char *s) { char *ret; FILEBUF *filebuf; filebuf = filebuf_new(); for (; *s; s++) { if (*s == '\\') filebuf_add(filebuf, "\\\\", 2); else if (*s == '<') filebuf_add(filebuf, "\\<", 2); else if (*s == '>') filebuf_add(filebuf, "\\>", 2); else filebuf_add(filebuf, s, 1); } filebuf_add(filebuf, "", 1); filebuf_shorten(filebuf); ret = filebuf->data; filebuf->data = NULL; filebuf_free(filebuf); return ret; } /* tokenize a string and return a NULL-terminated char ** */ char **string_break(char *s, char d) { int count = 0; char **ret = NULL, *start = s; for (; *s; s++) { if (*s == d) { ret = xrealloc(ret, sizeof(char *) * (count + 2)); ret[count] = xstrndup(start, s - start); start = s + 1; count++; } } if (*start) { ret = xrealloc(ret, sizeof(char *) * (count + 2)); ret[count] = xstrndup(start, s - start); count++; } if (count > 0) ret[count] = NULL; return ret; } /* read a file into a filebuf */ int filebuf_read(FILEBUF * filebuf, char *filename) { int x, ret = 0, fd; char buf[BLOCKSIZE]; fd = open(filename, O_RDONLY); if (fd == -1) return -1; while ((x = read(fd, buf, BLOCKSIZE)) > 0) { filebuf_add(filebuf, buf, x); ret += x; } close(fd); return ret; } /* replace area of filebuf at specified offset and length with something else */ void filebuf_replace(FILEBUF * filebuf, int so, int eo, char *replace) { int len1 = eo - so; int len2 = strlen(replace); if (len2 > len1) filebuf_resize(filebuf, filebuf->size + (len2 - len1)); if (len1 != len2) memmove(&filebuf->data[eo + (len2 - len1)], &filebuf->data[eo], filebuf->size - eo); if (len2 < len1) filebuf_resize(filebuf, filebuf->size + (len2 - len1)); memcpy(&filebuf->data[so], replace, len2); } /* gzip compress contents of a FILEBUF note: zlib doesn't provide any api's for doing in-memory compression/decompression in gzip format, so a temp file has to be used. */ int filebuf_gzip(FILEBUF *filebuf) { #ifdef HAVE_ZLIB int fd, x = 0, ret; char tmpfile[64]; gzFile *gzf; s_strncpy(tmpfile, TEMPFILE, sizeof(tmpfile)); fd = mkstemp(tmpfile); if (fd == -1) return FALSE; gzf = gzdopen(fd, "w"); if (gzf == NULL) { close(fd); unlink(tmpfile); return FALSE; } while (x < filebuf->size) { ret = gzwrite(gzf, &filebuf->data[x], filebuf->size - x); if (ret == -1) { gzclose(gzf); unlink(tmpfile); return FALSE; } else if (ret == 0) break; x += ret; } gzclose(gzf); FREE_AND_NULL(filebuf->data); filebuf->size = 0; filebuf_read(filebuf, tmpfile); unlink(tmpfile); return TRUE; #endif /* HAVE_ZLIB */ return FALSE; } /* ungzip the contents of a filebuf using zlib and a temporary file */ int filebuf_ungzip(FILEBUF *filebuf) { #ifdef HAVE_ZLIB int fd, x = 0, ret; char tmpfile[64], buf[BLOCKSIZE]; FILEBUF *new_filebuf; gzFile *gzf; s_strncpy(tmpfile, TEMPFILE, sizeof(tmpfile)); fd = mkstemp(tmpfile); if (fd == -1) return FALSE; while (x < filebuf->size) { ret = write(fd, &filebuf->data[x], filebuf->size - x); if (ret == -1) { close(fd); unlink(tmpfile); return FALSE; } x += ret; } close(fd); gzf = gzopen(tmpfile, "r"); if (gzf == NULL) { unlink(tmpfile); return FALSE; } new_filebuf = filebuf_new(); while (1) { ret = gzread(gzf, buf, sizeof(buf)); if (ret == -1) { gzclose(gzf); unlink(tmpfile); filebuf_free(new_filebuf); return FALSE; } else if (ret == 0) break; filebuf_add(new_filebuf, buf, ret); } gzclose(gzf); unlink(tmpfile); xfree(filebuf->data); filebuf->data = new_filebuf->data; filebuf->size = new_filebuf->size; new_filebuf->data = NULL; filebuf_free(new_filebuf); return TRUE; #endif /* HAVE_ZLIB */ return FALSE; }