~ubuntu-branches/ubuntu/saucy/wpasupplicant/saucy

« back to all changes in this revision

Viewing changes to src/wps/http_client.c

  • Committer: Bazaar Package Importer
  • Author(s): Mathieu Trudel-Lapierre
  • Date: 2010-11-22 09:43:43 UTC
  • mfrom: (1.1.16 upstream)
  • Revision ID: james.westby@ubuntu.com-20101122094343-qgsxaojvmswfri77
Tags: 0.7.3-0ubuntu1
* Get wpasupplicant 0.7.3 from Debian's SVN. Leaving 0.7.3-1 as unreleased
  for now.
* Build-Depend on debhelper 8, since the packaging from Debian uses compat 8.

Show diffs side-by-side

added added

removed removed

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