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>
19 #include <libubox/ustream-ssl.h>
21 #include "uclient-utils.h"
22 #include "uclient-backend.h"
24 char *uclient_get_addr(char *dest
, int *port
, union uclient_addr
*a
)
29 switch(a
->sa
.sa_family
) {
31 ptr
= &a
->sin
.sin_addr
;
32 portval
= a
->sin
.sin_port
;
35 ptr
= &a
->sin6
.sin6_addr
;
36 portval
= a
->sin6
.sin6_port
;
39 return strcpy(dest
, "Unknown");
42 inet_ntop(a
->sa
.sa_family
, ptr
, dest
, INET6_ADDRSTRLEN
);
44 *port
= ntohs(portval
);
49 static struct uclient_url
*
50 __uclient_get_url(const struct uclient_backend
*backend
,
51 const char *host
, int host_len
,
52 const char *location
, const char *auth_str
)
54 struct uclient_url
*url
;
55 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);
65 url
->backend
= backend
;
66 url
->location
= strcpy(uri_buf
, location
);
68 url
->host
= strncpy(host_buf
, host
, host_len
);
70 next
= strchr(host_buf
, '@');
75 if (uclient_urldecode(host_buf
, host_buf
, false) < 0)
81 if (!url
->auth
&& auth_str
)
82 url
->auth
= strcpy(auth_buf
, auth_str
);
84 /* Literal IPv6 address */
85 if (*url
->host
== '[') {
87 next
= strrchr(url
->host
, ']');
95 next
= strrchr(url
->host
, ':');
110 uclient_split_host(const char *base
, int *host_len
)
112 char *next
, *location
;
114 next
= strchr(base
, '/');
117 *host_len
= next
- base
;
120 *host_len
= strlen(base
);
126 struct uclient_url __hidden
*
127 uclient_get_url_location(struct uclient_url
*url
, const char *location
)
129 struct uclient_url
*new_url
;
130 char *host_buf
, *uri_buf
, *auth_buf
, *port_buf
;
131 int host_len
= strlen(url
->host
) + 1;
132 int auth_len
= url
->auth
? strlen(url
->auth
) + 1 : 0;
133 int port_len
= url
->port
? strlen(url
->port
) + 1 : 0;
136 if (strstr(location
, "://"))
137 return uclient_get_url(location
, url
->auth
);
139 if (location
[0] == '/')
140 uri_len
= strlen(location
) + 1;
142 uri_len
= strlen(url
->location
) + strlen(location
) + 2;
144 new_url
= calloc_a(sizeof(*url
),
148 &auth_buf
, auth_len
);
153 new_url
->backend
= url
->backend
;
154 new_url
->prefix
= url
->prefix
;
155 new_url
->host
= strcpy(host_buf
, url
->host
);
157 new_url
->port
= strcpy(port_buf
, url
->port
);
159 new_url
->auth
= strcpy(auth_buf
, url
->auth
);
161 new_url
->location
= uri_buf
;
162 if (location
[0] == '/')
163 strcpy(uri_buf
, location
);
165 int len
= strcspn(url
->location
, "?#");
168 memcpy(buf
, url
->location
, len
);
169 if (buf
[len
- 1] != '/') {
175 strcpy(buf
, location
);
181 struct uclient_url __hidden
*
182 uclient_get_url(const char *url_str
, const char *auth_str
)
184 static const struct uclient_backend
*backends
[] = {
185 &uclient_backend_http
,
188 const struct uclient_backend
*backend
;
189 const char * const *prefix
= NULL
;
190 struct uclient_url
*url
;
191 const char *location
;
195 for (i
= 0; i
< ARRAY_SIZE(backends
); i
++) {
198 for (prefix
= backends
[i
]->prefix
; *prefix
; prefix
++) {
199 prefix_len
= strlen(*prefix
);
201 if (!strncmp(url_str
, *prefix
, prefix_len
))
208 url_str
+= prefix_len
;
209 backend
= backends
[i
];
216 location
= uclient_split_host(url_str
, &host_len
);
217 url
= __uclient_get_url(backend
, url_str
, host_len
, location
, auth_str
);
221 url
->prefix
= prefix
- backend
->prefix
;
225 static void uclient_connection_timeout(struct uloop_timeout
*timeout
)
227 struct uclient
*cl
= container_of(timeout
, struct uclient
, connection_timeout
);
229 if (cl
->backend
->disconnect
)
230 cl
->backend
->disconnect(cl
);
232 uclient_backend_set_error(cl
, UCLIENT_ERROR_TIMEDOUT
);
235 struct uclient
*uclient_new(const char *url_str
, const char *auth_str
, const struct uclient_cb
*cb
)
238 struct uclient_url
*url
;
240 url
= uclient_get_url(url_str
, auth_str
);
244 cl
= url
->backend
->alloc();
248 cl
->backend
= url
->backend
;
251 cl
->timeout_msecs
= UCLIENT_DEFAULT_TIMEOUT_MS
;
252 cl
->connection_timeout
.cb
= uclient_connection_timeout
;
257 int uclient_set_proxy_url(struct uclient
*cl
, const char *url_str
, const char *auth_str
)
259 const struct uclient_backend
*backend
= cl
->backend
;
260 struct uclient_url
*url
;
264 if (!backend
->update_proxy_url
)
267 next
= strstr(url_str
, "://");
272 uclient_split_host(host
, &host_len
);
274 url
= __uclient_get_url(NULL
, host
, host_len
, url_str
, auth_str
);
281 if (backend
->update_proxy_url
)
282 backend
->update_proxy_url(cl
);
287 int uclient_set_url(struct uclient
*cl
, const char *url_str
, const char *auth_str
)
289 const struct uclient_backend
*backend
= cl
->backend
;
290 struct uclient_url
*url
= cl
->url
;
292 url
= uclient_get_url(url_str
, auth_str
);
296 if (url
->backend
!= cl
->backend
) {
302 cl
->proxy_url
= NULL
;
307 if (backend
->update_url
)
308 backend
->update_url(cl
);
313 int uclient_set_timeout(struct uclient
*cl
, int msecs
)
318 cl
->timeout_msecs
= msecs
;
323 int uclient_connect(struct uclient
*cl
)
325 return cl
->backend
->connect(cl
);
328 void uclient_free(struct uclient
*cl
)
330 struct uclient_url
*url
= cl
->url
;
332 if (cl
->backend
->free
)
333 cl
->backend
->free(cl
);
340 int uclient_write(struct uclient
*cl
, const char *buf
, int len
)
342 if (!cl
->backend
->write
)
345 return cl
->backend
->write(cl
, buf
, len
);
348 int uclient_request(struct uclient
*cl
)
352 if (!cl
->backend
->request
)
355 err
= cl
->backend
->request(cl
);
359 uloop_timeout_set(&cl
->connection_timeout
, cl
->timeout_msecs
);
364 int uclient_read(struct uclient
*cl
, char *buf
, int len
)
366 if (!cl
->backend
->read
)
369 return cl
->backend
->read(cl
, buf
, len
);
372 void uclient_disconnect(struct uclient
*cl
)
374 uloop_timeout_cancel(&cl
->connection_timeout
);
376 if (!cl
->backend
->disconnect
)
379 cl
->backend
->disconnect(cl
);
382 static void __uclient_backend_change_state(struct uloop_timeout
*timeout
)
384 struct uclient
*cl
= container_of(timeout
, struct uclient
, timeout
);
386 if (cl
->error_code
&& cl
->cb
->error
)
387 cl
->cb
->error(cl
, cl
->error_code
);
388 else if (cl
->eof
&& cl
->cb
->data_eof
)
389 cl
->cb
->data_eof(cl
);
392 static void uclient_backend_change_state(struct uclient
*cl
)
394 cl
->timeout
.cb
= __uclient_backend_change_state
;
395 uloop_timeout_set(&cl
->timeout
, 1);
398 void __hidden
uclient_backend_set_error(struct uclient
*cl
, int code
)
403 uloop_timeout_cancel(&cl
->connection_timeout
);
404 cl
->error_code
= code
;
405 uclient_backend_change_state(cl
);
408 void __hidden
uclient_backend_set_eof(struct uclient
*cl
)
410 if (cl
->eof
|| cl
->error_code
)
413 uloop_timeout_cancel(&cl
->connection_timeout
);
415 uclient_backend_change_state(cl
);
418 void __hidden
uclient_backend_reset_state(struct uclient
*cl
)
420 cl
->data_eof
= false;
423 uloop_timeout_cancel(&cl
->timeout
);
426 const char * uclient_strerror(unsigned err
)
429 case UCLIENT_ERROR_UNKNOWN
:
430 return "unknown error";
431 case UCLIENT_ERROR_CONNECT
:
432 return "connect failed";
433 case UCLIENT_ERROR_TIMEDOUT
:
435 case UCLIENT_ERROR_SSL_INVALID_CERT
:
436 return "ssl invalid cert";
437 case UCLIENT_ERROR_SSL_CN_MISMATCH
:
438 return "ssl cn mismatch";
439 case UCLIENT_ERROR_MISSING_SSL_CONTEXT
:
440 return "missing ssl context";
442 return "invalid error code";