http_client.c
Go to the documentation of this file.
00001 /*
00002  * http_client - HTTP client
00003  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License version 2 as
00007  * published by the Free Software Foundation.
00008  *
00009  * Alternatively, this software may be distributed under the terms of BSD
00010  * license.
00011  *
00012  * See README and COPYING for more details.
00013  */
00014 
00015 #include "includes.h"
00016 #include <fcntl.h>
00017 
00018 #include "common.h"
00019 #include "eloop.h"
00020 #include "httpread.h"
00021 #include "http_client.h"
00022 
00023 
00024 #define HTTP_CLIENT_TIMEOUT 30
00025 
00026 
00027 struct http_client {
00028         struct sockaddr_in dst;
00029         int sd;
00030         struct wpabuf *req;
00031         size_t req_pos;
00032         size_t max_response;
00033 
00034         void (*cb)(void *ctx, struct http_client *c,
00035                    enum http_client_event event);
00036         void *cb_ctx;
00037         struct httpread *hread;
00038         struct wpabuf body;
00039 };
00040 
00041 
00042 static void http_client_timeout(void *eloop_data, void *user_ctx)
00043 {
00044         struct http_client *c = eloop_data;
00045         wpa_printf(MSG_DEBUG, "HTTP: Timeout");
00046         c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
00047 }
00048 
00049 
00050 static void http_client_got_response(struct httpread *handle, void *cookie,
00051                                      enum httpread_event e)
00052 {
00053         struct http_client *c = cookie;
00054 
00055         eloop_cancel_timeout(http_client_timeout, c, NULL);
00056         switch (e) {
00057         case HTTPREAD_EVENT_FILE_READY:
00058                 if (httpread_hdr_type_get(c->hread) == HTTPREAD_HDR_TYPE_REPLY)
00059                 {
00060                         int reply_code = httpread_reply_code_get(c->hread);
00061                         if (reply_code == 200 /* OK */) {
00062                                 wpa_printf(MSG_DEBUG, "HTTP: Response OK from "
00063                                            "%s:%d",
00064                                            inet_ntoa(c->dst.sin_addr),
00065                                            ntohs(c->dst.sin_port));
00066                                 c->cb(c->cb_ctx, c, HTTP_CLIENT_OK);
00067                         } else {
00068                                 wpa_printf(MSG_DEBUG, "HTTP: Error %d from "
00069                                            "%s:%d", reply_code,
00070                                            inet_ntoa(c->dst.sin_addr),
00071                                            ntohs(c->dst.sin_port));
00072                                 c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
00073                         }
00074                 } else
00075                         c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
00076                 break;
00077         case HTTPREAD_EVENT_TIMEOUT:
00078                 c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
00079                 break;
00080         case HTTPREAD_EVENT_ERROR:
00081                 c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
00082                 break;
00083         }
00084 }
00085 
00086 
00087 static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
00088 {
00089         struct http_client *c = eloop_ctx;
00090         int res;
00091 
00092         wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu "
00093                    "bytes remaining)",
00094                    inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port),
00095                    (unsigned long) wpabuf_len(c->req),
00096                    (unsigned long) wpabuf_len(c->req) - c->req_pos);
00097 
00098         res = send(c->sd, wpabuf_head(c->req) + c->req_pos,
00099                    wpabuf_len(c->req) - c->req_pos, 0);
00100         if (res < 0) {
00101                 wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s",
00102                            strerror(errno));
00103                 eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
00104                 c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
00105                 return;
00106         }
00107 
00108         if ((size_t) res < wpabuf_len(c->req) - c->req_pos) {
00109                 wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes "
00110                            "remaining",
00111                            res, (unsigned long) wpabuf_len(c->req),
00112                            (unsigned long) wpabuf_len(c->req) - c->req_pos -
00113                            res);
00114                 c->req_pos += res;
00115                 return;
00116         }
00117 
00118         wpa_printf(MSG_DEBUG, "HTTP: Full client request sent to %s:%d",
00119                    inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port));
00120         eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
00121         wpabuf_free(c->req);
00122         c->req = NULL;
00123 
00124         c->hread = httpread_create(c->sd, http_client_got_response, c,
00125                                    c->max_response, HTTP_CLIENT_TIMEOUT);
00126         if (c->hread == NULL) {
00127                 c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
00128                 return;
00129         }
00130 }
00131 
00132 
00133 struct http_client * http_client_addr(struct sockaddr_in *dst,
00134                                       struct wpabuf *req, size_t max_response,
00135                                       void (*cb)(void *ctx,
00136                                                  struct http_client *c,
00137                                                  enum http_client_event event),
00138                                       void *cb_ctx)
00139 {
00140         struct http_client *c;
00141 
00142         c = os_zalloc(sizeof(*c));
00143         if (c == NULL)
00144                 return NULL;
00145         c->sd = -1;
00146         c->dst = *dst;
00147         c->max_response = max_response;
00148         c->cb = cb;
00149         c->cb_ctx = cb_ctx;
00150 
00151         c->sd = socket(AF_INET, SOCK_STREAM, 0);
00152         if (c->sd < 0) {
00153                 http_client_free(c);
00154                 return NULL;
00155         }
00156 
00157         if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) {
00158                 wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s",
00159                            strerror(errno));
00160                 http_client_free(c);
00161                 return NULL;
00162         }
00163 
00164         if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) {
00165                 if (errno != EINPROGRESS) {
00166                         wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s",
00167                                    strerror(errno));
00168                         http_client_free(c);
00169                         return NULL;
00170                 }
00171 
00172                 /*
00173                  * Continue connecting in the background; eloop will call us
00174                  * once the connection is ready (or failed).
00175                  */
00176         }
00177 
00178         if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready,
00179                                 c, NULL)) {
00180                 http_client_free(c);
00181                 return NULL;
00182         }
00183 
00184         if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT, 0, http_client_timeout,
00185                                    c, NULL)) {
00186                 http_client_free(c);
00187                 return NULL;
00188         }
00189 
00190         c->req = req;
00191 
00192         return c;
00193 }
00194 
00195 
00196 char * http_client_url_parse(const char *url, struct sockaddr_in *dst,
00197                              char **ret_path)
00198 {
00199         char *u, *addr, *port, *path;
00200 
00201         u = os_strdup(url);
00202         if (u == NULL)
00203                 return NULL;
00204 
00205         os_memset(dst, 0, sizeof(*dst));
00206         dst->sin_family = AF_INET;
00207         addr = u + 7;
00208         path = os_strchr(addr, '/');
00209         port = os_strchr(addr, ':');
00210         if (path == NULL) {
00211                 path = "/";
00212         } else {
00213                 *path = '\0'; /* temporary nul termination for address */
00214                 if (port > path)
00215                         port = NULL;
00216         }
00217         if (port)
00218                 *port++ = '\0';
00219 
00220         if (inet_aton(addr, &dst->sin_addr) == 0) {
00221                 /* TODO: name lookup */
00222                 wpa_printf(MSG_DEBUG, "HTTP: Unsupported address in URL '%s' "
00223                            "(addr='%s' port='%s')",
00224                            url, addr, port);
00225                 os_free(u);
00226                 return NULL;
00227         }
00228 
00229         if (port)
00230                 dst->sin_port = htons(atoi(port));
00231         else
00232                 dst->sin_port = htons(80);
00233 
00234         if (*path == '\0') {
00235                 /* remove temporary nul termination for address */
00236                 *path = '/';
00237         }
00238 
00239         *ret_path = path;
00240 
00241         return u;
00242 }
00243 
00244 
00245 struct http_client * http_client_url(const char *url,
00246                                      struct wpabuf *req, size_t max_response,
00247                                      void (*cb)(void *ctx,
00248                                                 struct http_client *c,
00249                                                 enum http_client_event event),
00250                                      void *cb_ctx)
00251 {
00252         struct sockaddr_in dst;
00253         struct http_client *c;
00254         char *u, *path;
00255         struct wpabuf *req_buf = NULL;
00256 
00257         if (os_strncmp(url, "http://", 7) != 0)
00258                 return NULL;
00259         u = http_client_url_parse(url, &dst, &path);
00260         if (u == NULL)
00261                 return NULL;
00262 
00263         if (req == NULL) {
00264                 req_buf = wpabuf_alloc(os_strlen(url) + 1000);
00265                 if (req_buf == NULL) {
00266                         os_free(u);
00267                         return NULL;
00268                 }
00269                 req = req_buf;
00270                 wpabuf_printf(req,
00271                               "GET %s HTTP/1.1\r\n"
00272                               "Cache-Control: no-cache\r\n"
00273                               "Pragma: no-cache\r\n"
00274                               "Accept: text/xml, application/xml\r\n"
00275                               "User-Agent: wpa_supplicant\r\n"
00276                               "Host: %s:%d\r\n"
00277                               "\r\n",
00278                               path, inet_ntoa(dst.sin_addr),
00279                               ntohs(dst.sin_port));
00280         }
00281         os_free(u);
00282 
00283         c = http_client_addr(&dst, req, max_response, cb, cb_ctx);
00284         if (c == NULL) {
00285                 wpabuf_free(req_buf);
00286                 return NULL;
00287         }
00288 
00289         return c;
00290 }
00291 
00292 
00293 void http_client_free(struct http_client *c)
00294 {
00295         if (c == NULL)
00296                 return;
00297         httpread_destroy(c->hread);
00298         wpabuf_free(c->req);
00299         if (c->sd >= 0) {
00300                 eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
00301                 close(c->sd);
00302         }
00303         eloop_cancel_timeout(http_client_timeout, c, NULL);
00304         os_free(c);
00305 }
00306 
00307 
00308 struct wpabuf * http_client_get_body(struct http_client *c)
00309 {
00310         if (c->hread == NULL)
00311                 return NULL;
00312         wpabuf_set(&c->body, httpread_data_get(c->hread),
00313                    httpread_length_get(c->hread));
00314         return &c->body;
00315 }
00316 
00317 
00318 char * http_client_get_hdr_line(struct http_client *c, const char *tag)
00319 {
00320         if (c->hread == NULL)
00321                 return NULL;
00322         return httpread_hdr_line_get(c->hread, tag);
00323 }
00324 
00325 
00326 char * http_link_update(char *url, const char *base)
00327 {
00328         char *n;
00329         size_t len;
00330         const char *pos;
00331 
00332         /* RFC 2396, Chapter 5.2 */
00333         /* TODO: consider adding all cases described in RFC 2396 */
00334 
00335         if (url == NULL)
00336                 return NULL;
00337 
00338         if (os_strncmp(url, "http://", 7) == 0)
00339                 return url; /* absolute link */
00340 
00341         if (os_strncmp(base, "http://", 7) != 0)
00342                 return url; /* unable to handle base URL */
00343 
00344         len = os_strlen(url) + 1 + os_strlen(base) + 1;
00345         n = os_malloc(len);
00346         if (n == NULL)
00347                 return url; /* failed */
00348 
00349         if (url[0] == '/') {
00350                 pos = os_strchr(base + 7, '/');
00351                 if (pos == NULL) {
00352                         os_snprintf(n, len, "%s%s", base, url);
00353                 } else {
00354                         os_memcpy(n, base, pos - base);
00355                         os_memcpy(n + (pos - base), url, os_strlen(url) + 1);
00356                 }
00357         } else {
00358                 pos = os_strrchr(base + 7, '/');
00359                 if (pos == NULL) {
00360                         os_snprintf(n, len, "%s/%s", base, url);
00361                 } else {
00362                         os_memcpy(n, base, pos - base + 1);
00363                         os_memcpy(n + (pos - base) + 1, url, os_strlen(url) +
00364                                   1);
00365                 }
00366         }
00367 
00368         os_free(url);
00369 
00370         return n;
00371 }


wpa_supplicant
Author(s): Package maintained by Blaise Gassend
autogenerated on Thu Apr 24 2014 15:34:35