LCOV - code coverage report
Current view: top level - src/wps - http_client.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1422976643 Lines: 125 180 69.4 %
Date: 2015-02-03 Functions: 9 10 90.0 %

          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         146 : static void http_client_got_response(struct httpread *handle, void *cookie,
      45             :                                      enum httpread_event e)
      46             : {
      47         146 :         struct http_client *c = cookie;
      48             : 
      49         146 :         wpa_printf(MSG_DEBUG, "HTTP: httpread callback: handle=%p cookie=%p "
      50             :                    "e=%d", handle, cookie, e);
      51             : 
      52         146 :         eloop_cancel_timeout(http_client_timeout, c, NULL);
      53         146 :         switch (e) {
      54             :         case HTTPREAD_EVENT_FILE_READY:
      55         145 :                 if (httpread_hdr_type_get(c->hread) == HTTPREAD_HDR_TYPE_REPLY)
      56             :                 {
      57         145 :                         int reply_code = httpread_reply_code_get(c->hread);
      58         145 :                         if (reply_code == 200 /* OK */) {
      59         144 :                                 wpa_printf(MSG_DEBUG, "HTTP: Response OK from "
      60             :                                            "%s:%d",
      61             :                                            inet_ntoa(c->dst.sin_addr),
      62         144 :                                            ntohs(c->dst.sin_port));
      63         144 :                                 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         145 :                 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           1 :                 c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
      79           1 :                 break;
      80             :         }
      81         146 : }
      82             : 
      83             : 
      84         155 : static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
      85             : {
      86         155 :         struct http_client *c = eloop_ctx;
      87             :         int res;
      88             : 
      89         465 :         wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu "
      90             :                    "bytes remaining)",
      91         155 :                    inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port),
      92         155 :                    (unsigned long) wpabuf_len(c->req),
      93         155 :                    (unsigned long) wpabuf_len(c->req) - c->req_pos);
      94             : 
      95         155 :         res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos,
      96         155 :                    wpabuf_len(c->req) - c->req_pos, 0);
      97         155 :         if (res < 0) {
      98           9 :                 wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s",
      99           9 :                            strerror(errno));
     100           9 :                 eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
     101           9 :                 c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
     102           9 :                 return;
     103             :         }
     104             : 
     105         146 :         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         146 :         wpa_printf(MSG_DEBUG, "HTTP: Full client request sent to %s:%d",
     116         146 :                    inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port));
     117         146 :         eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
     118         146 :         wpabuf_free(c->req);
     119         146 :         c->req = NULL;
     120             : 
     121         146 :         c->hread = httpread_create(c->sd, http_client_got_response, c,
     122         146 :                                    c->max_response, HTTP_CLIENT_TIMEOUT_SEC);
     123         146 :         if (c->hread == NULL) {
     124           0 :                 c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
     125           0 :                 return;
     126             :         }
     127             : }
     128             : 
     129             : 
     130         157 : 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         157 :         c = os_zalloc(sizeof(*c));
     140         157 :         if (c == NULL)
     141           0 :                 return NULL;
     142         157 :         c->sd = -1;
     143         157 :         c->dst = *dst;
     144         157 :         c->max_response = max_response;
     145         157 :         c->cb = cb;
     146         157 :         c->cb_ctx = cb_ctx;
     147             : 
     148         157 :         c->sd = socket(AF_INET, SOCK_STREAM, 0);
     149         157 :         if (c->sd < 0) {
     150           0 :                 http_client_free(c);
     151           0 :                 return NULL;
     152             :         }
     153             : 
     154         157 :         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         157 :         if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) {
     162         157 :                 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         157 :         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         157 :         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         157 :         c->req = req;
     188             : 
     189         157 :         return c;
     190             : }
     191             : 
     192             : 
     193          93 : 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          93 :         u = os_strdup(url);
     199          93 :         if (u == NULL)
     200           0 :                 return NULL;
     201             : 
     202          93 :         os_memset(dst, 0, sizeof(*dst));
     203          93 :         dst->sin_family = AF_INET;
     204          93 :         addr = u + 7;
     205          93 :         path = os_strchr(addr, '/');
     206          93 :         port = os_strchr(addr, ':');
     207          93 :         if (path == NULL) {
     208           0 :                 path = "/";
     209             :         } else {
     210          93 :                 *path = '\0'; /* temporary nul termination for address */
     211          93 :                 if (port > path)
     212           0 :                         port = NULL;
     213             :         }
     214          93 :         if (port)
     215          93 :                 *port++ = '\0';
     216             : 
     217          93 :         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          93 :         if (port)
     227          93 :                 dst->sin_port = htons(atoi(port));
     228             :         else
     229           0 :                 dst->sin_port = htons(80);
     230             : 
     231          93 :         if (*path == '\0') {
     232             :                 /* remove temporary nul termination for address */
     233          93 :                 *path = '/';
     234             :         }
     235             : 
     236          93 :         *ret_path = path;
     237             : 
     238          93 :         return u;
     239             : }
     240             : 
     241             : 
     242          10 : 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          10 :         struct wpabuf *req_buf = NULL;
     253             : 
     254          10 :         if (os_strncmp(url, "http://", 7) != 0)
     255           0 :                 return NULL;
     256          10 :         u = http_client_url_parse(url, &dst, &path);
     257          10 :         if (u == NULL)
     258           0 :                 return NULL;
     259             : 
     260          10 :         if (req == NULL) {
     261          10 :                 req_buf = wpabuf_alloc(os_strlen(url) + 1000);
     262          10 :                 if (req_buf == NULL) {
     263           0 :                         os_free(u);
     264           0 :                         return NULL;
     265             :                 }
     266          10 :                 req = req_buf;
     267          10 :                 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          10 :                               ntohs(dst.sin_port));
     277             :         }
     278          10 :         os_free(u);
     279             : 
     280          10 :         c = http_client_addr(&dst, req, max_response, cb, cb_ctx);
     281          10 :         if (c == NULL) {
     282           0 :                 wpabuf_free(req_buf);
     283           0 :                 return NULL;
     284             :         }
     285             : 
     286          10 :         return c;
     287             : }
     288             : 
     289             : 
     290         199 : void http_client_free(struct http_client *c)
     291             : {
     292         199 :         if (c == NULL)
     293         241 :                 return;
     294         157 :         httpread_destroy(c->hread);
     295         157 :         wpabuf_free(c->req);
     296         157 :         if (c->sd >= 0) {
     297         157 :                 eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
     298         157 :                 close(c->sd);
     299             :         }
     300         157 :         eloop_cancel_timeout(http_client_timeout, c, NULL);
     301         157 :         os_free(c);
     302             : }
     303             : 
     304             : 
     305          29 : struct wpabuf * http_client_get_body(struct http_client *c)
     306             : {
     307          29 :         if (c->hread == NULL)
     308           0 :                 return NULL;
     309          29 :         wpabuf_set(&c->body, httpread_data_get(c->hread),
     310          29 :                    httpread_length_get(c->hread));
     311          29 :         return &c->body;
     312             : }
     313             : 
     314             : 
     315          10 : char * http_client_get_hdr_line(struct http_client *c, const char *tag)
     316             : {
     317          10 :         if (c->hread == NULL)
     318           0 :                 return NULL;
     319          10 :         return httpread_hdr_line_get(c->hread, tag);
     320             : }
     321             : 
     322             : 
     323          30 : 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          30 :         if (url == NULL)
     333           0 :                 return NULL;
     334             : 
     335          30 :         if (os_strncmp(url, "http://", 7) == 0)
     336           0 :                 return url; /* absolute link */
     337             : 
     338          30 :         if (os_strncmp(base, "http://", 7) != 0)
     339           0 :                 return url; /* unable to handle base URL */
     340             : 
     341          30 :         len = os_strlen(url) + 1 + os_strlen(base) + 1;
     342          30 :         n = os_malloc(len);
     343          30 :         if (n == NULL)
     344           0 :                 return url; /* failed */
     345             : 
     346          30 :         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          30 :                 pos = os_strrchr(base + 7, '/');
     356          30 :                 if (pos == NULL) {
     357           0 :                         os_snprintf(n, len, "%s/%s", base, url);
     358             :                 } else {
     359          30 :                         os_memcpy(n, base, pos - base + 1);
     360          30 :                         os_memcpy(n + (pos - base) + 1, url, os_strlen(url) +
     361             :                                   1);
     362             :                 }
     363             :         }
     364             : 
     365          30 :         os_free(url);
     366             : 
     367          30 :         return n;
     368             : }

Generated by: LCOV version 1.10