Branch data Line data Source code
1 : : /*
2 : : * http_client - HTTP client
3 : : * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
4 : : *
5 : : * This software may be distributed under the terms of the BSD license.
6 : : * See README for more details.
7 : : */
8 : :
9 : : #include "includes.h"
10 : : #include <fcntl.h>
11 : :
12 : : #include "common.h"
13 : : #include "eloop.h"
14 : : #include "httpread.h"
15 : : #include "http_client.h"
16 : :
17 : :
18 : : #define HTTP_CLIENT_TIMEOUT_SEC 30
19 : :
20 : :
21 : : struct http_client {
22 : : struct sockaddr_in dst;
23 : : int sd;
24 : : struct wpabuf *req;
25 : : size_t req_pos;
26 : : size_t max_response;
27 : :
28 : : void (*cb)(void *ctx, struct http_client *c,
29 : : enum http_client_event event);
30 : : void *cb_ctx;
31 : : struct httpread *hread;
32 : : struct wpabuf body;
33 : : };
34 : :
35 : :
36 : 0 : static void http_client_timeout(void *eloop_data, void *user_ctx)
37 : : {
38 : 0 : struct http_client *c = eloop_data;
39 : 0 : wpa_printf(MSG_DEBUG, "HTTP: Timeout (c=%p)", c);
40 : 0 : c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
41 : 0 : }
42 : :
43 : :
44 : 39 : static void http_client_got_response(struct httpread *handle, void *cookie,
45 : : enum httpread_event e)
46 : : {
47 : 39 : struct http_client *c = cookie;
48 : :
49 : 39 : wpa_printf(MSG_DEBUG, "HTTP: httpread callback: handle=%p cookie=%p "
50 : : "e=%d", handle, cookie, e);
51 : :
52 : 39 : eloop_cancel_timeout(http_client_timeout, c, NULL);
53 [ + - - - ]: 39 : switch (e) {
54 : : case HTTPREAD_EVENT_FILE_READY:
55 [ + - ]: 39 : if (httpread_hdr_type_get(c->hread) == HTTPREAD_HDR_TYPE_REPLY)
56 : : {
57 : 39 : int reply_code = httpread_reply_code_get(c->hread);
58 [ + + ]: 39 : if (reply_code == 200 /* OK */) {
59 : 38 : wpa_printf(MSG_DEBUG, "HTTP: Response OK from "
60 : : "%s:%d",
61 : : inet_ntoa(c->dst.sin_addr),
62 : 38 : ntohs(c->dst.sin_port));
63 : 38 : c->cb(c->cb_ctx, c, HTTP_CLIENT_OK);
64 : : } else {
65 : 1 : wpa_printf(MSG_DEBUG, "HTTP: Error %d from "
66 : : "%s:%d", reply_code,
67 : : inet_ntoa(c->dst.sin_addr),
68 : 1 : ntohs(c->dst.sin_port));
69 : 1 : c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
70 : : }
71 : : } else
72 : 0 : c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
73 : 39 : break;
74 : : case HTTPREAD_EVENT_TIMEOUT:
75 : 0 : c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
76 : 0 : break;
77 : : case HTTPREAD_EVENT_ERROR:
78 : 0 : c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
79 : 0 : break;
80 : : }
81 : 39 : }
82 : :
83 : :
84 : 42 : static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
85 : : {
86 : 42 : struct http_client *c = eloop_ctx;
87 : : int res;
88 : :
89 : 84 : wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu "
90 : : "bytes remaining)",
91 : 42 : inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port),
92 : 42 : (unsigned long) wpabuf_len(c->req),
93 : 42 : (unsigned long) wpabuf_len(c->req) - c->req_pos);
94 : :
95 : 42 : res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos,
96 : 42 : wpabuf_len(c->req) - c->req_pos, 0);
97 [ + + ]: 42 : if (res < 0) {
98 : 3 : wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s",
99 : 3 : strerror(errno));
100 : 3 : eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
101 : 3 : c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
102 : 3 : return;
103 : : }
104 : :
105 [ - + ]: 39 : if ((size_t) res < wpabuf_len(c->req) - c->req_pos) {
106 : 0 : wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes "
107 : : "remaining",
108 : 0 : res, (unsigned long) wpabuf_len(c->req),
109 : 0 : (unsigned long) wpabuf_len(c->req) - c->req_pos -
110 : : res);
111 : 0 : c->req_pos += res;
112 : 0 : return;
113 : : }
114 : :
115 : 39 : wpa_printf(MSG_DEBUG, "HTTP: Full client request sent to %s:%d",
116 : 39 : inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port));
117 : 39 : eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
118 : 39 : wpabuf_free(c->req);
119 : 39 : c->req = NULL;
120 : :
121 : 39 : c->hread = httpread_create(c->sd, http_client_got_response, c,
122 : 39 : c->max_response, HTTP_CLIENT_TIMEOUT_SEC);
123 [ - + ]: 39 : if (c->hread == NULL) {
124 : 0 : c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
125 : 42 : return;
126 : : }
127 : : }
128 : :
129 : :
130 : 42 : struct http_client * http_client_addr(struct sockaddr_in *dst,
131 : : struct wpabuf *req, size_t max_response,
132 : : void (*cb)(void *ctx,
133 : : struct http_client *c,
134 : : enum http_client_event event),
135 : : void *cb_ctx)
136 : : {
137 : : struct http_client *c;
138 : :
139 : 42 : c = os_zalloc(sizeof(*c));
140 [ - + ]: 42 : if (c == NULL)
141 : 0 : return NULL;
142 : 42 : c->sd = -1;
143 : 42 : c->dst = *dst;
144 : 42 : c->max_response = max_response;
145 : 42 : c->cb = cb;
146 : 42 : c->cb_ctx = cb_ctx;
147 : :
148 : 42 : c->sd = socket(AF_INET, SOCK_STREAM, 0);
149 [ - + ]: 42 : if (c->sd < 0) {
150 : 0 : http_client_free(c);
151 : 0 : return NULL;
152 : : }
153 : :
154 [ - + ]: 42 : if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) {
155 : 0 : wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s",
156 : 0 : strerror(errno));
157 : 0 : http_client_free(c);
158 : 0 : return NULL;
159 : : }
160 : :
161 [ + - ]: 42 : if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) {
162 [ - + ]: 42 : if (errno != EINPROGRESS) {
163 : 0 : wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s",
164 : 0 : strerror(errno));
165 : 0 : http_client_free(c);
166 : 0 : return NULL;
167 : : }
168 : :
169 : : /*
170 : : * Continue connecting in the background; eloop will call us
171 : : * once the connection is ready (or failed).
172 : : */
173 : : }
174 : :
175 [ - + ]: 42 : if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready,
176 : : c, NULL)) {
177 : 0 : http_client_free(c);
178 : 0 : return NULL;
179 : : }
180 : :
181 [ - + ]: 42 : if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0,
182 : : http_client_timeout, c, NULL)) {
183 : 0 : http_client_free(c);
184 : 0 : return NULL;
185 : : }
186 : :
187 : 42 : c->req = req;
188 : :
189 : 42 : return c;
190 : : }
191 : :
192 : :
193 : 42 : char * http_client_url_parse(const char *url, struct sockaddr_in *dst,
194 : : char **ret_path)
195 : : {
196 : : char *u, *addr, *port, *path;
197 : :
198 : 42 : u = os_strdup(url);
199 [ - + ]: 42 : if (u == NULL)
200 : 0 : return NULL;
201 : :
202 : 42 : os_memset(dst, 0, sizeof(*dst));
203 : 42 : dst->sin_family = AF_INET;
204 : 42 : addr = u + 7;
205 : 42 : path = os_strchr(addr, '/');
206 : 42 : port = os_strchr(addr, ':');
207 [ - + ]: 42 : if (path == NULL) {
208 : 0 : path = "/";
209 : : } else {
210 : 42 : *path = '\0'; /* temporary nul termination for address */
211 [ - + ]: 42 : if (port > path)
212 : 0 : port = NULL;
213 : : }
214 [ + - ]: 42 : if (port)
215 : 42 : *port++ = '\0';
216 : :
217 [ - + ]: 42 : if (inet_aton(addr, &dst->sin_addr) == 0) {
218 : : /* TODO: name lookup */
219 : 0 : wpa_printf(MSG_DEBUG, "HTTP: Unsupported address in URL '%s' "
220 : : "(addr='%s' port='%s')",
221 : : url, addr, port);
222 : 0 : os_free(u);
223 : 0 : return NULL;
224 : : }
225 : :
226 [ + - ]: 42 : if (port)
227 : 42 : dst->sin_port = htons(atoi(port));
228 : : else
229 : 0 : dst->sin_port = htons(80);
230 : :
231 [ + - ]: 42 : if (*path == '\0') {
232 : : /* remove temporary nul termination for address */
233 : 42 : *path = '/';
234 : : }
235 : :
236 : 42 : *ret_path = path;
237 : :
238 : 42 : return u;
239 : : }
240 : :
241 : :
242 : 4 : struct http_client * http_client_url(const char *url,
243 : : struct wpabuf *req, size_t max_response,
244 : : void (*cb)(void *ctx,
245 : : struct http_client *c,
246 : : enum http_client_event event),
247 : : void *cb_ctx)
248 : : {
249 : : struct sockaddr_in dst;
250 : : struct http_client *c;
251 : : char *u, *path;
252 : 4 : struct wpabuf *req_buf = NULL;
253 : :
254 [ - + ]: 4 : if (os_strncmp(url, "http://", 7) != 0)
255 : 0 : return NULL;
256 : 4 : u = http_client_url_parse(url, &dst, &path);
257 [ - + ]: 4 : if (u == NULL)
258 : 0 : return NULL;
259 : :
260 [ + - ]: 4 : if (req == NULL) {
261 : 4 : req_buf = wpabuf_alloc(os_strlen(url) + 1000);
262 [ - + ]: 4 : if (req_buf == NULL) {
263 : 0 : os_free(u);
264 : 0 : return NULL;
265 : : }
266 : 4 : req = req_buf;
267 : 4 : wpabuf_printf(req,
268 : : "GET %s HTTP/1.1\r\n"
269 : : "Cache-Control: no-cache\r\n"
270 : : "Pragma: no-cache\r\n"
271 : : "Accept: text/xml, application/xml\r\n"
272 : : "User-Agent: wpa_supplicant\r\n"
273 : : "Host: %s:%d\r\n"
274 : : "\r\n",
275 : : path, inet_ntoa(dst.sin_addr),
276 : 4 : ntohs(dst.sin_port));
277 : : }
278 : 4 : os_free(u);
279 : :
280 : 4 : c = http_client_addr(&dst, req, max_response, cb, cb_ctx);
281 [ - + ]: 4 : if (c == NULL) {
282 : 0 : wpabuf_free(req_buf);
283 : 0 : return NULL;
284 : : }
285 : :
286 : 4 : return c;
287 : : }
288 : :
289 : :
290 : 57 : void http_client_free(struct http_client *c)
291 : : {
292 [ + + ]: 57 : if (c == NULL)
293 : 57 : return;
294 : 42 : httpread_destroy(c->hread);
295 : 42 : wpabuf_free(c->req);
296 [ + - ]: 42 : if (c->sd >= 0) {
297 : 42 : eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
298 : 42 : close(c->sd);
299 : : }
300 : 42 : eloop_cancel_timeout(http_client_timeout, c, NULL);
301 : 42 : os_free(c);
302 : : }
303 : :
304 : :
305 : 17 : struct wpabuf * http_client_get_body(struct http_client *c)
306 : : {
307 [ - + ]: 17 : if (c->hread == NULL)
308 : 0 : return NULL;
309 : 17 : wpabuf_set(&c->body, httpread_data_get(c->hread),
310 : 17 : httpread_length_get(c->hread));
311 : 17 : return &c->body;
312 : : }
313 : :
314 : :
315 : 4 : char * http_client_get_hdr_line(struct http_client *c, const char *tag)
316 : : {
317 [ - + ]: 4 : if (c->hread == NULL)
318 : 0 : return NULL;
319 : 4 : return httpread_hdr_line_get(c->hread, tag);
320 : : }
321 : :
322 : :
323 : 12 : char * http_link_update(char *url, const char *base)
324 : : {
325 : : char *n;
326 : : size_t len;
327 : : const char *pos;
328 : :
329 : : /* RFC 2396, Chapter 5.2 */
330 : : /* TODO: consider adding all cases described in RFC 2396 */
331 : :
332 [ - + ]: 12 : if (url == NULL)
333 : 0 : return NULL;
334 : :
335 [ - + ]: 12 : if (os_strncmp(url, "http://", 7) == 0)
336 : 0 : return url; /* absolute link */
337 : :
338 [ - + ]: 12 : if (os_strncmp(base, "http://", 7) != 0)
339 : 0 : return url; /* unable to handle base URL */
340 : :
341 : 12 : len = os_strlen(url) + 1 + os_strlen(base) + 1;
342 : 12 : n = os_malloc(len);
343 [ - + ]: 12 : if (n == NULL)
344 : 0 : return url; /* failed */
345 : :
346 [ - + ]: 12 : if (url[0] == '/') {
347 : 0 : pos = os_strchr(base + 7, '/');
348 [ # # ]: 0 : if (pos == NULL) {
349 : 0 : os_snprintf(n, len, "%s%s", base, url);
350 : : } else {
351 : 0 : os_memcpy(n, base, pos - base);
352 : 0 : os_memcpy(n + (pos - base), url, os_strlen(url) + 1);
353 : : }
354 : : } else {
355 : 12 : pos = os_strrchr(base + 7, '/');
356 [ - + ]: 12 : if (pos == NULL) {
357 : 0 : os_snprintf(n, len, "%s/%s", base, url);
358 : : } else {
359 : 12 : os_memcpy(n, base, pos - base + 1);
360 : 12 : os_memcpy(n + (pos - base) + 1, url, os_strlen(url) +
361 : : 1);
362 : : }
363 : : }
364 : :
365 : 12 : os_free(url);
366 : :
367 : 12 : return n;
368 : : }
|