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 return strcpy(dest, "Unknown"); 47 } 48 49 inet_ntop(a->sa.sa_family, ptr, dest, INET6_ADDRSTRLEN); 50 if (port) 51 *port = ntohs(portval); 52 53 return dest; 54 } 55 56 static struct uclient_url * 57 __uclient_get_url(const struct uclient_backend *backend, 58 const char *host, int host_len, 59 const char *location, const char *auth_str) 60 { 61 struct uclient_url *url; 62 char *host_buf, *uri_buf, *auth_buf, *next; 63 64 url = calloc_a(sizeof(*url), 65 &host_buf, host_len + 1, 66 &uri_buf, strlen(location) + 1, 67 &auth_buf, auth_str ? strlen(auth_str) + 1 : 0); 68 69 if (!url) 70 return NULL; 71 72 url->backend = backend; 73 url->location = strcpy(uri_buf, location); 74 if (host) 75 url->host = strncpy(host_buf, host, host_len); 76 77 next = strchr(host_buf, '@'); 78 if (next) { 79 *next = 0; 80 url->host = next + 1; 81 82 if (uclient_urldecode(host_buf, host_buf, false) < 0) 83 goto free; 84 85 url->auth = host_buf; 86 } 87 88 if (!url->auth && auth_str) 89 url->auth = strcpy(auth_buf, auth_str); 90 91 /* Literal IPv6 address */ 92 if (*url->host == '[') { 93 url->host++; 94 next = strrchr(url->host, ']'); 95 if (!next) 96 goto free; 97 98 *(next++) = 0; 99 if (*next == ':') 100 url->port = next + 1; 101 } else { 102 next = strrchr(url->host, ':'); 103 if (next) { 104 *next = 0; 105 url->port = next + 1; 106 } 107 } 108 109 return url; 110 111 free: 112 free(url); 113 return NULL; 114 } 115 116 static const char * 117 uclient_split_host(const char *base, int *host_len) 118 { 119 char *next, *location; 120 121 next = strchr(base, '/'); 122 if (next) { 123 location = next; 124 *host_len = next - base; 125 } else { 126 location = "/"; 127 *host_len = strlen(base); 128 } 129 130 return location; 131 } 132 133 struct uclient_url __hidden * 134 uclient_get_url_location(struct uclient_url *url, const char *location) 135 { 136 struct uclient_url *new_url; 137 char *host_buf, *uri_buf, *auth_buf, *port_buf; 138 int host_len = strlen(url->host) + 1; 139 int auth_len = url->auth ? strlen(url->auth) + 1 : 0; 140 int port_len = url->port ? strlen(url->port) + 1 : 0; 141 int uri_len; 142 143 if (strstr(location, "://")) 144 return uclient_get_url(location, url->auth); 145 146 if (location[0] == '/') 147 uri_len = strlen(location) + 1; 148 else 149 uri_len = strlen(url->location) + strlen(location) + 2; 150 151 new_url = calloc_a(sizeof(*url), 152 &host_buf, host_len, 153 &port_buf, port_len, 154 &uri_buf, uri_len, 155 &auth_buf, auth_len); 156 157 if (!new_url) 158 return NULL; 159 160 new_url->backend = url->backend; 161 new_url->prefix = url->prefix; 162 new_url->host = strcpy(host_buf, url->host); 163 if (url->port) 164 new_url->port = strcpy(port_buf, url->port); 165 if (url->auth) 166 new_url->auth = strcpy(auth_buf, url->auth); 167 168 new_url->location = uri_buf; 169 if (location[0] == '/') 170 strcpy(uri_buf, location); 171 else { 172 int len = strcspn(url->location, "?#"); 173 char *buf = uri_buf; 174 175 memcpy(buf, url->location, len); 176 if (buf[len - 1] != '/') { 177 buf[len] = '/'; 178 len++; 179 } 180 181 buf += len; 182 strcpy(buf, location); 183 } 184 185 return new_url; 186 } 187 188 struct uclient_url __hidden * 189 uclient_get_url(const char *url_str, const char *auth_str) 190 { 191 static const struct uclient_backend *backends[] = { 192 &uclient_backend_http, 193 }; 194 195 const struct uclient_backend *backend; 196 const char * const *prefix = NULL; 197 struct uclient_url *url; 198 const char *location; 199 int host_len; 200 unsigned int i; 201 202 for (i = 0; i < ARRAY_SIZE(backends); i++) { 203 int prefix_len = 0; 204 205 for (prefix = backends[i]->prefix; *prefix; prefix++) { 206 prefix_len = strlen(*prefix); 207 208 if (!strncmp(url_str, *prefix, prefix_len)) 209 break; 210 } 211 212 if (!*prefix) 213 continue; 214 215 url_str += prefix_len; 216 backend = backends[i]; 217 break; 218 } 219 220 if (!*prefix) 221 return NULL; 222 223 location = uclient_split_host(url_str, &host_len); 224 url = __uclient_get_url(backend, url_str, host_len, location, auth_str); 225 if (!url) 226 return NULL; 227 228 url->prefix = prefix - backend->prefix; 229 return url; 230 } 231 232 static void uclient_connection_timeout(struct uloop_timeout *timeout) 233 { 234 struct uclient *cl = container_of(timeout, struct uclient, connection_timeout); 235 236 if (cl->backend->disconnect) 237 cl->backend->disconnect(cl); 238 239 uclient_backend_set_error(cl, UCLIENT_ERROR_TIMEDOUT); 240 } 241 242 static void __uclient_read_notify(struct uloop_timeout *timeout) 243 { 244 struct uclient *cl = container_of(timeout, struct uclient, read_notify); 245 246 if (cl->cb->data_read) 247 cl->cb->data_read(cl); 248 } 249 250 struct uclient *uclient_new(const char *url_str, const char *auth_str, const struct uclient_cb *cb) 251 { 252 struct uclient *cl; 253 struct uclient_url *url; 254 255 url = uclient_get_url(url_str, auth_str); 256 if (!url) 257 return NULL; 258 259 cl = url->backend->alloc(); 260 if (!cl) 261 return NULL; 262 263 cl->backend = url->backend; 264 cl->cb = cb; 265 cl->url = url; 266 cl->timeout_msecs = UCLIENT_DEFAULT_TIMEOUT_MS; 267 cl->connection_timeout.cb = uclient_connection_timeout; 268 cl->read_notify.cb = __uclient_read_notify; 269 270 return cl; 271 } 272 273 int uclient_set_proxy_url(struct uclient *cl, const char *url_str, const char *auth_str) 274 { 275 const struct uclient_backend *backend = cl->backend; 276 struct uclient_url *url; 277 int host_len; 278 char *next, *host; 279 280 if (!backend->update_proxy_url) 281 return -1; 282 283 next = strstr(url_str, "://"); 284 if (!next) 285 return -1; 286 287 host = next + 3; 288 uclient_split_host(host, &host_len); 289 290 url = __uclient_get_url(NULL, host, host_len, url_str, auth_str); 291 if (!url) 292 return -1; 293 294 free(cl->proxy_url); 295 cl->proxy_url = url; 296 297 if (backend->update_proxy_url) 298 backend->update_proxy_url(cl); 299 300 return 0; 301 } 302 303 int uclient_set_url(struct uclient *cl, const char *url_str, const char *auth_str) 304 { 305 const struct uclient_backend *backend = cl->backend; 306 struct uclient_url *url; 307 308 url = uclient_get_url(url_str, auth_str); 309 if (!url) 310 return -1; 311 312 if (url->backend != cl->backend) { 313 free(url); 314 return -1; 315 } 316 317 free(cl->proxy_url); 318 cl->proxy_url = NULL; 319 320 free(cl->url); 321 cl->url = url; 322 323 if (backend->update_url) 324 backend->update_url(cl); 325 326 return 0; 327 } 328 329 int uclient_set_timeout(struct uclient *cl, int msecs) 330 { 331 if (msecs <= 0) 332 return -EINVAL; 333 334 cl->timeout_msecs = msecs; 335 336 return 0; 337 } 338 339 int uclient_connect(struct uclient *cl) 340 { 341 return cl->backend->connect(cl); 342 } 343 344 void uclient_free(struct uclient *cl) 345 { 346 struct uclient_url *url = cl->url; 347 348 if (cl->backend->free) 349 cl->backend->free(cl); 350 else 351 free(cl); 352 353 free(url); 354 } 355 356 int uclient_write(struct uclient *cl, const char *buf, int len) 357 { 358 if (!cl->backend->write) 359 return -1; 360 361 return cl->backend->write(cl, buf, len); 362 } 363 364 int uclient_request(struct uclient *cl) 365 { 366 int err; 367 368 if (!cl->backend->request) 369 return -1; 370 371 err = cl->backend->request(cl); 372 if (err) 373 return err; 374 375 uloop_timeout_set(&cl->connection_timeout, cl->timeout_msecs); 376 377 return 0; 378 } 379 380 struct ustream_ssl_ctx *uclient_new_ssl_context(const struct ustream_ssl_ops **ops) 381 { 382 static const struct ustream_ssl_ops *ssl_ops; 383 void *dlh; 384 385 if (!ssl_ops) { 386 dlh = dlopen("libustream-ssl." LIB_EXT, RTLD_LAZY | RTLD_LOCAL); 387 if (!dlh) 388 return NULL; 389 390 ssl_ops = dlsym(dlh, "ustream_ssl_ops"); 391 if (!ssl_ops) { 392 dlclose(dlh); 393 return NULL; 394 } 395 } 396 397 *ops = ssl_ops; 398 return ssl_ops->context_new(false); 399 } 400 401 int uclient_read(struct uclient *cl, char *buf, int len) 402 { 403 if (!cl->backend->read) 404 return -1; 405 406 return cl->backend->read(cl, buf, len); 407 } 408 409 int uclient_pending_bytes(struct uclient *cl, bool write) 410 { 411 if (!cl->backend->pending_bytes) 412 return -1; 413 414 return cl->backend->pending_bytes(cl, write); 415 } 416 417 void uclient_disconnect(struct uclient *cl) 418 { 419 uloop_timeout_cancel(&cl->connection_timeout); 420 uloop_timeout_cancel(&cl->timeout); 421 uloop_timeout_cancel(&cl->read_notify); 422 423 if (!cl->backend->disconnect) 424 return; 425 426 cl->backend->disconnect(cl); 427 } 428 429 static void __uclient_backend_change_state(struct uloop_timeout *timeout) 430 { 431 struct uclient *cl = container_of(timeout, struct uclient, timeout); 432 433 if (cl->error_code && cl->cb->error) 434 cl->cb->error(cl, cl->error_code); 435 else if (cl->eof && cl->cb->data_eof) 436 cl->cb->data_eof(cl); 437 } 438 439 static void uclient_backend_change_state(struct uclient *cl) 440 { 441 cl->timeout.cb = __uclient_backend_change_state; 442 uloop_timeout_set(&cl->timeout, 1); 443 } 444 445 void __hidden uclient_backend_set_error(struct uclient *cl, int code) 446 { 447 if (cl->error_code) 448 return; 449 450 uloop_timeout_cancel(&cl->connection_timeout); 451 cl->error_code = code; 452 uclient_backend_change_state(cl); 453 } 454 455 void __hidden uclient_backend_set_eof(struct uclient *cl) 456 { 457 if (cl->eof || cl->error_code) 458 return; 459 460 uloop_timeout_cancel(&cl->connection_timeout); 461 cl->eof = true; 462 uclient_backend_change_state(cl); 463 } 464 465 void __hidden uclient_backend_reset_state(struct uclient *cl) 466 { 467 cl->data_eof = false; 468 cl->eof = false; 469 cl->error_code = 0; 470 uloop_timeout_cancel(&cl->timeout); 471 uloop_timeout_cancel(&cl->read_notify); 472 } 473 474 const char * uclient_strerror(unsigned err) 475 { 476 switch (err) { 477 case UCLIENT_ERROR_UNKNOWN: 478 return "unknown error"; 479 case UCLIENT_ERROR_CONNECT: 480 return "connect failed"; 481 case UCLIENT_ERROR_TIMEDOUT: 482 return "timeout"; 483 case UCLIENT_ERROR_SSL_INVALID_CERT: 484 return "ssl invalid cert"; 485 case UCLIENT_ERROR_SSL_CN_MISMATCH: 486 return "ssl cn mismatch"; 487 case UCLIENT_ERROR_MISSING_SSL_CONTEXT: 488 return "missing ssl context"; 489 default: 490 return "invalid error code"; 491 } 492 } 493
This page was automatically generated by LXR 0.3.1. • OpenWrt