1/*
2 * Generic HTTP routines
3 *
4 * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $Id: http.c 435851 2013-11-12 10:17:29Z $
19 */
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <limits.h>
24#include <ctype.h>
25#include <string.h>
26#include <signal.h>
27#include <time.h>
28#include <unistd.h>
29#include <errno.h>
30#include <sys/socket.h>
31#include <netinet/in.h>
32#include <arpa/inet.h>
33
34#include <shutils.h>
35
36static char *
37base64enc(const char *p, char *buf, int len)
38{
39	char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
40		"0123456789+/";
41	char *s = buf;
42
43	while (*p) {
44		if (s >= buf+len-4)
45			break;
46		*(s++) = al[(*p >> 2) & 0x3F];
47		*(s++) = al[((*p << 4) & 0x30) | ((*(p+1) >> 4) & 0x0F)];
48		*s = *(s+1) = '=';
49		*(s+2) = 0;
50		if (! *(++p)) break;
51		*(s++) = al[((*p << 2) & 0x3C) | ((*(p+1) >> 6) & 0x03)];
52		if (! *(++p)) break;
53		*(s++) = al[*(p++) & 0x3F];
54	}
55
56	return buf;
57}
58
59enum {
60	METHOD_GET,
61	METHOD_POST
62};
63
64static int
65wget(int method, const char *server, char *buf, size_t count, off_t offset)
66{
67	char url[PATH_MAX] = { 0 }, *s;
68	char *host = url, *path = "", auth[128] = { 0 }, line[512];
69	unsigned short port = 80;
70	int fd;
71	FILE *fp;
72	struct sockaddr_in sin;
73	int chunked = 0, len = 0;
74
75	if (server == NULL || !strcmp(server, "")) {
76		dprintf("wget: null server input\n");
77		return (0);
78	}
79
80	if (strlen(server) >= sizeof(url)) {
81		dprintf("wget: server url is too long\n");
82		return 0;
83	}
84	strcpy(url, server);
85
86	/* Parse URL */
87	if (!strncmp(url, "http://", 7)) {
88		port = 80;
89		host = url + 7;
90	}
91	if ((s = strchr(host, '/'))) {
92		*s++ = '\0';
93		path = s;
94	}
95	if ((s = strchr(host, '@'))) {
96		*s++ = '\0';
97		base64enc(host, auth, sizeof(auth));
98		host = s;
99	}
100	if ((s = strchr(host, ':'))) {
101		*s++ = '\0';
102		port = atoi(s);
103	}
104
105	/* Open socket */
106	if (!inet_aton(host, &sin.sin_addr))
107		return 0;
108	sin.sin_family = AF_INET;
109	sin.sin_port = htons(port);
110
111	dprintf("Connecting to %s:%u...\n", host, port);
112	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
113	    connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0 ||
114	    !(fp = fdopen(fd, "r+"))) {
115		perror(host);
116		if (fd >= 0)
117			close(fd);
118		return 0;
119	}
120	dprintf("connected!\n");
121
122	/* Send HTTP request */
123	fprintf(fp, "%s /%s HTTP/1.1\r\n", method == METHOD_POST ? "POST" : "GET", path);
124	fprintf(fp, "Host: %s\r\n", host);
125	fprintf(fp, "User-Agent: wget\r\n");
126	if (strlen(auth))
127		fprintf(fp, "Authorization: Basic %s\r\n", auth);
128	if (offset)
129		fprintf(fp, "Range: bytes=%ld-\r\n", offset);
130	if (method == METHOD_POST) {
131		fprintf(fp, "Content-Type: application/x-www-form-urlencoded\r\n");
132		fprintf(fp, "Content-Length: %d\r\n\r\n", (int) strlen(buf));
133		fputs(buf, fp);
134	} else
135		fprintf(fp, "Connection: close\r\n\r\n");
136
137	/* Check HTTP response */
138	dprintf("HTTP request sent, awaiting response...\n");
139	if (fgets(line, sizeof(line), fp)) {
140		dprintf("%s", line);
141		for (s = line; *s && !isspace((int)*s); s++);
142		for (; isspace((int)*s); s++);
143		switch (atoi(s)) {
144		case 200: if (offset) goto done; else break;
145		case 206: if (offset) break; else goto done;
146		default: goto done;
147		}
148	}
149
150	/* Parse headers */
151	while (fgets(line, sizeof(line), fp)) {
152		dprintf("%s", line);
153		for (s = line; *s == '\r'; s++);
154		if (*s == '\n')
155			break;
156		if (!strncasecmp(s, "Content-Length:", 15)) {
157			for (s += 15; isblank(*s); s++);
158			chomp(s);
159			len = atoi(s);
160		}
161		else if (!strncasecmp(s, "Transfer-Encoding:", 18)) {
162			for (s += 18; isblank(*s); s++);
163			chomp(s);
164			if (!strncasecmp(s, "chunked", 7))
165				chunked = 1;
166		}
167	}
168
169	if (chunked && fgets(line, sizeof(line), fp))
170		len = strtol(line, NULL, 16);
171
172	len = (len > count) ? count : len;
173	len = fread(buf, 1, len, fp);
174
175done:
176	/* Close socket */
177	fflush(fp);
178	fclose(fp);
179	return len;
180}
181
182int
183http_get(const char *server, char *buf, size_t count, off_t offset)
184{
185	return wget(METHOD_GET, server, buf, count, offset);
186}
187
188int
189http_post(const char *server, char *buf, size_t count)
190{
191	/* No continuation generally possible with POST */
192	return wget(METHOD_POST, server, buf, count, 0);
193}
194