1255332Scy/* $FreeBSD$ */
2255332Scy
3170263Sdarrenr/*
4255332Scy * Copyright (C) 2012 by Darren Reed.
5170263Sdarrenr *
6170263Sdarrenr * See the IPFILTER.LICENCE file for details on licencing.
7170263Sdarrenr *
8255332Scy * $Id: load_http.c,v 1.5.2.5 2012/07/22 08:04:24 darren_r Exp $
9170263Sdarrenr */
10170263Sdarrenr
11170263Sdarrenr#include "ipf.h"
12255332Scy#include <ctype.h>
13170263Sdarrenr
14170263Sdarrenr/*
15255332Scy * Because the URL can be included twice into the buffer, once as the
16255332Scy * full path for the "GET" and once as the "Host:", the buffer it is
17255332Scy * put in needs to be larger than 512*2 to make room for the supporting
18255332Scy * text. Why not just use snprintf and truncate? The warning about the
19255332Scy * URL being too long tells you something is wrong and does not fetch
20255332Scy * any data - just truncating the URL (with snprintf, etc) and sending
21255332Scy * that to the server is allowing an unknown and unintentioned action
22255332Scy * to happen.
23255332Scy */
24255332Scy#define	MAX_URL_LEN	512
25255332Scy#define	LOAD_BUFSIZE	(MAX_URL_LEN * 2 + 128)
26255332Scy
27255332Scy/*
28170263Sdarrenr * Format expected is one addres per line, at the start of each line.
29170263Sdarrenr */
30170263Sdarrenralist_t *
31170263Sdarrenrload_http(char *url)
32170263Sdarrenr{
33255332Scy	int fd, len, left, port, endhdr, removed, linenum = 0;
34255332Scy	char *s, *t, *u, buffer[LOAD_BUFSIZE], *myurl;
35170263Sdarrenr	alist_t *a, *rtop, *rbot;
36193043Sstas	size_t avail;
37193043Sstas	int error;
38170263Sdarrenr
39170263Sdarrenr	/*
40170263Sdarrenr	 * More than this would just be absurd.
41170263Sdarrenr	 */
42255332Scy	if (strlen(url) > MAX_URL_LEN) {
43255332Scy		fprintf(stderr, "load_http has a URL > %d bytes?!\n",
44255332Scy			MAX_URL_LEN);
45170263Sdarrenr		return NULL;
46170263Sdarrenr	}
47170263Sdarrenr
48170263Sdarrenr	fd = -1;
49170263Sdarrenr	rtop = NULL;
50170263Sdarrenr	rbot = NULL;
51170263Sdarrenr
52193043Sstas	avail = sizeof(buffer);
53193043Sstas	error = snprintf(buffer, avail, "GET %s HTTP/1.0\r\n", url);
54170263Sdarrenr
55193043Sstas	/*
56193043Sstas	 * error is always less then avail due to the constraint on
57193043Sstas	 * the url length above.
58193043Sstas	 */
59193043Sstas	avail -= error;
60193043Sstas
61170263Sdarrenr	myurl = strdup(url);
62170263Sdarrenr	if (myurl == NULL)
63170263Sdarrenr		goto done;
64170263Sdarrenr
65170263Sdarrenr	s = myurl + 7;			/* http:// */
66170263Sdarrenr	t = strchr(s, '/');
67170263Sdarrenr	if (t == NULL) {
68170263Sdarrenr		fprintf(stderr, "load_http has a malformed URL '%s'\n", url);
69170263Sdarrenr		free(myurl);
70170263Sdarrenr		return NULL;
71170263Sdarrenr	}
72170263Sdarrenr	*t++ = '\0';
73170263Sdarrenr
74255332Scy	/*
75255332Scy	 * 10 is the length of 'Host: \r\n\r\n' below.
76255332Scy	 */
77255332Scy	if (strlen(s) + strlen(buffer) + 10 > sizeof(buffer)) {
78255332Scy		fprintf(stderr, "load_http has a malformed URL '%s'\n", url);
79255332Scy		free(myurl);
80255332Scy		return NULL;
81255332Scy	}
82255332Scy
83170263Sdarrenr	u = strchr(s, '@');
84170263Sdarrenr	if (u != NULL)
85170263Sdarrenr		s = u + 1;		/* AUTH */
86170263Sdarrenr
87193043Sstas	error = snprintf(buffer + strlen(buffer), avail, "Host: %s\r\n\r\n", s);
88193043Sstas	if (error >= avail) {
89193043Sstas		fprintf(stderr, "URL is too large: %s\n", url);
90193043Sstas		goto done;
91193043Sstas	}
92170263Sdarrenr
93170263Sdarrenr	u = strchr(s, ':');
94170263Sdarrenr	if (u != NULL) {
95170263Sdarrenr		*u++ = '\0';
96170263Sdarrenr		port = atoi(u);
97170263Sdarrenr		if (port < 0 || port > 65535)
98170263Sdarrenr			goto done;
99170263Sdarrenr	} else {
100170263Sdarrenr		port = 80;
101170263Sdarrenr	}
102170263Sdarrenr
103170263Sdarrenr
104255332Scy	fd = connecttcp(s, port);
105170263Sdarrenr	if (fd == -1)
106170263Sdarrenr		goto done;
107170263Sdarrenr
108170263Sdarrenr
109170263Sdarrenr	len = strlen(buffer);
110193043Sstas	if (write(fd, buffer, len) != len)
111170263Sdarrenr		goto done;
112170263Sdarrenr
113170263Sdarrenr	s = buffer;
114170263Sdarrenr	endhdr = 0;
115170263Sdarrenr	left = sizeof(buffer) - 1;
116170263Sdarrenr
117170263Sdarrenr	while ((len = read(fd, s, left)) > 0) {
118170263Sdarrenr		s[len] = '\0';
119170263Sdarrenr		left -= len;
120170263Sdarrenr		s += len;
121170263Sdarrenr
122170263Sdarrenr		if (endhdr >= 0) {
123170263Sdarrenr			if (endhdr == 0) {
124170263Sdarrenr				t = strchr(buffer, ' ');
125170263Sdarrenr				if (t == NULL)
126170263Sdarrenr					continue;
127170263Sdarrenr				t++;
128170263Sdarrenr				if (*t != '2')
129170263Sdarrenr					break;
130170263Sdarrenr			}
131170263Sdarrenr
132170263Sdarrenr			u = buffer;
133170263Sdarrenr			while ((t = strchr(u, '\r')) != NULL) {
134170263Sdarrenr				if (t == u) {
135170263Sdarrenr					if (*(t + 1) == '\n') {
136170263Sdarrenr						u = t + 2;
137170263Sdarrenr						endhdr = -1;
138170263Sdarrenr						break;
139170263Sdarrenr					} else
140170263Sdarrenr						t++;
141170263Sdarrenr				} else if (*(t + 1) == '\n') {
142170263Sdarrenr					endhdr++;
143170263Sdarrenr					u = t + 2;
144170263Sdarrenr				} else
145170263Sdarrenr					u = t + 1;
146170263Sdarrenr			}
147170263Sdarrenr			if (endhdr >= 0)
148170263Sdarrenr				continue;
149170263Sdarrenr			removed = (u - buffer) + 1;
150170263Sdarrenr			memmove(buffer, u, (sizeof(buffer) - left) - removed);
151170263Sdarrenr			s -= removed;
152170263Sdarrenr			left += removed;
153170263Sdarrenr		}
154170263Sdarrenr
155170263Sdarrenr		do {
156170263Sdarrenr			t = strchr(buffer, '\n');
157170263Sdarrenr			if (t == NULL)
158170263Sdarrenr				break;
159170263Sdarrenr
160255332Scy			linenum++;
161255332Scy			*t = '\0';
162170263Sdarrenr
163255332Scy			/*
164255332Scy			 * Remove comment and continue to the next line if
165255332Scy			 * the comment is at the start of the line.
166255332Scy			 */
167255332Scy			u = strchr(buffer, '#');
168255332Scy			if (u != NULL) {
169255332Scy				*u = '\0';
170255332Scy				if (u == buffer)
171255332Scy					continue;
172170263Sdarrenr			}
173170263Sdarrenr
174255332Scy			/*
175255332Scy			 * Trim off tailing white spaces, will include \r
176255332Scy			 */
177255332Scy			for (u = t - 1; (u >= buffer) && ISSPACE(*u); u--)
178255332Scy				*u = '\0';
179255332Scy
180255332Scy			a = alist_new(AF_UNSPEC, buffer);
181170263Sdarrenr			if (a != NULL) {
182170263Sdarrenr				if (rbot != NULL)
183170263Sdarrenr					rbot->al_next = a;
184170263Sdarrenr				else
185170263Sdarrenr					rtop = a;
186170263Sdarrenr				rbot = a;
187255332Scy			} else {
188255332Scy				fprintf(stderr,
189255332Scy					"%s:%d unrecognised content:%s\n",
190255332Scy					url, linenum, buffer);
191170263Sdarrenr			}
192170263Sdarrenr
193255332Scy			t++;
194170263Sdarrenr			removed = t - buffer;
195170263Sdarrenr			memmove(buffer, t, sizeof(buffer) - left - removed);
196170263Sdarrenr			s -= removed;
197170263Sdarrenr			left += removed;
198170263Sdarrenr
199170263Sdarrenr		} while (1);
200170263Sdarrenr	}
201170263Sdarrenr
202170263Sdarrenrdone:
203170263Sdarrenr	if (myurl != NULL)
204170263Sdarrenr		free(myurl);
205170263Sdarrenr	if (fd != -1)
206170263Sdarrenr		close(fd);
207170263Sdarrenr	return rtop;
208170263Sdarrenr}
209