uclient.c 10 KB

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