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