/* $FreeBSD$ */
/*
* Copyright (C) 2012 by Darren Reed.
*
* See the IPFILTER.LICENCE file for details on licencing.
*
* $Id: load_http.c,v 1.5.2.5 2012/07/22 08:04:24 darren_r Exp $
*/
#include "ipf.h"
#include <ctype.h>
/*
* Because the URL can be included twice into the buffer, once as the
* full path for the "GET" and once as the "Host:", the buffer it is
* put in needs to be larger than 512*2 to make room for the supporting
* text. Why not just use snprintf and truncate? The warning about the
* URL being too long tells you something is wrong and does not fetch
* any data - just truncating the URL (with snprintf, etc) and sending
* that to the server is allowing an unknown and unintentioned action
* to happen.
*/
#define MAX_URL_LEN 512
#define LOAD_BUFSIZE (MAX_URL_LEN * 2 + 128)
/*
* Format expected is one addres per line, at the start of each line.
*/
alist_t *
load_http(char *url)
{
int fd, len, left, port, endhdr, removed, linenum = 0;
char *s, *t, *u, buffer[LOAD_BUFSIZE], *myurl;
alist_t *a, *rtop, *rbot;
size_t avail;
int error;
/*
* More than this would just be absurd.
*/
if (strlen(url) > MAX_URL_LEN) {
fprintf(stderr, "load_http has a URL > %d bytes?!\n",
MAX_URL_LEN);
return NULL;
}
fd = -1;
rtop = NULL;
rbot = NULL;
avail = sizeof(buffer);
error = snprintf(buffer, avail, "GET %s HTTP/1.0\r\n", url);
/*
* error is always less then avail due to the constraint on
* the url length above.
*/
avail -= error;
myurl = strdup(url);
if (myurl == NULL)
goto done;
s = myurl + 7; /* http:// */
t = strchr(s, '/');
if (t == NULL) {
fprintf(stderr, "load_http has a malformed URL '%s'\n", url);
free(myurl);
return NULL;
}
*t++ = '\0';
/*
* 10 is the length of 'Host: \r\n\r\n' below.
*/
if (strlen(s) + strlen(buffer) + 10 > sizeof(buffer)) {
fprintf(stderr, "load_http has a malformed URL '%s'\n", url);
free(myurl);
return NULL;
}
u = strchr(s, '@');
if (u != NULL)
s = u + 1; /* AUTH */
error = snprintf(buffer + strlen(buffer), avail, "Host: %s\r\n\r\n", s);
if (error >= avail) {
fprintf(stderr, "URL is too large: %s\n", url);
goto done;
}
u = strchr(s, ':');
if (u != NULL) {
*u++ = '\0';
port = atoi(u);
if (port < 0 || port > 65535)
goto done;
} else {
port = 80;
}
fd = connecttcp(s, port);
if (fd == -1)
goto done;
len = strlen(buffer);
if (write(fd, buffer, len) != len)
goto done;
s = buffer;
endhdr = 0;
left = sizeof(buffer) - 1;
while ((len = read(fd, s, left)) > 0) {
s[len] = '\0';
left -= len;
s += len;
if (endhdr >= 0) {
if (endhdr == 0) {
t = strchr(buffer, ' ');
if (t == NULL)
continue;
t++;
if (*t != '2')
break;
}
u = buffer;
while ((t = strchr(u, '\r')) != NULL) {
if (t == u) {
if (*(t + 1) == '\n') {
u = t + 2;
endhdr = -1;
break;
} else
t++;
} else if (*(t + 1) == '\n') {
endhdr++;
u = t + 2;
} else
u = t + 1;
}
if (endhdr >= 0)
continue;
removed = (u - buffer) + 1;
memmove(buffer, u, (sizeof(buffer) - left) - removed);
s -= removed;
left += removed;
}
do {
t = strchr(buffer, '\n');
if (t == NULL)
break;
linenum++;
*t = '\0';
/*
* Remove comment and continue to the next line if
* the comment is at the start of the line.
*/
u = strchr(buffer, '#');
if (u != NULL) {
*u = '\0';
if (u == buffer)
continue;
}
/*
* Trim off tailing white spaces, will include \r
*/
for (u = t - 1; (u >= buffer) && ISSPACE(*u); u--)
*u = '\0';
a = alist_new(AF_UNSPEC, buffer);
if (a != NULL) {
if (rbot != NULL)
rbot->al_next = a;
else
rtop = a;
rbot = a;
} else {
fprintf(stderr,
"%s:%d unrecognised content:%s\n",
url, linenum, buffer);
}
t++;
removed = t - buffer;
memmove(buffer, t, sizeof(buffer) - left - removed);
s -= removed;
left += removed;
} while (1);
}
done:
if (myurl != NULL)
free(myurl);
if (fd != -1)
close(fd);
return rtop;
}