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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt