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