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

Generated by: LCOV version 1.10