http_client.c revision 214501
1214501Srpaulo/*
2214501Srpaulo * http_client - HTTP client
3214501Srpaulo * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
4214501Srpaulo *
5214501Srpaulo * This program is free software; you can redistribute it and/or modify
6214501Srpaulo * it under the terms of the GNU General Public License version 2 as
7214501Srpaulo * published by the Free Software Foundation.
8214501Srpaulo *
9214501Srpaulo * Alternatively, this software may be distributed under the terms of BSD
10214501Srpaulo * license.
11214501Srpaulo *
12214501Srpaulo * See README and COPYING for more details.
13214501Srpaulo */
14214501Srpaulo
15214501Srpaulo#include "includes.h"
16214501Srpaulo#include <fcntl.h>
17214501Srpaulo
18214501Srpaulo#include "common.h"
19214501Srpaulo#include "eloop.h"
20214501Srpaulo#include "httpread.h"
21214501Srpaulo#include "http_client.h"
22214501Srpaulo
23214501Srpaulo
24214501Srpaulo#define HTTP_CLIENT_TIMEOUT 30
25214501Srpaulo
26214501Srpaulo
27214501Srpaulostruct http_client {
28214501Srpaulo	struct sockaddr_in dst;
29214501Srpaulo	int sd;
30214501Srpaulo	struct wpabuf *req;
31214501Srpaulo	size_t req_pos;
32214501Srpaulo	size_t max_response;
33214501Srpaulo
34214501Srpaulo	void (*cb)(void *ctx, struct http_client *c,
35214501Srpaulo		   enum http_client_event event);
36214501Srpaulo	void *cb_ctx;
37214501Srpaulo	struct httpread *hread;
38214501Srpaulo	struct wpabuf body;
39214501Srpaulo};
40214501Srpaulo
41214501Srpaulo
42214501Srpaulostatic void http_client_timeout(void *eloop_data, void *user_ctx)
43214501Srpaulo{
44214501Srpaulo	struct http_client *c = eloop_data;
45214501Srpaulo	wpa_printf(MSG_DEBUG, "HTTP: Timeout");
46214501Srpaulo	c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
47214501Srpaulo}
48214501Srpaulo
49214501Srpaulo
50214501Srpaulostatic void http_client_got_response(struct httpread *handle, void *cookie,
51214501Srpaulo				     enum httpread_event e)
52214501Srpaulo{
53214501Srpaulo	struct http_client *c = cookie;
54214501Srpaulo
55214501Srpaulo	eloop_cancel_timeout(http_client_timeout, c, NULL);
56214501Srpaulo	switch (e) {
57214501Srpaulo	case HTTPREAD_EVENT_FILE_READY:
58214501Srpaulo		if (httpread_hdr_type_get(c->hread) == HTTPREAD_HDR_TYPE_REPLY)
59214501Srpaulo		{
60214501Srpaulo			int reply_code = httpread_reply_code_get(c->hread);
61214501Srpaulo			if (reply_code == 200 /* OK */) {
62214501Srpaulo				wpa_printf(MSG_DEBUG, "HTTP: Response OK from "
63214501Srpaulo					   "%s:%d",
64214501Srpaulo					   inet_ntoa(c->dst.sin_addr),
65214501Srpaulo					   ntohs(c->dst.sin_port));
66214501Srpaulo				c->cb(c->cb_ctx, c, HTTP_CLIENT_OK);
67214501Srpaulo			} else {
68214501Srpaulo				wpa_printf(MSG_DEBUG, "HTTP: Error %d from "
69214501Srpaulo					   "%s:%d", reply_code,
70214501Srpaulo					   inet_ntoa(c->dst.sin_addr),
71214501Srpaulo					   ntohs(c->dst.sin_port));
72214501Srpaulo				c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
73214501Srpaulo			}
74214501Srpaulo		} else
75214501Srpaulo			c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
76214501Srpaulo		break;
77214501Srpaulo	case HTTPREAD_EVENT_TIMEOUT:
78214501Srpaulo		c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
79214501Srpaulo		break;
80214501Srpaulo	case HTTPREAD_EVENT_ERROR:
81214501Srpaulo		c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
82214501Srpaulo		break;
83214501Srpaulo	}
84214501Srpaulo}
85214501Srpaulo
86214501Srpaulo
87214501Srpaulostatic void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
88214501Srpaulo{
89214501Srpaulo	struct http_client *c = eloop_ctx;
90214501Srpaulo	int res;
91214501Srpaulo
92214501Srpaulo	wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu "
93214501Srpaulo		   "bytes remaining)",
94214501Srpaulo		   inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port),
95214501Srpaulo		   (unsigned long) wpabuf_len(c->req),
96214501Srpaulo		   (unsigned long) wpabuf_len(c->req) - c->req_pos);
97214501Srpaulo
98214501Srpaulo	res = send(c->sd, wpabuf_head(c->req) + c->req_pos,
99214501Srpaulo		   wpabuf_len(c->req) - c->req_pos, 0);
100214501Srpaulo	if (res < 0) {
101214501Srpaulo		wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s",
102214501Srpaulo			   strerror(errno));
103214501Srpaulo		eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
104214501Srpaulo		c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
105214501Srpaulo		return;
106214501Srpaulo	}
107214501Srpaulo
108214501Srpaulo	if ((size_t) res < wpabuf_len(c->req) - c->req_pos) {
109214501Srpaulo		wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes "
110214501Srpaulo			   "remaining",
111214501Srpaulo			   res, (unsigned long) wpabuf_len(c->req),
112214501Srpaulo			   (unsigned long) wpabuf_len(c->req) - c->req_pos -
113214501Srpaulo			   res);
114214501Srpaulo		c->req_pos += res;
115214501Srpaulo		return;
116214501Srpaulo	}
117214501Srpaulo
118214501Srpaulo	wpa_printf(MSG_DEBUG, "HTTP: Full client request sent to %s:%d",
119214501Srpaulo		   inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port));
120214501Srpaulo	eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
121214501Srpaulo	wpabuf_free(c->req);
122214501Srpaulo	c->req = NULL;
123214501Srpaulo
124214501Srpaulo	c->hread = httpread_create(c->sd, http_client_got_response, c,
125214501Srpaulo				   c->max_response, HTTP_CLIENT_TIMEOUT);
126214501Srpaulo	if (c->hread == NULL) {
127214501Srpaulo		c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
128214501Srpaulo		return;
129214501Srpaulo	}
130214501Srpaulo}
131214501Srpaulo
132214501Srpaulo
133214501Srpaulostruct http_client * http_client_addr(struct sockaddr_in *dst,
134214501Srpaulo				      struct wpabuf *req, size_t max_response,
135214501Srpaulo				      void (*cb)(void *ctx,
136214501Srpaulo						 struct http_client *c,
137214501Srpaulo						 enum http_client_event event),
138214501Srpaulo				      void *cb_ctx)
139214501Srpaulo{
140214501Srpaulo	struct http_client *c;
141214501Srpaulo
142214501Srpaulo	c = os_zalloc(sizeof(*c));
143214501Srpaulo	if (c == NULL)
144214501Srpaulo		return NULL;
145214501Srpaulo	c->sd = -1;
146214501Srpaulo	c->dst = *dst;
147214501Srpaulo	c->max_response = max_response;
148214501Srpaulo	c->cb = cb;
149214501Srpaulo	c->cb_ctx = cb_ctx;
150214501Srpaulo
151214501Srpaulo	c->sd = socket(AF_INET, SOCK_STREAM, 0);
152214501Srpaulo	if (c->sd < 0) {
153214501Srpaulo		http_client_free(c);
154214501Srpaulo		return NULL;
155214501Srpaulo	}
156214501Srpaulo
157214501Srpaulo	if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) {
158214501Srpaulo		wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s",
159214501Srpaulo			   strerror(errno));
160214501Srpaulo		http_client_free(c);
161214501Srpaulo		return NULL;
162214501Srpaulo	}
163214501Srpaulo
164214501Srpaulo	if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) {
165214501Srpaulo		if (errno != EINPROGRESS) {
166214501Srpaulo			wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s",
167214501Srpaulo				   strerror(errno));
168214501Srpaulo			http_client_free(c);
169214501Srpaulo			return NULL;
170214501Srpaulo		}
171214501Srpaulo
172214501Srpaulo		/*
173214501Srpaulo		 * Continue connecting in the background; eloop will call us
174214501Srpaulo		 * once the connection is ready (or failed).
175214501Srpaulo		 */
176214501Srpaulo	}
177214501Srpaulo
178214501Srpaulo	if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready,
179214501Srpaulo				c, NULL)) {
180214501Srpaulo		http_client_free(c);
181214501Srpaulo		return NULL;
182214501Srpaulo	}
183214501Srpaulo
184214501Srpaulo	if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT, 0, http_client_timeout,
185214501Srpaulo				   c, NULL)) {
186214501Srpaulo		http_client_free(c);
187214501Srpaulo		return NULL;
188214501Srpaulo	}
189214501Srpaulo
190214501Srpaulo	c->req = req;
191214501Srpaulo
192214501Srpaulo	return c;
193214501Srpaulo}
194214501Srpaulo
195214501Srpaulo
196214501Srpaulochar * http_client_url_parse(const char *url, struct sockaddr_in *dst,
197214501Srpaulo			     char **ret_path)
198214501Srpaulo{
199214501Srpaulo	char *u, *addr, *port, *path;
200214501Srpaulo
201214501Srpaulo	u = os_strdup(url);
202214501Srpaulo	if (u == NULL)
203214501Srpaulo		return NULL;
204214501Srpaulo
205214501Srpaulo	os_memset(dst, 0, sizeof(*dst));
206214501Srpaulo	dst->sin_family = AF_INET;
207214501Srpaulo	addr = u + 7;
208214501Srpaulo	path = os_strchr(addr, '/');
209214501Srpaulo	port = os_strchr(addr, ':');
210214501Srpaulo	if (path == NULL) {
211214501Srpaulo		path = "/";
212214501Srpaulo	} else {
213214501Srpaulo		*path = '\0'; /* temporary nul termination for address */
214214501Srpaulo		if (port > path)
215214501Srpaulo			port = NULL;
216214501Srpaulo	}
217214501Srpaulo	if (port)
218214501Srpaulo		*port++ = '\0';
219214501Srpaulo
220214501Srpaulo	if (inet_aton(addr, &dst->sin_addr) == 0) {
221214501Srpaulo		/* TODO: name lookup */
222214501Srpaulo		wpa_printf(MSG_DEBUG, "HTTP: Unsupported address in URL '%s' "
223214501Srpaulo			   "(addr='%s' port='%s')",
224214501Srpaulo			   url, addr, port);
225214501Srpaulo		os_free(u);
226214501Srpaulo		return NULL;
227214501Srpaulo	}
228214501Srpaulo
229214501Srpaulo	if (port)
230214501Srpaulo		dst->sin_port = htons(atoi(port));
231214501Srpaulo	else
232214501Srpaulo		dst->sin_port = htons(80);
233214501Srpaulo
234214501Srpaulo	if (*path == '\0') {
235214501Srpaulo		/* remove temporary nul termination for address */
236214501Srpaulo		*path = '/';
237214501Srpaulo	}
238214501Srpaulo
239214501Srpaulo	*ret_path = path;
240214501Srpaulo
241214501Srpaulo	return u;
242214501Srpaulo}
243214501Srpaulo
244214501Srpaulo
245214501Srpaulostruct http_client * http_client_url(const char *url,
246214501Srpaulo				     struct wpabuf *req, size_t max_response,
247214501Srpaulo				     void (*cb)(void *ctx,
248214501Srpaulo						struct http_client *c,
249214501Srpaulo						enum http_client_event event),
250214501Srpaulo				     void *cb_ctx)
251214501Srpaulo{
252214501Srpaulo	struct sockaddr_in dst;
253214501Srpaulo	struct http_client *c;
254214501Srpaulo	char *u, *path;
255214501Srpaulo	struct wpabuf *req_buf = NULL;
256214501Srpaulo
257214501Srpaulo	if (os_strncmp(url, "http://", 7) != 0)
258214501Srpaulo		return NULL;
259214501Srpaulo	u = http_client_url_parse(url, &dst, &path);
260214501Srpaulo	if (u == NULL)
261214501Srpaulo		return NULL;
262214501Srpaulo
263214501Srpaulo	if (req == NULL) {
264214501Srpaulo		req_buf = wpabuf_alloc(os_strlen(url) + 1000);
265214501Srpaulo		if (req_buf == NULL) {
266214501Srpaulo			os_free(u);
267214501Srpaulo			return NULL;
268214501Srpaulo		}
269214501Srpaulo		req = req_buf;
270214501Srpaulo		wpabuf_printf(req,
271214501Srpaulo			      "GET %s HTTP/1.1\r\n"
272214501Srpaulo			      "Cache-Control: no-cache\r\n"
273214501Srpaulo			      "Pragma: no-cache\r\n"
274214501Srpaulo			      "Accept: text/xml, application/xml\r\n"
275214501Srpaulo			      "User-Agent: wpa_supplicant\r\n"
276214501Srpaulo			      "Host: %s:%d\r\n"
277214501Srpaulo			      "\r\n",
278214501Srpaulo			      path, inet_ntoa(dst.sin_addr),
279214501Srpaulo			      ntohs(dst.sin_port));
280214501Srpaulo	}
281214501Srpaulo	os_free(u);
282214501Srpaulo
283214501Srpaulo	c = http_client_addr(&dst, req, max_response, cb, cb_ctx);
284214501Srpaulo	if (c == NULL) {
285214501Srpaulo		wpabuf_free(req_buf);
286214501Srpaulo		return NULL;
287214501Srpaulo	}
288214501Srpaulo
289214501Srpaulo	return c;
290214501Srpaulo}
291214501Srpaulo
292214501Srpaulo
293214501Srpaulovoid http_client_free(struct http_client *c)
294214501Srpaulo{
295214501Srpaulo	if (c == NULL)
296214501Srpaulo		return;
297214501Srpaulo	httpread_destroy(c->hread);
298214501Srpaulo	wpabuf_free(c->req);
299214501Srpaulo	if (c->sd >= 0) {
300214501Srpaulo		eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
301214501Srpaulo		close(c->sd);
302214501Srpaulo	}
303214501Srpaulo	eloop_cancel_timeout(http_client_timeout, c, NULL);
304214501Srpaulo	os_free(c);
305214501Srpaulo}
306214501Srpaulo
307214501Srpaulo
308214501Srpaulostruct wpabuf * http_client_get_body(struct http_client *c)
309214501Srpaulo{
310214501Srpaulo	if (c->hread == NULL)
311214501Srpaulo		return NULL;
312214501Srpaulo	wpabuf_set(&c->body, httpread_data_get(c->hread),
313214501Srpaulo		   httpread_length_get(c->hread));
314214501Srpaulo	return &c->body;
315214501Srpaulo}
316214501Srpaulo
317214501Srpaulo
318214501Srpaulochar * http_client_get_hdr_line(struct http_client *c, const char *tag)
319214501Srpaulo{
320214501Srpaulo	if (c->hread == NULL)
321214501Srpaulo		return NULL;
322214501Srpaulo	return httpread_hdr_line_get(c->hread, tag);
323214501Srpaulo}
324214501Srpaulo
325214501Srpaulo
326214501Srpaulochar * http_link_update(char *url, const char *base)
327214501Srpaulo{
328214501Srpaulo	char *n;
329214501Srpaulo	size_t len;
330214501Srpaulo	const char *pos;
331214501Srpaulo
332214501Srpaulo	/* RFC 2396, Chapter 5.2 */
333214501Srpaulo	/* TODO: consider adding all cases described in RFC 2396 */
334214501Srpaulo
335214501Srpaulo	if (url == NULL)
336214501Srpaulo		return NULL;
337214501Srpaulo
338214501Srpaulo	if (os_strncmp(url, "http://", 7) == 0)
339214501Srpaulo		return url; /* absolute link */
340214501Srpaulo
341214501Srpaulo	if (os_strncmp(base, "http://", 7) != 0)
342214501Srpaulo		return url; /* unable to handle base URL */
343214501Srpaulo
344214501Srpaulo	len = os_strlen(url) + 1 + os_strlen(base) + 1;
345214501Srpaulo	n = os_malloc(len);
346214501Srpaulo	if (n == NULL)
347214501Srpaulo		return url; /* failed */
348214501Srpaulo
349214501Srpaulo	if (url[0] == '/') {
350214501Srpaulo		pos = os_strchr(base + 7, '/');
351214501Srpaulo		if (pos == NULL) {
352214501Srpaulo			os_snprintf(n, len, "%s%s", base, url);
353214501Srpaulo		} else {
354214501Srpaulo			os_memcpy(n, base, pos - base);
355214501Srpaulo			os_memcpy(n + (pos - base), url, os_strlen(url) + 1);
356214501Srpaulo		}
357214501Srpaulo	} else {
358214501Srpaulo		pos = os_strrchr(base + 7, '/');
359214501Srpaulo		if (pos == NULL) {
360214501Srpaulo			os_snprintf(n, len, "%s/%s", base, url);
361214501Srpaulo		} else {
362214501Srpaulo			os_memcpy(n, base, pos - base + 1);
363214501Srpaulo			os_memcpy(n + (pos - base) + 1, url, os_strlen(url) +
364214501Srpaulo				  1);
365214501Srpaulo		}
366214501Srpaulo	}
367214501Srpaulo
368214501Srpaulo	os_free(url);
369214501Srpaulo
370214501Srpaulo	return n;
371214501Srpaulo}
372