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 size_t send_len; 89 90 send_len = wpabuf_len(c->req) - c->req_pos; 91 wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu " 92 "bytes remaining)", 93 inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port), 94 (unsigned long) wpabuf_len(c->req), 95 (unsigned long) send_len); 96 97 res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, send_len, 0); 98 if (res < 0) { 99 wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s", 100 strerror(errno)); 101 eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE); 102 c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED); 103 return; 104 } 105 106 if ((size_t) res < send_len) { 107 wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes " 108 "remaining", 109 res, (unsigned long) wpabuf_len(c->req), 110 (unsigned long) send_len - 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 goto fail; 151 152 if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) { 153 wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s", 154 strerror(errno)); 155 goto fail; 156 } 157 158 if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) { 159 if (errno != EINPROGRESS) { 160 wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s", 161 strerror(errno)); 162 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 if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready, 172 c, NULL) || 173 eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0, 174 http_client_timeout, c, NULL)) 175 goto fail; 176 177 c->req = req; 178 179 return c; 180 181fail: 182 http_client_free(c); 183 return NULL; 184} 185 186 187char * http_client_url_parse(const char *url, struct sockaddr_in *dst, 188 char **ret_path) 189{ 190 char *u, *addr, *port, *path; 191 192 u = os_strdup(url); 193 if (u == NULL) 194 return NULL; 195 196 os_memset(dst, 0, sizeof(*dst)); 197 dst->sin_family = AF_INET; 198 addr = u + 7; 199 path = os_strchr(addr, '/'); 200 port = os_strchr(addr, ':'); 201 if (path == NULL) { 202 path = "/"; 203 } else { 204 *path = '\0'; /* temporary nul termination for address */ 205 if (port > path) 206 port = NULL; 207 } 208 if (port) 209 *port++ = '\0'; 210 211 if (inet_aton(addr, &dst->sin_addr) == 0) { 212 /* TODO: name lookup */ 213 wpa_printf(MSG_DEBUG, "HTTP: Unsupported address in URL '%s' " 214 "(addr='%s' port='%s')", 215 url, addr, port); 216 os_free(u); 217 return NULL; 218 } 219 220 if (port) 221 dst->sin_port = htons(atoi(port)); 222 else 223 dst->sin_port = htons(80); 224 225 if (*path == '\0') { 226 /* remove temporary nul termination for address */ 227 *path = '/'; 228 } 229 230 *ret_path = path; 231 232 return u; 233} 234 235 236struct 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 struct wpabuf *req_buf = NULL; 247 248 if (os_strncmp(url, "http://", 7) != 0) 249 return NULL; 250 u = http_client_url_parse(url, &dst, &path); 251 if (u == NULL) 252 return NULL; 253 254 if (req == NULL) { 255 req_buf = wpabuf_alloc(os_strlen(url) + 1000); 256 if (req_buf == NULL) { 257 os_free(u); 258 return NULL; 259 } 260 req = req_buf; 261 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 ntohs(dst.sin_port)); 271 } 272 os_free(u); 273 274 c = http_client_addr(&dst, req, max_response, cb, cb_ctx); 275 if (c == NULL) { 276 wpabuf_free(req_buf); 277 return NULL; 278 } 279 280 return c; 281} 282 283 284void http_client_free(struct http_client *c) 285{ 286 if (c == NULL) 287 return; 288 httpread_destroy(c->hread); 289 wpabuf_free(c->req); 290 if (c->sd >= 0) { 291 eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE); 292 close(c->sd); 293 } 294 eloop_cancel_timeout(http_client_timeout, c, NULL); 295 os_free(c); 296} 297 298 299struct wpabuf * http_client_get_body(struct http_client *c) 300{ 301 if (c->hread == NULL) 302 return NULL; 303 wpabuf_set(&c->body, httpread_data_get(c->hread), 304 httpread_length_get(c->hread)); 305 return &c->body; 306} 307 308 309char * http_client_get_hdr_line(struct http_client *c, const char *tag) 310{ 311 if (c->hread == NULL) 312 return NULL; 313 return httpread_hdr_line_get(c->hread, tag); 314} 315 316 317char * 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 if (url == NULL) 327 return NULL; 328 329 if (os_strncmp(url, "http://", 7) == 0) 330 return url; /* absolute link */ 331 332 if (os_strncmp(base, "http://", 7) != 0) 333 return url; /* unable to handle base URL */ 334 335 len = os_strlen(url) + 1 + os_strlen(base) + 1; 336 n = os_malloc(len); 337 if (n == NULL) 338 return url; /* failed */ 339 340 if (url[0] == '/') { 341 pos = os_strchr(base + 7, '/'); 342 if (pos == NULL) { 343 os_snprintf(n, len, "%s%s", base, url); 344 } else { 345 os_memcpy(n, base, pos - base); 346 os_memcpy(n + (pos - base), url, os_strlen(url) + 1); 347 } 348 } else { 349 pos = os_strrchr(base + 7, '/'); 350 if (pos == NULL) { 351 os_snprintf(n, len, "%s/%s", base, url); 352 } else { 353 os_memcpy(n, base, pos - base + 1); 354 os_memcpy(n + (pos - base) + 1, url, os_strlen(url) + 355 1); 356 } 357 } 358 359 os_free(url); 360 361 return n; 362} 363