• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/uclient/uclient.c

  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