1 /* 2 * uclient - ustream based protocol client library 3 * 4 * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org> 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 #include <arpa/inet.h> 19 #include <dlfcn.h> 20 #include <libubox/ustream-ssl.h> 21 #include "uclient.h" 22 #include "uclient-utils.h" 23 #include "uclient-backend.h" 24 25 #ifdef __APPLE__ 26 #define LIB_EXT "dylib" 27 #else 28 #define LIB_EXT "so" 29 #endif 30 31 char *uclient_get_addr(char *dest, int *port, union uclient_addr *a) 32 { 33 int portval; 34 void *ptr; 35 36 switch(a->sa.sa_family) { 37 case AF_INET: 38 ptr = &a->sin.sin_addr; 39 portval = a->sin.sin_port; 40 break; 41 case AF_INET6: 42 ptr = &a->sin6.sin6_addr; 43 portval = a->sin6.sin6_port; 44 break; 45 default: 46 if (port) 47 *port = 0; 48 return strcpy(dest, "Unknown"); 49 } 50 51 inet_ntop(a->sa.sa_family, ptr, dest, INET6_ADDRSTRLEN); 52 if (port) 53 *port = ntohs(portval); 54 55 return dest; 56 } 57 58 static struct uclient_url * 59 __uclient_get_url(const struct uclient_backend *backend, 60 const char *host, int host_len, 61 const char *location, const char *auth_str) 62 { 63 struct uclient_url *url; 64 char *host_buf, *uri_buf, *auth_buf, *next; 65 66 url = calloc_a(sizeof(*url), 67 &host_buf, host_len + 1, 68 &uri_buf, strlen(location) + 1, 69 &auth_buf, auth_str ? strlen(auth_str) + 1 : 0); 70 71 if (!url) 72 return NULL; 73 74 url->backend = backend; 75 url->location = strcpy(uri_buf, location); 76 if (host) 77 url->host = strncpy(host_buf, host, host_len); 78 79 next = strchr(host_buf, '@'); 80 if (next) { 81 *next = 0; 82 url->host = next + 1; 83 84 if (uclient_urldecode(host_buf, host_buf, false) < 0) 85 goto free; 86 87 url->auth = host_buf; 88 } 89 90 if (!url->auth && auth_str) 91 url->auth = strcpy(auth_buf, auth_str); 92 93 /* Literal IPv6 address */ 94 if (*url->host == '[') { 95 url->host++; 96 next = strrchr(url->host, ']'); 97 if (!next) 98 goto free; 99 100 *(next++) = 0; 101 if (*next == ':') 102 url->port = next + 1; 103 } else { 104 next = strrchr(url->host, ':'); 105 if (next) { 106 *next = 0; 107 url->port = next + 1; 108 } 109 } 110 111 return url; 112 113 free: 114 free(url); 115 return NULL; 116 } 117 118 static const char * 119 uclient_split_host(const char *base, int *host_len) 120 { 121 char *next, *location; 122 123 next = strchr(base, '/'); 124 if (next) { 125 location = next; 126 *host_len = next - base; 127 } else { 128 location = "/"; 129 *host_len = strlen(base); 130 } 131 132 return location; 133 } 134 135 struct uclient_url __hidden * 136 uclient_get_url_location(struct uclient_url *url, const char *location) 137 { 138 struct uclient_url *new_url; 139 char *host_buf, *uri_buf, *auth_buf, *port_buf; 140 int host_len = strlen(url->host) + 1; 141 int auth_len = url->auth ? strlen(url->auth) + 1 : 0; 142 int port_len = url->port ? strlen(url->port) + 1 : 0; 143 int uri_len; 144 145 if (strstr(location, "://")) 146 return uclient_get_url(location, url->auth); 147 148 if (location[0] == '/') 149 uri_len = strlen(location) + 1; 150 else 151 uri_len = strlen(url->location) + strlen(location) + 2; 152 153 new_url = calloc_a(sizeof(*url), 154 &host_buf, host_len, 155 &port_buf, port_len, 156 &uri_buf, uri_len, 157 &auth_buf, auth_len); 158 159 if (!new_url) 160 return NULL; 161 162 new_url->backend = url->backend; 163 new_url->prefix = url->prefix; 164 new_url->host = strcpy(host_buf, url->host); 165 if (url->port) 166 new_url->port = strcpy(port_buf, url->port); 167 if (url->auth) 168 new_url->auth = strcpy(auth_buf, url->auth); 169 170 new_url->location = uri_buf; 171 if (location[0] == '/') 172 strcpy(uri_buf, location); 173 else { 174 int len = strcspn(url->location, "?#"); 175 char *buf = uri_buf; 176 177 memcpy(buf, url->location, len); 178 if (buf[len - 1] != '/') { 179 buf[len] = '/'; 180 len++; 181 } 182 183 buf += len; 184 strcpy(buf, location); 185 } 186 187 return new_url; 188 } 189 190 struct uclient_url __hidden * 191 uclient_get_url(const char *url_str, const char *auth_str) 192 { 193 static const struct uclient_backend *backends[] = { 194 &uclient_backend_http, 195 }; 196 197 const struct uclient_backend *backend; 198 const char * const *prefix = NULL; 199 struct uclient_url *url; 200 const char *location; 201 int host_len; 202 unsigned int i; 203 204 for (i = 0; i < ARRAY_SIZE(backends); i++) { 205 int prefix_len = 0; 206 207 for (prefix = backends[i]->prefix; *prefix; prefix++) { 208 prefix_len = strlen(*prefix); 209 210 if (!strncmp(url_str, *prefix, prefix_len)) 211 break; 212 } 213 214 if (!*prefix) 215 continue; 216 217 url_str += prefix_len; 218 backend = backends[i]; 219 break; 220 } 221 222 if (!*prefix) 223 return NULL; 224 225 location = uclient_split_host(url_str, &host_len); 226 url = __uclient_get_url(backend, url_str, host_len, location, auth_str); 227 if (!url) 228 return NULL; 229 230 url->prefix = prefix - backend->prefix; 231 return url; 232 } 233 234 static void uclient_connection_timeout(struct uloop_timeout *timeout) 235 { 236 struct uclient *cl = container_of(timeout, struct uclient, connection_timeout); 237 238 if (cl->backend->disconnect) 239 cl->backend->disconnect(cl); 240 241 uclient_backend_set_error(cl, UCLIENT_ERROR_TIMEDOUT); 242 } 243 244 static void __uclient_read_notify(struct uloop_timeout *timeout) 245 { 246 struct uclient *cl = container_of(timeout, struct uclient, read_notify); 247 248 if (cl->cb->data_read) 249 cl->cb->data_read(cl); 250 } 251 252 struct uclient *uclient_new(const char *url_str, const char *auth_str, const struct uclient_cb *cb) 253 { 254 struct uclient *cl; 255 struct uclient_url *url; 256 257 url = uclient_get_url(url_str, auth_str); 258 if (!url) 259 return NULL; 260 261 cl = url->backend->alloc(); 262 if (!cl) { 263 free(url); 264 return NULL; 265 } 266 267 cl->backend = url->backend; 268 cl->cb = cb; 269 cl->url = url; 270 cl->timeout_msecs = UCLIENT_DEFAULT_TIMEOUT_MS; 271 cl->connection_timeout.cb = uclient_connection_timeout; 272 cl->read_notify.cb = __uclient_read_notify; 273 274 return cl; 275 } 276 277 int uclient_set_proxy_url(struct uclient *cl, const char *url_str, const char *auth_str) 278 { 279 const struct uclient_backend *backend = cl->backend; 280 struct uclient_url *url; 281 int host_len; 282 char *next, *host; 283 284 if (!backend->update_proxy_url) 285 return -1; 286 287 next = strstr(url_str, "://"); 288 if (!next) 289 return -1; 290 291 host = next + 3; 292 uclient_split_host(host, &host_len); 293 294 url = __uclient_get_url(NULL, host, host_len, url_str, auth_str); 295 if (!url) 296 return -1; 297 298 free(cl->proxy_url); 299 cl->proxy_url = url; 300 301 if (backend->update_proxy_url) 302 backend->update_proxy_url(cl); 303 304 return 0; 305 } 306 307 int uclient_set_url(struct uclient *cl, const char *url_str, const char *auth_str) 308 { 309 const struct uclient_backend *backend = cl->backend; 310 struct uclient_url *url; 311 312 url = uclient_get_url(url_str, auth_str); 313 if (!url) 314 return -1; 315 316 if (url->backend != cl->backend) { 317 free(url); 318 return -1; 319 } 320 321 free(cl->proxy_url); 322 cl->proxy_url = NULL; 323 324 free(cl->url); 325 cl->url = url; 326 327 if (backend->update_url) 328 backend->update_url(cl); 329 330 return 0; 331 } 332 333 int uclient_set_timeout(struct uclient *cl, int msecs) 334 { 335 if (msecs <= 0) 336 return -EINVAL; 337 338 cl->timeout_msecs = msecs; 339 340 return 0; 341 } 342 343 int uclient_connect(struct uclient *cl) 344 { 345 return cl->backend->connect(cl); 346 } 347 348 void uclient_free(struct uclient *cl) 349 { 350 struct uclient_url *url = cl->url; 351 struct uclient_url *proxy_url = cl->proxy_url; 352 353 /* Cancel timers embedded in cl before the backend frees the memory, 354 * otherwise a still-pending timeout would reference freed memory. */ 355 uloop_timeout_cancel(&cl->connection_timeout); 356 uloop_timeout_cancel(&cl->timeout); 357 uloop_timeout_cancel(&cl->read_notify); 358 359 if (cl->backend->free) 360 cl->backend->free(cl); 361 else 362 free(cl); 363 364 free(url); 365 free(proxy_url); 366 } 367 368 int uclient_write(struct uclient *cl, const char *buf, int len) 369 { 370 if (!cl->backend->write) 371 return -1; 372 373 return cl->backend->write(cl, buf, len); 374 } 375 376 int uclient_request(struct uclient *cl) 377 { 378 int err; 379 380 if (!cl->backend->request) 381 return -1; 382 383 err = cl->backend->request(cl); 384 if (err) 385 return err; 386 387 uloop_timeout_set(&cl->connection_timeout, cl->timeout_msecs); 388 389 return 0; 390 } 391 392 struct ustream_ssl_ctx *uclient_new_ssl_context(const struct ustream_ssl_ops **ops) 393 { 394 static const struct ustream_ssl_ops *ssl_ops; 395 void *dlh; 396 397 if (!ssl_ops) { 398 dlh = dlopen("libustream-ssl." LIB_EXT, RTLD_LAZY | RTLD_LOCAL); 399 if (!dlh) 400 return NULL; 401 402 ssl_ops = dlsym(dlh, "ustream_ssl_ops"); 403 if (!ssl_ops) { 404 dlclose(dlh); 405 return NULL; 406 } 407 } 408 409 *ops = ssl_ops; 410 return ssl_ops->context_new(false); 411 } 412 413 int uclient_read(struct uclient *cl, char *buf, int len) 414 { 415 if (!cl->backend->read) 416 return -1; 417 418 return cl->backend->read(cl, buf, len); 419 } 420 421 int uclient_pending_bytes(struct uclient *cl, bool write) 422 { 423 if (!cl->backend->pending_bytes) 424 return -1; 425 426 return cl->backend->pending_bytes(cl, write); 427 } 428 429 void uclient_disconnect(struct uclient *cl) 430 { 431 uloop_timeout_cancel(&cl->connection_timeout); 432 uloop_timeout_cancel(&cl->timeout); 433 uloop_timeout_cancel(&cl->read_notify); 434 435 if (!cl->backend->disconnect) 436 return; 437 438 cl->backend->disconnect(cl); 439 } 440 441 static void __uclient_backend_change_state(struct uloop_timeout *timeout) 442 { 443 struct uclient *cl = container_of(timeout, struct uclient, timeout); 444 445 if (cl->error_code && cl->cb->error) 446 cl->cb->error(cl, cl->error_code); 447 else if (cl->eof && cl->cb->data_eof) 448 cl->cb->data_eof(cl); 449 } 450 451 static void uclient_backend_change_state(struct uclient *cl) 452 { 453 cl->timeout.cb = __uclient_backend_change_state; 454 uloop_timeout_set(&cl->timeout, 1); 455 } 456 457 void __hidden uclient_backend_set_error(struct uclient *cl, int code) 458 { 459 if (cl->error_code) 460 return; 461 462 uloop_timeout_cancel(&cl->connection_timeout); 463 cl->error_code = code; 464 uclient_backend_change_state(cl); 465 } 466 467 void __hidden uclient_backend_set_eof(struct uclient *cl) 468 { 469 if (cl->eof || cl->error_code) 470 return; 471 472 uloop_timeout_cancel(&cl->connection_timeout); 473 cl->eof = true; 474 uclient_backend_change_state(cl); 475 } 476 477 void __hidden uclient_backend_reset_state(struct uclient *cl) 478 { 479 cl->data_eof = false; 480 cl->eof = false; 481 cl->error_code = 0; 482 uloop_timeout_cancel(&cl->timeout); 483 uloop_timeout_cancel(&cl->read_notify); 484 } 485 486 const char * uclient_strerror(unsigned err) 487 { 488 switch (err) { 489 case UCLIENT_ERROR_UNKNOWN: 490 return "unknown error"; 491 case UCLIENT_ERROR_CONNECT: 492 return "connect failed"; 493 case UCLIENT_ERROR_TIMEDOUT: 494 return "timeout"; 495 case UCLIENT_ERROR_SSL_INVALID_CERT: 496 return "ssl invalid cert"; 497 case UCLIENT_ERROR_SSL_CN_MISMATCH: 498 return "ssl cn mismatch"; 499 case UCLIENT_ERROR_MISSING_SSL_CONTEXT: 500 return "missing ssl context"; 501 default: 502 return "invalid error code"; 503 } 504 } 505
This page was automatically generated by LXR 0.3.1. • OpenWrt