uclient.c 9.1 KB

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