summaryrefslogtreecommitdiff
path: root/src/utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils.c')
-rw-r--r--src/utils.c367
1 files changed, 189 insertions, 178 deletions
diff --git a/src/utils.c b/src/utils.c
index 1a91d7a..63f4dbb 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -1,9 +1,9 @@
-/* $Id: utils.c,v 1.1.1.1 2000-02-16 17:32:24 sdyoung Exp $
+/* $Id: utils.c,v 1.2 2000-09-12 00:01:29 rjkaes Exp $
*
* Misc. routines which are used by the various functions to handle strings
* and memory allocation and pretty much anything else we can think of. Also,
- * the load cutoff routine is in here, along with the HTML show stats
- * function. Could not think of a better place for it, so it's in here.
+ * the load cutoff routine is in here. Could not think of a better place for
+ * it, so it's in here.
*
* Copyright (C) 1998 Steven Young
* Copyright (C) 1999 Robert James Kaes (rjkaes@flarenet.com)
@@ -19,60 +19,27 @@
* General Public License for more details.
*/
-#ifdef HAVE_CONFIG_H
-#include <defines.h>
-#endif
+#include "tinyproxy.h"
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <time.h>
#include <ctype.h>
-#include <sysexits.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <errno.h>
#include <signal.h>
-#include <sys/stat.h>
-#include <assert.h>
+#include <sysexits.h>
-#include "config.h"
-#include "tinyproxy.h"
-#include "utils.h"
-#include "log.h"
-#include "conns.h"
#include "buffer.h"
-
-char *xstrdup(char *st)
-{
- char *p;
-
- assert(st);
-
- if (!(p = strdup(st))) {
- log("ERROR xstrdup: out of memory (%s)", strerror(errno));
- return NULL;
- } else {
- return p;
- }
-}
+#include "log.h"
+#include "sock.h"
+#include "utils.h"
/*
* Find the start of the needle in the haystack. Limits the search to less
* than "length" characters. Returns NULL if the needle is not found.
*/
-char *xstrstr(char *haystack, char *needle, unsigned int length,
- int case_sensitive)
+char *xstrstr(char *haystack, char *needle, size_t length,
+ bool_t case_sensitive)
{
unsigned int i;
/* Used to specify which function to use... need the decl. */
- int (*fn) (const char *s1, const char *s2, unsigned int n);
-
- assert(haystack);
- assert(needle);
- assert(length > 0);
- assert(case_sensitive == FALSE || case_sensitive == TRUE);
+ int (*fn) (const char *s1, const char *s2, size_t n);
if (case_sensitive)
fn = strncmp;
@@ -84,7 +51,7 @@ char *xstrstr(char *haystack, char *needle, unsigned int length,
for (i = 0; i <= length - strlen(needle); i++) {
if ((*fn) (haystack + i, needle, strlen(needle)) == 0)
- return haystack + i;
+ return (haystack + i);
}
@@ -92,173 +59,217 @@ char *xstrstr(char *haystack, char *needle, unsigned int length,
}
/*
- * for-sure malloc
+ * Display an error to the client.
*/
-void *xmalloc(unsigned long int sz)
-{
- void *p;
-
- assert(sz > 0);
-
- if (!(p = malloc((size_t) sz))) {
- log("ERROR xmalloc: out of memory (%s)", strerror(errno));
- return NULL;
- }
- return p;
-}
-
-#ifdef USE_PROC
-int calcload(void)
+int httperr(struct conn_s *connptr, int err, char *msg)
{
- char buf[BUFFER], *p;
- FILE *f;
+ static char *premsg = "HTTP/1.0 %d %s\r\n" \
+ "Content-type: text/html\r\n\r\n" \
+ "<html><head><title>%s</title></head>\r\n" \
+ "<body>\r\n" \
+ "<font size=\"+2\">Cache Error!</font><br>\r\n" \
+ "An error of type %d occurred: %s\r\n" \
+ "<hr>\r\n" \
+ "<font size=\"-1\"><em>Generated by %s</em></font>\r\n" \
+ "</body></html>\r\n";
- if (!config.cutoffload) {
+ connptr->output_message = malloc(MAXBUFFSIZE);
+ if (!connptr->output_message) {
+ log(LOG_CRIT, "Out of memory!");
return -1;
}
- if (!(f = fopen("/proc/loadavg", "rt"))) {
- log("unable to read /proc/loadavg");
- config.cutoffload = 0.0;
- return -1;
- }
- fgets(buf, BUFFER, f);
- p = strchr(buf, ' ');
- *p = '\0';
- load = atof(buf);
- fclose(f);
+ sprintf(connptr->output_message, premsg, err, msg, msg, err, msg, VERSION);
+
return 0;
}
-#else
-int calcload(void)
+void makedaemon(void)
{
- FILE *f;
- char buf[BUFFER];
- char *p, *y;
+ if (fork() != 0)
+ exit(0);
- if (!config.cutoffload) {
- return -1;
- }
+ setsid();
+ signal(SIGHUP, SIG_IGN);
- if (!(f = popen(UPTIME_PATH, "r"))) {
- log("calcload: unable to exec uptime");
- config.cutoffload = 0.0;
- return -1;
- }
- fgets(buf, BUFFER, f);
- p = strrchr(buf, ':');
- p += 2;
- y = strchr(p, ',');
- *y = '\0';
- load = atof(p);
- pclose(f);
- return 0;
-}
+ if (fork() != 0)
+ exit(0);
-#endif
+ chdir("/");
+ umask(077);
+
+ close(0);
+ close(1);
+ close(2);
+}
/*
- * Delete the server's buffer and replace it with a premade message which will
- * be sent to the client.
+ * Safely creates filename and returns the low-level file descriptor.
*/
-static void update_output_buffer(struct conn_s *connptr, char *outbuf)
+static int create_file_safely(const char *filename)
{
- assert(connptr);
- assert(outbuf);
-
- delete_buffer(connptr->sbuffer);
- connptr->sbuffer = new_buffer();
+ struct stat lstatinfo;
+ int fildes;
+
+ /*
+ * lstat() the file. If it doesn't exist, create it with O_EXCL.
+ * If it does exist, open it for writing and perform the fstat()
+ * check.
+ */
+ if (lstat(filename, &lstatinfo) < 0) {
+ /*
+ * If lstat() failed for any reason other than "file not
+ * existing", exit.
+ */
+ if (errno != ENOENT) {
+ log(LOG_ERR, "Error checking PID file %s: %s",
+ filename, strerror(errno));
+ return -1;
+ }
+
+ /*
+ * The file doesn't exist, so create it with O_EXCL to make
+ * sure an attacker can't slip in a file between the lstat()
+ * and open()
+ */
+ if ((fildes = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600)) < 0) {
+ log(LOG_ERR, "Could not create PID file %s: %s",
+ filename, strerror(errno));
+ return -1;
+ }
+ } else {
+ struct stat fstatinfo;
+
+ /*
+ * Open an existing file.
+ */
+ if ((fildes = open(filename, O_RDWR)) < 0) {
+ log(LOG_ERR, "Could not open PID file %s: %s",
+ filename, strerror(errno));
+ return -1;
+ }
+
+ /*
+ * fstat() the opened file and check that the file mode bits,
+ * inode, and device match.
+ */
+ if (fstat(fildes, &fstatinfo) < 0
+ || lstatinfo.st_mode != fstatinfo.st_mode
+ || lstatinfo.st_ino != fstatinfo.st_ino
+ || lstatinfo.st_dev != fstatinfo.st_dev) {
+ log(LOG_ERR, "The PID file %s has been changed before it could be opened!",
+ filename);
+ close(fildes);
+ return -1;
+ }
+
+ /*
+ * If the above check was passed, we know that the lstat()
+ * and fstat() were done on the same file. Now we check that
+ * there's only one link, and that it's a normal file (this
+ * isn't strictly necessary because the fstat() vs lstat()
+ * st_mode check would also find this)
+ */
+ if (fstatinfo.st_nlink > 1 || !S_ISREG(lstatinfo.st_mode)) {
+ log(LOG_ERR, "The PID file %s has too many links, or is not a regular file: %s",
+ filename, strerror(errno));
+ close(fildes);
+ return -1;
+ }
+
+ /*
+ * On systems whcih don't support ftruncate() the best we can
+ * do is to close the file and reopen it in create mode, which
+ * unfortunately leads to a race condition, however "systems
+ * which don't support ftruncate()" is pretty much SCO only,
+ * and if you're using that you deserver what you get.
+ * ("Little sympathy has been extended")
+ */
+#if defined NO_FTRUNCATE
+ close(fildes);
+ if ((fildes = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0600)) < 0) {
+ log(LOG_ERR, "Could not open PID file %s: %s",
+ filename, strerror(errno));
+ return -1;
+ }
+#else
+ ftruncate(fildes, 0);
+#endif /* NO_FTRUNCATE */
+ }
- push_buffer(connptr->sbuffer, outbuf, strlen(outbuf));
- shutdown(connptr->server_fd, 2);
- connptr->type = CLOSINGCONN;
+ return fildes;
}
/*
- * Display the statics of the tinyproxy server.
+ * Write the PID of the program to the specified file.
*/
-int showstats(struct conn_s *connptr)
+void pidfile_create(const char *filename)
{
- char *outbuf;
- static char *msg = "HTTP/1.0 200 OK\r\n" \
- "Content-type: text/html\r\n\r\n" \
- "<html><head><title>%s stats</title></head>\r\n" \
- "<body>\r\n" \
- "<center><h2>%s run-time statistics</h2></center><hr>\r\n" \
- "<blockquote>\r\n" \
- "Number of requests: %lu<br>\r\n" \
- "Number of connections: %lu<br>\r\n" \
- "Number of bad connections: %lu<br>\r\n" \
- "Number of opens: %lu<br>\r\n" \
- "Number of listens: %lu<br>\r\n" \
- "Number of bytes (tx): %lu<br>\r\n" \
- "Number of bytes (rx): %lu<br>\r\n" \
- "Number of garbage collects:%lu<br>\r\n" \
- "Number of idle connection kills:%lu<br>\r\n" \
- "Number of refused connections due to high load:%lu<br>\r\n" \
- "Current system load average:%.2f" \
- "(recalculated every % lu seconds)<br>\r\n" \
- "</blockquote>\r\n</body></html>\r\n";
-
- assert(connptr);
-
- outbuf = xmalloc(BUFFER);
-
- sprintf(outbuf, msg, VERSION, VERSION, stats.num_reqs,
- stats.num_cons, stats.num_badcons, stats.num_opens,
- stats.num_listens, stats.num_tx, stats.num_rx,
- stats.num_garbage, stats.num_idles, stats.num_refused, load,
- LOAD_RECALCTIMER);
-
- update_output_buffer(connptr, outbuf);
+ int fildes;
+ FILE *fd;
+
+ /*
+ * Create a new file
+ */
+ if ((fildes = create_file_safely(filename)) < 0)
+ exit(1);
+
+ /*
+ * Open a stdio file over the low-level one.
+ */
+ if ((fd = fdopen(fildes, "w")) == NULL) {
+ log(LOG_ERR, "fdopen() error on PID file %s: %s",
+ filename, strerror(errno));
+ close(fildes);
+ unlink(filename);
+ exit(1);
+ }
- return 0;
+ fprintf(fd, "%ld\n", (long)getpid());
+ fclose(fd);
}
+#ifndef HAVE_STRLCPY
/*
- * Display an error to the client.
+ * Function API taken from OpenBSD. Like strncpy(), but does not 0 fill the
+ * buffer, and always NULL terminates the buffer. size is the size of the
+ * destination buffer.
*/
-int httperr(struct conn_s *connptr, int err, char *msg)
+size_t strlcpy(char *dst, const char *src, size_t size)
{
- char *outbuf;
- static char *premsg = "HTTP/1.0 %d %s\r\n" \
- "Content-type: text/html\r\n\r\n" \
- "<html><head><title>%s</title></head>\r\n" \
- "<body>\r\n" \
- "<font size=\"+2\">Cache Error!</font><br>\r\n" \
- "An error of type %d occurred: %s\r\n" \
- "<hr>\r\n" \
- "<font size=\"-1\"><em>Generated by %s</em></font>\r\n" \
- "</body></html>\r\n";
+ size_t len = strlen(src);
+ size_t ret = len;
- assert(connptr);
- assert(err > 0);
- assert(msg);
+ if (len >= size)
+ len = size - 1;
- outbuf = xmalloc(BUFFER);
- sprintf(outbuf, premsg, err, msg, msg, err, msg, VERSION);
+ memcpy(dst, src, len);
+ dst[len] = '\0';
- update_output_buffer(connptr, outbuf);
-
- return 0;
+ return ret;
}
+#endif
-void makedaemon(void)
+#ifndef HAVE_STRLCAT
+/*
+ * Function API taken from OpenBSD. Like strncat(), but does not 0 fill the
+ * buffer, and always NULL terminates the buffer. size is the length of the
+ * buffer, which should be one more than the maximum resulting string
+ * length.
+ */
+size_t strlcat(char *dst, const char *src, size_t size)
{
- if (fork() != 0)
- exit(0);
-
- setsid();
- signal(SIGHUP, SIG_IGN);
-
- if (fork() != 0)
- exit(0);
-
- chdir("/");
- umask(0);
+ size_t len1 = strlen(dst);
+ size_t len2 = strlen(src);
+ size_t ret = len1 + len2;
+
+ if (len1 + len2 >= size)
+ len2 = size - len1 - 1;
+ if (len2 > 0) {
+ memcpy(dst + len1, src, len2);
+ dst[len1 + len2] = '\0';
+ }
- close(0);
- close(1);
- close(2);
+ return ret;
}
+#endif