xref: /freebsd/sbin/ipf/libipf/load_http.c (revision 9f23cbd6cae82fd77edfad7173432fa8dccd0a95)
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 address per line, at the start of each line.
29  */
30 alist_t *
31 load_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 
202 done:
203 	if (myurl != NULL)
204 		free(myurl);
205 	if (fd != -1)
206 		close(fd);
207 	return (rtop);
208 }
209