uclient: fix http regression
[project/uclient.git] / 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 }