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