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