2 * uclient - ustream based protocol client library
4 * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
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.
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.
18 #include <arpa/inet.h>
20 #include <libubox/ustream-ssl.h>
22 #include "uclient-utils.h"
23 #include "uclient-backend.h"
26 #define LIB_EXT "dylib"
31 char *uclient_get_addr(char *dest
, int *port
, union uclient_addr
*a
)
36 switch(a
->sa
.sa_family
) {
38 ptr
= &a
->sin
.sin_addr
;
39 portval
= a
->sin
.sin_port
;
42 ptr
= &a
->sin6
.sin6_addr
;
43 portval
= a
->sin6
.sin6_port
;
46 return strcpy(dest
, "Unknown");
49 inet_ntop(a
->sa
.sa_family
, ptr
, dest
, INET6_ADDRSTRLEN
);
51 *port
= ntohs(portval
);
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
)
61 struct uclient_url
*url
;
62 char *host_buf
, *uri_buf
, *auth_buf
, *next
;
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);
72 url
->backend
= backend
;
73 url
->location
= strcpy(uri_buf
, location
);
75 url
->host
= strncpy(host_buf
, host
, host_len
);
77 next
= strchr(host_buf
, '@');
82 if (uclient_urldecode(host_buf
, host_buf
, false) < 0)
88 if (!url
->auth
&& auth_str
)
89 url
->auth
= strcpy(auth_buf
, auth_str
);
91 /* Literal IPv6 address */
92 if (*url
->host
== '[') {
94 next
= strrchr(url
->host
, ']');
100 url
->port
= next
+ 1;
102 next
= strrchr(url
->host
, ':');
105 url
->port
= next
+ 1;
117 uclient_split_host(const char *base
, int *host_len
)
119 char *next
, *location
;
121 next
= strchr(base
, '/');
124 *host_len
= next
- base
;
127 *host_len
= strlen(base
);
133 struct uclient_url __hidden
*
134 uclient_get_url_location(struct uclient_url
*url
, const char *location
)
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;
143 if (strstr(location
, "://"))
144 return uclient_get_url(location
, url
->auth
);
146 if (location
[0] == '/')
147 uri_len
= strlen(location
) + 1;
149 uri_len
= strlen(url
->location
) + strlen(location
) + 2;
151 new_url
= calloc_a(sizeof(*url
),
155 &auth_buf
, auth_len
);
160 new_url
->backend
= url
->backend
;
161 new_url
->prefix
= url
->prefix
;
162 new_url
->host
= strcpy(host_buf
, url
->host
);
164 new_url
->port
= strcpy(port_buf
, url
->port
);
166 new_url
->auth
= strcpy(auth_buf
, url
->auth
);
168 new_url
->location
= uri_buf
;
169 if (location
[0] == '/')
170 strcpy(uri_buf
, location
);
172 int len
= strcspn(url
->location
, "?#");
175 memcpy(buf
, url
->location
, len
);
176 if (buf
[len
- 1] != '/') {
182 strcpy(buf
, location
);
188 struct uclient_url __hidden
*
189 uclient_get_url(const char *url_str
, const char *auth_str
)
191 static const struct uclient_backend
*backends
[] = {
192 &uclient_backend_http
,
195 const struct uclient_backend
*backend
;
196 const char * const *prefix
= NULL
;
197 struct uclient_url
*url
;
198 const char *location
;
202 for (i
= 0; i
< ARRAY_SIZE(backends
); i
++) {
205 for (prefix
= backends
[i
]->prefix
; *prefix
; prefix
++) {
206 prefix_len
= strlen(*prefix
);
208 if (!strncmp(url_str
, *prefix
, prefix_len
))
215 url_str
+= prefix_len
;
216 backend
= backends
[i
];
223 location
= uclient_split_host(url_str
, &host_len
);
224 url
= __uclient_get_url(backend
, url_str
, host_len
, location
, auth_str
);
228 url
->prefix
= prefix
- backend
->prefix
;
232 static void uclient_connection_timeout(struct uloop_timeout
*timeout
)
234 struct uclient
*cl
= container_of(timeout
, struct uclient
, connection_timeout
);
236 if (cl
->backend
->disconnect
)
237 cl
->backend
->disconnect(cl
);
239 uclient_backend_set_error(cl
, UCLIENT_ERROR_TIMEDOUT
);
242 static void __uclient_read_notify(struct uloop_timeout
*timeout
)
244 struct uclient
*cl
= container_of(timeout
, struct uclient
, read_notify
);
246 if (cl
->cb
->data_read
)
247 cl
->cb
->data_read(cl
);
250 struct uclient
*uclient_new(const char *url_str
, const char *auth_str
, const struct uclient_cb
*cb
)
253 struct uclient_url
*url
;
255 url
= uclient_get_url(url_str
, auth_str
);
259 cl
= url
->backend
->alloc();
263 cl
->backend
= url
->backend
;
266 cl
->timeout_msecs
= UCLIENT_DEFAULT_TIMEOUT_MS
;
267 cl
->connection_timeout
.cb
= uclient_connection_timeout
;
268 cl
->read_notify
.cb
= __uclient_read_notify
;
273 int uclient_set_proxy_url(struct uclient
*cl
, const char *url_str
, const char *auth_str
)
275 const struct uclient_backend
*backend
= cl
->backend
;
276 struct uclient_url
*url
;
280 if (!backend
->update_proxy_url
)
283 next
= strstr(url_str
, "://");
288 uclient_split_host(host
, &host_len
);
290 url
= __uclient_get_url(NULL
, host
, host_len
, url_str
, auth_str
);
297 if (backend
->update_proxy_url
)
298 backend
->update_proxy_url(cl
);
303 int uclient_set_url(struct uclient
*cl
, const char *url_str
, const char *auth_str
)
305 const struct uclient_backend
*backend
= cl
->backend
;
306 struct uclient_url
*url
;
308 url
= uclient_get_url(url_str
, auth_str
);
312 if (url
->backend
!= cl
->backend
) {
318 cl
->proxy_url
= NULL
;
323 if (backend
->update_url
)
324 backend
->update_url(cl
);
329 int uclient_set_timeout(struct uclient
*cl
, int msecs
)
334 cl
->timeout_msecs
= msecs
;
339 int uclient_connect(struct uclient
*cl
)
341 return cl
->backend
->connect(cl
);
344 void uclient_free(struct uclient
*cl
)
346 struct uclient_url
*url
= cl
->url
;
348 if (cl
->backend
->free
)
349 cl
->backend
->free(cl
);
356 int uclient_write(struct uclient
*cl
, const char *buf
, int len
)
358 if (!cl
->backend
->write
)
361 return cl
->backend
->write(cl
, buf
, len
);
364 int uclient_request(struct uclient
*cl
)
368 if (!cl
->backend
->request
)
371 err
= cl
->backend
->request(cl
);
375 uloop_timeout_set(&cl
->connection_timeout
, cl
->timeout_msecs
);
380 struct ustream_ssl_ctx
*uclient_new_ssl_context(const struct ustream_ssl_ops
**ops
)
382 static const struct ustream_ssl_ops
*ssl_ops
;
386 dlh
= dlopen("libustream-ssl." LIB_EXT
, RTLD_LAZY
| RTLD_LOCAL
);
390 ssl_ops
= dlsym(dlh
, "ustream_ssl_ops");
398 return ssl_ops
->context_new(false);
401 int uclient_read(struct uclient
*cl
, char *buf
, int len
)
403 if (!cl
->backend
->read
)
406 return cl
->backend
->read(cl
, buf
, len
);
409 void uclient_disconnect(struct uclient
*cl
)
411 uloop_timeout_cancel(&cl
->connection_timeout
);
412 uloop_timeout_cancel(&cl
->timeout
);
413 uloop_timeout_cancel(&cl
->read_notify
);
415 if (!cl
->backend
->disconnect
)
418 cl
->backend
->disconnect(cl
);
421 static void __uclient_backend_change_state(struct uloop_timeout
*timeout
)
423 struct uclient
*cl
= container_of(timeout
, struct uclient
, timeout
);
425 if (cl
->error_code
&& cl
->cb
->error
)
426 cl
->cb
->error(cl
, cl
->error_code
);
427 else if (cl
->eof
&& cl
->cb
->data_eof
)
428 cl
->cb
->data_eof(cl
);
431 static void uclient_backend_change_state(struct uclient
*cl
)
433 cl
->timeout
.cb
= __uclient_backend_change_state
;
434 uloop_timeout_set(&cl
->timeout
, 1);
437 void __hidden
uclient_backend_set_error(struct uclient
*cl
, int code
)
442 uloop_timeout_cancel(&cl
->connection_timeout
);
443 cl
->error_code
= code
;
444 uclient_backend_change_state(cl
);
447 void __hidden
uclient_backend_set_eof(struct uclient
*cl
)
449 if (cl
->eof
|| cl
->error_code
)
452 uloop_timeout_cancel(&cl
->connection_timeout
);
454 uclient_backend_change_state(cl
);
457 void __hidden
uclient_backend_reset_state(struct uclient
*cl
)
459 cl
->data_eof
= false;
462 uloop_timeout_cancel(&cl
->timeout
);
463 uloop_timeout_cancel(&cl
->read_notify
);
466 const char * uclient_strerror(unsigned err
)
469 case UCLIENT_ERROR_UNKNOWN
:
470 return "unknown error";
471 case UCLIENT_ERROR_CONNECT
:
472 return "connect failed";
473 case UCLIENT_ERROR_TIMEDOUT
:
475 case UCLIENT_ERROR_SSL_INVALID_CERT
:
476 return "ssl invalid cert";
477 case UCLIENT_ERROR_SSL_CN_MISMATCH
:
478 return "ssl cn mismatch";
479 case UCLIENT_ERROR_MISSING_SSL_CONTEXT
:
480 return "missing ssl context";
482 return "invalid error code";