1214501Srpaulo/* 2214501Srpaulo * http_client - HTTP client 3214501Srpaulo * Copyright (c) 2009, Jouni Malinen <j@w1.fi> 4214501Srpaulo * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7214501Srpaulo */ 8214501Srpaulo 9214501Srpaulo#include "includes.h" 10214501Srpaulo#include <fcntl.h> 11214501Srpaulo 12214501Srpaulo#include "common.h" 13214501Srpaulo#include "eloop.h" 14214501Srpaulo#include "httpread.h" 15214501Srpaulo#include "http_client.h" 16214501Srpaulo 17214501Srpaulo 18252726Srpaulo#define HTTP_CLIENT_TIMEOUT_SEC 30 19214501Srpaulo 20214501Srpaulo 21214501Srpaulostruct http_client { 22214501Srpaulo struct sockaddr_in dst; 23214501Srpaulo int sd; 24214501Srpaulo struct wpabuf *req; 25214501Srpaulo size_t req_pos; 26214501Srpaulo size_t max_response; 27214501Srpaulo 28214501Srpaulo void (*cb)(void *ctx, struct http_client *c, 29214501Srpaulo enum http_client_event event); 30214501Srpaulo void *cb_ctx; 31214501Srpaulo struct httpread *hread; 32214501Srpaulo struct wpabuf body; 33214501Srpaulo}; 34214501Srpaulo 35214501Srpaulo 36214501Srpaulostatic void http_client_timeout(void *eloop_data, void *user_ctx) 37214501Srpaulo{ 38214501Srpaulo struct http_client *c = eloop_data; 39252726Srpaulo wpa_printf(MSG_DEBUG, "HTTP: Timeout (c=%p)", c); 40214501Srpaulo c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT); 41214501Srpaulo} 42214501Srpaulo 43214501Srpaulo 44214501Srpaulostatic void http_client_got_response(struct httpread *handle, void *cookie, 45214501Srpaulo enum httpread_event e) 46214501Srpaulo{ 47214501Srpaulo struct http_client *c = cookie; 48214501Srpaulo 49252726Srpaulo wpa_printf(MSG_DEBUG, "HTTP: httpread callback: handle=%p cookie=%p " 50252726Srpaulo "e=%d", handle, cookie, e); 51252726Srpaulo 52214501Srpaulo eloop_cancel_timeout(http_client_timeout, c, NULL); 53214501Srpaulo switch (e) { 54214501Srpaulo case HTTPREAD_EVENT_FILE_READY: 55214501Srpaulo if (httpread_hdr_type_get(c->hread) == HTTPREAD_HDR_TYPE_REPLY) 56214501Srpaulo { 57214501Srpaulo int reply_code = httpread_reply_code_get(c->hread); 58214501Srpaulo if (reply_code == 200 /* OK */) { 59214501Srpaulo wpa_printf(MSG_DEBUG, "HTTP: Response OK from " 60214501Srpaulo "%s:%d", 61214501Srpaulo inet_ntoa(c->dst.sin_addr), 62214501Srpaulo ntohs(c->dst.sin_port)); 63214501Srpaulo c->cb(c->cb_ctx, c, HTTP_CLIENT_OK); 64214501Srpaulo } else { 65214501Srpaulo wpa_printf(MSG_DEBUG, "HTTP: Error %d from " 66214501Srpaulo "%s:%d", reply_code, 67214501Srpaulo inet_ntoa(c->dst.sin_addr), 68214501Srpaulo ntohs(c->dst.sin_port)); 69214501Srpaulo c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY); 70214501Srpaulo } 71214501Srpaulo } else 72214501Srpaulo c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY); 73214501Srpaulo break; 74214501Srpaulo case HTTPREAD_EVENT_TIMEOUT: 75214501Srpaulo c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT); 76214501Srpaulo break; 77214501Srpaulo case HTTPREAD_EVENT_ERROR: 78214501Srpaulo c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED); 79214501Srpaulo break; 80214501Srpaulo } 81214501Srpaulo} 82214501Srpaulo 83214501Srpaulo 84214501Srpaulostatic void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) 85214501Srpaulo{ 86214501Srpaulo struct http_client *c = eloop_ctx; 87214501Srpaulo int res; 88214501Srpaulo 89214501Srpaulo wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu " 90214501Srpaulo "bytes remaining)", 91214501Srpaulo inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port), 92214501Srpaulo (unsigned long) wpabuf_len(c->req), 93214501Srpaulo (unsigned long) wpabuf_len(c->req) - c->req_pos); 94214501Srpaulo 95214501Srpaulo res = send(c->sd, wpabuf_head(c->req) + c->req_pos, 96214501Srpaulo wpabuf_len(c->req) - c->req_pos, 0); 97214501Srpaulo if (res < 0) { 98214501Srpaulo wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s", 99214501Srpaulo strerror(errno)); 100214501Srpaulo eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE); 101214501Srpaulo c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED); 102214501Srpaulo return; 103214501Srpaulo } 104214501Srpaulo 105214501Srpaulo if ((size_t) res < wpabuf_len(c->req) - c->req_pos) { 106214501Srpaulo wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes " 107214501Srpaulo "remaining", 108214501Srpaulo res, (unsigned long) wpabuf_len(c->req), 109214501Srpaulo (unsigned long) wpabuf_len(c->req) - c->req_pos - 110214501Srpaulo res); 111214501Srpaulo c->req_pos += res; 112214501Srpaulo return; 113214501Srpaulo } 114214501Srpaulo 115214501Srpaulo wpa_printf(MSG_DEBUG, "HTTP: Full client request sent to %s:%d", 116214501Srpaulo inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port)); 117214501Srpaulo eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE); 118214501Srpaulo wpabuf_free(c->req); 119214501Srpaulo c->req = NULL; 120214501Srpaulo 121214501Srpaulo c->hread = httpread_create(c->sd, http_client_got_response, c, 122252726Srpaulo c->max_response, HTTP_CLIENT_TIMEOUT_SEC); 123214501Srpaulo if (c->hread == NULL) { 124214501Srpaulo c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED); 125214501Srpaulo return; 126214501Srpaulo } 127214501Srpaulo} 128214501Srpaulo 129214501Srpaulo 130214501Srpaulostruct http_client * http_client_addr(struct sockaddr_in *dst, 131214501Srpaulo struct wpabuf *req, size_t max_response, 132214501Srpaulo void (*cb)(void *ctx, 133214501Srpaulo struct http_client *c, 134214501Srpaulo enum http_client_event event), 135214501Srpaulo void *cb_ctx) 136214501Srpaulo{ 137214501Srpaulo struct http_client *c; 138214501Srpaulo 139214501Srpaulo c = os_zalloc(sizeof(*c)); 140214501Srpaulo if (c == NULL) 141214501Srpaulo return NULL; 142214501Srpaulo c->sd = -1; 143214501Srpaulo c->dst = *dst; 144214501Srpaulo c->max_response = max_response; 145214501Srpaulo c->cb = cb; 146214501Srpaulo c->cb_ctx = cb_ctx; 147214501Srpaulo 148214501Srpaulo c->sd = socket(AF_INET, SOCK_STREAM, 0); 149214501Srpaulo if (c->sd < 0) { 150214501Srpaulo http_client_free(c); 151214501Srpaulo return NULL; 152214501Srpaulo } 153214501Srpaulo 154214501Srpaulo if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) { 155214501Srpaulo wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s", 156214501Srpaulo strerror(errno)); 157214501Srpaulo http_client_free(c); 158214501Srpaulo return NULL; 159214501Srpaulo } 160214501Srpaulo 161214501Srpaulo if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) { 162214501Srpaulo if (errno != EINPROGRESS) { 163214501Srpaulo wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s", 164214501Srpaulo strerror(errno)); 165214501Srpaulo http_client_free(c); 166214501Srpaulo return NULL; 167214501Srpaulo } 168214501Srpaulo 169214501Srpaulo /* 170214501Srpaulo * Continue connecting in the background; eloop will call us 171214501Srpaulo * once the connection is ready (or failed). 172214501Srpaulo */ 173214501Srpaulo } 174214501Srpaulo 175214501Srpaulo if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready, 176214501Srpaulo c, NULL)) { 177214501Srpaulo http_client_free(c); 178214501Srpaulo return NULL; 179214501Srpaulo } 180214501Srpaulo 181252726Srpaulo if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0, 182252726Srpaulo http_client_timeout, c, NULL)) { 183214501Srpaulo http_client_free(c); 184214501Srpaulo return NULL; 185214501Srpaulo } 186214501Srpaulo 187214501Srpaulo c->req = req; 188214501Srpaulo 189214501Srpaulo return c; 190214501Srpaulo} 191214501Srpaulo 192214501Srpaulo 193214501Srpaulochar * http_client_url_parse(const char *url, struct sockaddr_in *dst, 194214501Srpaulo char **ret_path) 195214501Srpaulo{ 196214501Srpaulo char *u, *addr, *port, *path; 197214501Srpaulo 198214501Srpaulo u = os_strdup(url); 199214501Srpaulo if (u == NULL) 200214501Srpaulo return NULL; 201214501Srpaulo 202214501Srpaulo os_memset(dst, 0, sizeof(*dst)); 203214501Srpaulo dst->sin_family = AF_INET; 204214501Srpaulo addr = u + 7; 205214501Srpaulo path = os_strchr(addr, '/'); 206214501Srpaulo port = os_strchr(addr, ':'); 207214501Srpaulo if (path == NULL) { 208214501Srpaulo path = "/"; 209214501Srpaulo } else { 210214501Srpaulo *path = '\0'; /* temporary nul termination for address */ 211214501Srpaulo if (port > path) 212214501Srpaulo port = NULL; 213214501Srpaulo } 214214501Srpaulo if (port) 215214501Srpaulo *port++ = '\0'; 216214501Srpaulo 217214501Srpaulo if (inet_aton(addr, &dst->sin_addr) == 0) { 218214501Srpaulo /* TODO: name lookup */ 219214501Srpaulo wpa_printf(MSG_DEBUG, "HTTP: Unsupported address in URL '%s' " 220214501Srpaulo "(addr='%s' port='%s')", 221214501Srpaulo url, addr, port); 222214501Srpaulo os_free(u); 223214501Srpaulo return NULL; 224214501Srpaulo } 225214501Srpaulo 226214501Srpaulo if (port) 227214501Srpaulo dst->sin_port = htons(atoi(port)); 228214501Srpaulo else 229214501Srpaulo dst->sin_port = htons(80); 230214501Srpaulo 231214501Srpaulo if (*path == '\0') { 232214501Srpaulo /* remove temporary nul termination for address */ 233214501Srpaulo *path = '/'; 234214501Srpaulo } 235214501Srpaulo 236214501Srpaulo *ret_path = path; 237214501Srpaulo 238214501Srpaulo return u; 239214501Srpaulo} 240214501Srpaulo 241214501Srpaulo 242214501Srpaulostruct http_client * http_client_url(const char *url, 243214501Srpaulo struct wpabuf *req, size_t max_response, 244214501Srpaulo void (*cb)(void *ctx, 245214501Srpaulo struct http_client *c, 246214501Srpaulo enum http_client_event event), 247214501Srpaulo void *cb_ctx) 248214501Srpaulo{ 249214501Srpaulo struct sockaddr_in dst; 250214501Srpaulo struct http_client *c; 251214501Srpaulo char *u, *path; 252214501Srpaulo struct wpabuf *req_buf = NULL; 253214501Srpaulo 254214501Srpaulo if (os_strncmp(url, "http://", 7) != 0) 255214501Srpaulo return NULL; 256214501Srpaulo u = http_client_url_parse(url, &dst, &path); 257214501Srpaulo if (u == NULL) 258214501Srpaulo return NULL; 259214501Srpaulo 260214501Srpaulo if (req == NULL) { 261214501Srpaulo req_buf = wpabuf_alloc(os_strlen(url) + 1000); 262214501Srpaulo if (req_buf == NULL) { 263214501Srpaulo os_free(u); 264214501Srpaulo return NULL; 265214501Srpaulo } 266214501Srpaulo req = req_buf; 267214501Srpaulo wpabuf_printf(req, 268214501Srpaulo "GET %s HTTP/1.1\r\n" 269214501Srpaulo "Cache-Control: no-cache\r\n" 270214501Srpaulo "Pragma: no-cache\r\n" 271214501Srpaulo "Accept: text/xml, application/xml\r\n" 272214501Srpaulo "User-Agent: wpa_supplicant\r\n" 273214501Srpaulo "Host: %s:%d\r\n" 274214501Srpaulo "\r\n", 275214501Srpaulo path, inet_ntoa(dst.sin_addr), 276214501Srpaulo ntohs(dst.sin_port)); 277214501Srpaulo } 278214501Srpaulo os_free(u); 279214501Srpaulo 280214501Srpaulo c = http_client_addr(&dst, req, max_response, cb, cb_ctx); 281214501Srpaulo if (c == NULL) { 282214501Srpaulo wpabuf_free(req_buf); 283214501Srpaulo return NULL; 284214501Srpaulo } 285214501Srpaulo 286214501Srpaulo return c; 287214501Srpaulo} 288214501Srpaulo 289214501Srpaulo 290214501Srpaulovoid http_client_free(struct http_client *c) 291214501Srpaulo{ 292214501Srpaulo if (c == NULL) 293214501Srpaulo return; 294214501Srpaulo httpread_destroy(c->hread); 295214501Srpaulo wpabuf_free(c->req); 296214501Srpaulo if (c->sd >= 0) { 297214501Srpaulo eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE); 298214501Srpaulo close(c->sd); 299214501Srpaulo } 300214501Srpaulo eloop_cancel_timeout(http_client_timeout, c, NULL); 301214501Srpaulo os_free(c); 302214501Srpaulo} 303214501Srpaulo 304214501Srpaulo 305214501Srpaulostruct wpabuf * http_client_get_body(struct http_client *c) 306214501Srpaulo{ 307214501Srpaulo if (c->hread == NULL) 308214501Srpaulo return NULL; 309214501Srpaulo wpabuf_set(&c->body, httpread_data_get(c->hread), 310214501Srpaulo httpread_length_get(c->hread)); 311214501Srpaulo return &c->body; 312214501Srpaulo} 313214501Srpaulo 314214501Srpaulo 315214501Srpaulochar * http_client_get_hdr_line(struct http_client *c, const char *tag) 316214501Srpaulo{ 317214501Srpaulo if (c->hread == NULL) 318214501Srpaulo return NULL; 319214501Srpaulo return httpread_hdr_line_get(c->hread, tag); 320214501Srpaulo} 321214501Srpaulo 322214501Srpaulo 323214501Srpaulochar * http_link_update(char *url, const char *base) 324214501Srpaulo{ 325214501Srpaulo char *n; 326214501Srpaulo size_t len; 327214501Srpaulo const char *pos; 328214501Srpaulo 329214501Srpaulo /* RFC 2396, Chapter 5.2 */ 330214501Srpaulo /* TODO: consider adding all cases described in RFC 2396 */ 331214501Srpaulo 332214501Srpaulo if (url == NULL) 333214501Srpaulo return NULL; 334214501Srpaulo 335214501Srpaulo if (os_strncmp(url, "http://", 7) == 0) 336214501Srpaulo return url; /* absolute link */ 337214501Srpaulo 338214501Srpaulo if (os_strncmp(base, "http://", 7) != 0) 339214501Srpaulo return url; /* unable to handle base URL */ 340214501Srpaulo 341214501Srpaulo len = os_strlen(url) + 1 + os_strlen(base) + 1; 342214501Srpaulo n = os_malloc(len); 343214501Srpaulo if (n == NULL) 344214501Srpaulo return url; /* failed */ 345214501Srpaulo 346214501Srpaulo if (url[0] == '/') { 347214501Srpaulo pos = os_strchr(base + 7, '/'); 348214501Srpaulo if (pos == NULL) { 349214501Srpaulo os_snprintf(n, len, "%s%s", base, url); 350214501Srpaulo } else { 351214501Srpaulo os_memcpy(n, base, pos - base); 352214501Srpaulo os_memcpy(n + (pos - base), url, os_strlen(url) + 1); 353214501Srpaulo } 354214501Srpaulo } else { 355214501Srpaulo pos = os_strrchr(base + 7, '/'); 356214501Srpaulo if (pos == NULL) { 357214501Srpaulo os_snprintf(n, len, "%s/%s", base, url); 358214501Srpaulo } else { 359214501Srpaulo os_memcpy(n, base, pos - base + 1); 360214501Srpaulo os_memcpy(n + (pos - base) + 1, url, os_strlen(url) + 361214501Srpaulo 1); 362214501Srpaulo } 363214501Srpaulo } 364214501Srpaulo 365214501Srpaulo os_free(url); 366214501Srpaulo 367214501Srpaulo return n; 368214501Srpaulo} 369