1/* $FreeBSD$ */
2
3/*
4 * Copyright (C) 2012 by Darren Reed.
5 *
6 * See the IPFILTER.LICENCE file for details on licencing.
7 *
8 * $Id: load_http.c,v 1.5.2.5 2012/07/22 08:04:24 darren_r Exp $
9 */
10
11#include "ipf.h"
12#include <ctype.h>
13
14/*
15 * Because the URL can be included twice into the buffer, once as the
16 * full path for the "GET" and once as the "Host:", the buffer it is
17 * put in needs to be larger than 512*2 to make room for the supporting
18 * text. Why not just use snprintf and truncate? The warning about the
19 * URL being too long tells you something is wrong and does not fetch
20 * any data - just truncating the URL (with snprintf, etc) and sending
21 * that to the server is allowing an unknown and unintentioned action
22 * to happen.
23 */
24#define	MAX_URL_LEN	512
25#define	LOAD_BUFSIZE	(MAX_URL_LEN * 2 + 128)
26
27/*
28 * Format expected is one addres per line, at the start of each line.
29 */
30alist_t *
31load_http(char *url)
32{
33	int fd, len, left, port, endhdr, removed, linenum = 0;
34	char *s, *t, *u, buffer[LOAD_BUFSIZE], *myurl;
35	alist_t *a, *rtop, *rbot;
36	size_t avail;
37	int error;
38
39	/*
40	 * More than this would just be absurd.
41	 */
42	if (strlen(url) > MAX_URL_LEN) {
43		fprintf(stderr, "load_http has a URL > %d bytes?!\n",
44			MAX_URL_LEN);
45		return NULL;
46	}
47
48	fd = -1;
49	rtop = NULL;
50	rbot = NULL;
51
52	avail = sizeof(buffer);
53	error = snprintf(buffer, avail, "GET %s HTTP/1.0\r\n", url);
54
55	/*
56	 * error is always less then avail due to the constraint on
57	 * the url length above.
58	 */
59	avail -= error;
60
61	myurl = strdup(url);
62	if (myurl == NULL)
63		goto done;
64
65	s = myurl + 7;			/* http:// */
66	t = strchr(s, '/');
67	if (t == NULL) {
68		fprintf(stderr, "load_http has a malformed URL '%s'\n", url);
69		free(myurl);
70		return NULL;
71	}
72	*t++ = '\0';
73
74	/*
75	 * 10 is the length of 'Host: \r\n\r\n' below.
76	 */
77	if (strlen(s) + strlen(buffer) + 10 > sizeof(buffer)) {
78		fprintf(stderr, "load_http has a malformed URL '%s'\n", url);
79		free(myurl);
80		return NULL;
81	}
82
83	u = strchr(s, '@');
84	if (u != NULL)
85		s = u + 1;		/* AUTH */
86
87	error = snprintf(buffer + strlen(buffer), avail, "Host: %s\r\n\r\n", s);
88	if (error >= avail) {
89		fprintf(stderr, "URL is too large: %s\n", url);
90		goto done;
91	}
92
93	u = strchr(s, ':');
94	if (u != NULL) {
95		*u++ = '\0';
96		port = atoi(u);
97		if (port < 0 || port > 65535)
98			goto done;
99	} else {
100		port = 80;
101	}
102
103
104	fd = connecttcp(s, port);
105	if (fd == -1)
106		goto done;
107
108
109	len = strlen(buffer);
110	if (write(fd, buffer, len) != len)
111		goto done;
112
113	s = buffer;
114	endhdr = 0;
115	left = sizeof(buffer) - 1;
116
117	while ((len = read(fd, s, left)) > 0) {
118		s[len] = '\0';
119		left -= len;
120		s += len;
121
122		if (endhdr >= 0) {
123			if (endhdr == 0) {
124				t = strchr(buffer, ' ');
125				if (t == NULL)
126					continue;
127				t++;
128				if (*t != '2')
129					break;
130			}
131
132			u = buffer;
133			while ((t = strchr(u, '\r')) != NULL) {
134				if (t == u) {
135					if (*(t + 1) == '\n') {
136						u = t + 2;
137						endhdr = -1;
138						break;
139					} else
140						t++;
141				} else if (*(t + 1) == '\n') {
142					endhdr++;
143					u = t + 2;
144				} else
145					u = t + 1;
146			}
147			if (endhdr >= 0)
148				continue;
149			removed = (u - buffer) + 1;
150			memmove(buffer, u, (sizeof(buffer) - left) - removed);
151			s -= removed;
152			left += removed;
153		}
154
155		do {
156			t = strchr(buffer, '\n');
157			if (t == NULL)
158				break;
159
160			linenum++;
161			*t = '\0';
162
163			/*
164			 * Remove comment and continue to the next line if
165			 * the comment is at the start of the line.
166			 */
167			u = strchr(buffer, '#');
168			if (u != NULL) {
169				*u = '\0';
170				if (u == buffer)
171					continue;
172			}
173
174			/*
175			 * Trim off tailing white spaces, will include \r
176			 */
177			for (u = t - 1; (u >= buffer) && ISSPACE(*u); u--)
178				*u = '\0';
179
180			a = alist_new(AF_UNSPEC, buffer);
181			if (a != NULL) {
182				if (rbot != NULL)
183					rbot->al_next = a;
184				else
185					rtop = a;
186				rbot = a;
187			} else {
188				fprintf(stderr,
189					"%s:%d unrecognised content:%s\n",
190					url, linenum, buffer);
191			}
192
193			t++;
194			removed = t - buffer;
195			memmove(buffer, t, sizeof(buffer) - left - removed);
196			s -= removed;
197			left += removed;
198
199		} while (1);
200	}
201
202done:
203	if (myurl != NULL)
204		free(myurl);
205	if (fd != -1)
206		close(fd);
207	return rtop;
208}
209