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 21struct 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 36static void http_client_timeout(void *eloop_data, void *user_ctx) 37{ 38 struct http_client *c = eloop_data; 39 wpa_printf(MSG_DEBUG, "HTTP: Timeout (c=%p)", c); 40 c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT); 41} 42 43 44static void http_client_got_response(struct httpread *handle, void *cookie, 45 enum httpread_event e) 46{ 47 struct http_client *c = cookie; 48 49 wpa_printf(MSG_DEBUG, "HTTP: httpread callback: handle=%p cookie=%p " 50 "e=%d", handle, cookie, e); 51 52 eloop_cancel_timeout(http_client_timeout, c, NULL); 53 switch (e) { 54 case HTTPREAD_EVENT_FILE_READY: 55 if (httpread_hdr_type_get(c->hread) == HTTPREAD_HDR_TYPE_REPLY) 56 { 57 int reply_code = httpread_reply_code_get(c->hread); 58 if (reply_code == 200 /* OK */) { 59 wpa_printf(MSG_DEBUG, "HTTP: Response OK from " 60 "%s:%d", 61 inet_ntoa(c->dst.sin_addr), 62 ntohs(c->dst.sin_port)); 63 c->cb(c->cb_ctx, c, HTTP_CLIENT_OK); 64 } else { 65 wpa_printf(MSG_DEBUG, "HTTP: Error %d from " 66 "%s:%d", reply_code, 67 inet_ntoa(c->dst.sin_addr), 68 ntohs(c->dst.sin_port)); 69 c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY); 70 } 71 } else 72 c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY); 73 break; 74 case HTTPREAD_EVENT_TIMEOUT: 75 c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT); 76 break; 77 case HTTPREAD_EVENT_ERROR: 78 c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED); 79 break; 80 } 81} 82 83 84static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) 85{ 86 struct http_client *c = eloop_ctx; 87 int res; 88 89 wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu " 90 "bytes remaining)", 91 inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port), 92 (unsigned long) wpabuf_len(c->req), 93 (unsigned long) wpabuf_len(c->req) - c->req_pos); 94 95 res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, 96 wpabuf_len(c->req) - c->req_pos, 0); 97 if (res < 0) { 98 wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s", 99 strerror(errno)); 100 eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE); 101 c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED); 102 return; 103 } 104 105 if ((size_t) res < wpabuf_len(c->req) - c->req_pos) { 106 wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes " 107 "remaining", 108 res, (unsigned long) wpabuf_len(c->req), 109 (unsigned long) wpabuf_len(c->req) - c->req_pos - 110 res); 111 c->req_pos += res; 112 return; 113 } 114 115 wpa_printf(MSG_DEBUG, "HTTP: Full client request sent to %s:%d", 116 inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port)); 117 eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE); 118 wpabuf_free(c->req); 119 c->req = NULL; 120 121 c->hread = httpread_create(c->sd, http_client_got_response, c, 122 c->max_response, HTTP_CLIENT_TIMEOUT_SEC); 123 if (c->hread == NULL) { 124 c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED); 125 return; 126 } 127} 128 129 130struct 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 c = os_zalloc(sizeof(*c)); 140 if (c == NULL) 141 return NULL; 142 c->sd = -1; 143 c->dst = *dst; 144 c->max_response = max_response; 145 c->cb = cb; 146 c->cb_ctx = cb_ctx; 147 148 c->sd = socket(AF_INET, SOCK_STREAM, 0); 149 if (c->sd < 0) { 150 http_client_free(c); 151 return NULL; 152 } 153 154 if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) { 155 wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s", 156 strerror(errno)); 157 http_client_free(c); 158 return NULL; 159 } 160 161 if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) { 162 if (errno != EINPROGRESS) { 163 wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s", 164 strerror(errno)); 165 http_client_free(c); 166 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 if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready, 176 c, NULL)) { 177 http_client_free(c); 178 return NULL; 179 } 180 181 if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0, 182 http_client_timeout, c, NULL)) { 183 http_client_free(c); 184 return NULL; 185 } 186 187 c->req = req; 188 189 return c; 190} 191 192 193char * http_client_url_parse(const char *url, struct sockaddr_in *dst, 194 char **ret_path) 195{ 196 char *u, *addr, *port, *path; 197 198 u = os_strdup(url); 199 if (u == NULL) 200 return NULL; 201 202 os_memset(dst, 0, sizeof(*dst)); 203 dst->sin_family = AF_INET; 204 addr = u + 7; 205 path = os_strchr(addr, '/'); 206 port = os_strchr(addr, ':'); 207 if (path == NULL) { 208 path = "/"; 209 } else { 210 *path = '\0'; /* temporary nul termination for address */ 211 if (port > path) 212 port = NULL; 213 } 214 if (port) 215 *port++ = '\0'; 216 217 if (inet_aton(addr, &dst->sin_addr) == 0) { 218 /* TODO: name lookup */ 219 wpa_printf(MSG_DEBUG, "HTTP: Unsupported address in URL '%s' " 220 "(addr='%s' port='%s')", 221 url, addr, port); 222 os_free(u); 223 return NULL; 224 } 225 226 if (port) 227 dst->sin_port = htons(atoi(port)); 228 else 229 dst->sin_port = htons(80); 230 231 if (*path == '\0') { 232 /* remove temporary nul termination for address */ 233 *path = '/'; 234 } 235 236 *ret_path = path; 237 238 return u; 239} 240 241 242struct 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 struct wpabuf *req_buf = NULL; 253 254 if (os_strncmp(url, "http://", 7) != 0) 255 return NULL; 256 u = http_client_url_parse(url, &dst, &path); 257 if (u == NULL) 258 return NULL; 259 260 if (req == NULL) { 261 req_buf = wpabuf_alloc(os_strlen(url) + 1000); 262 if (req_buf == NULL) { 263 os_free(u); 264 return NULL; 265 } 266 req = req_buf; 267 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 ntohs(dst.sin_port)); 277 } 278 os_free(u); 279 280 c = http_client_addr(&dst, req, max_response, cb, cb_ctx); 281 if (c == NULL) { 282 wpabuf_free(req_buf); 283 return NULL; 284 } 285 286 return c; 287} 288 289 290void http_client_free(struct http_client *c) 291{ 292 if (c == NULL) 293 return; 294 httpread_destroy(c->hread); 295 wpabuf_free(c->req); 296 if (c->sd >= 0) { 297 eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE); 298 close(c->sd); 299 } 300 eloop_cancel_timeout(http_client_timeout, c, NULL); 301 os_free(c); 302} 303 304 305struct wpabuf * http_client_get_body(struct http_client *c) 306{ 307 if (c->hread == NULL) 308 return NULL; 309 wpabuf_set(&c->body, httpread_data_get(c->hread), 310 httpread_length_get(c->hread)); 311 return &c->body; 312} 313 314 315char * http_client_get_hdr_line(struct http_client *c, const char *tag) 316{ 317 if (c->hread == NULL) 318 return NULL; 319 return httpread_hdr_line_get(c->hread, tag); 320} 321 322 323char * 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 if (url == NULL) 333 return NULL; 334 335 if (os_strncmp(url, "http://", 7) == 0) 336 return url; /* absolute link */ 337 338 if (os_strncmp(base, "http://", 7) != 0) 339 return url; /* unable to handle base URL */ 340 341 len = os_strlen(url) + 1 + os_strlen(base) + 1; 342 n = os_malloc(len); 343 if (n == NULL) 344 return url; /* failed */ 345 346 if (url[0] == '/') { 347 pos = os_strchr(base + 7, '/'); 348 if (pos == NULL) { 349 os_snprintf(n, len, "%s%s", base, url); 350 } else { 351 os_memcpy(n, base, pos - base); 352 os_memcpy(n + (pos - base), url, os_strlen(url) + 1); 353 } 354 } else { 355 pos = os_strrchr(base + 7, '/'); 356 if (pos == NULL) { 357 os_snprintf(n, len, "%s/%s", base, url); 358 } else { 359 os_memcpy(n, base, pos - base + 1); 360 os_memcpy(n + (pos - base) + 1, url, os_strlen(url) + 361 1); 362 } 363 } 364 365 os_free(url); 366 367 return n; 368} 369