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