1 /* MIT License
2 *
3 * Copyright (c) 2024 Brad house
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * SPDX-License-Identifier: MIT
25 */
26
27
28 #include "ares_private.h"
29 #include "ares_uri.h"
30 #ifdef HAVE_STDINT_H
31 # include <stdint.h>
32 #endif
33
34 struct ares_uri {
35 char scheme[16];
36 char *username;
37 char *password;
38 unsigned short port;
39 char host[256];
40 char *path;
41 ares_htable_dict_t *query;
42 char *fragment;
43 };
44
45 /* RFC3986 character set notes:
46 * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
47 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
48 * / "*" / "+" / "," / ";" / "="
49 * reserved = gen-delims / sub-delims
50 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
51 * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
52 * authority = [ userinfo "@" ] host [ ":" port ]
53 * userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
54 * NOTE: Use of the format "user:password" in the userinfo field is
55 * deprecated. Applications should not render as clear text any data
56 * after the first colon (":") character found within a userinfo
57 * subcomponent unless the data after the colon is the empty string
58 * (indicating no password).
59 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
60 * query = *( pchar / "/" / "?" )
61 * fragment = *( pchar / "/" / "?" )
62 *
63 * NOTE: Due to ambiguity, "+" in a query must be percent-encoded, as old
64 * URLs used that for spaces.
65 */
66
67
ares_uri_chis_subdelim(char x)68 static ares_bool_t ares_uri_chis_subdelim(char x)
69 {
70 switch (x) {
71 case '!':
72 return ARES_TRUE;
73 case '$':
74 return ARES_TRUE;
75 case '&':
76 return ARES_TRUE;
77 case '\'':
78 return ARES_TRUE;
79 case '(':
80 return ARES_TRUE;
81 case ')':
82 return ARES_TRUE;
83 case '*':
84 return ARES_TRUE;
85 case '+':
86 return ARES_TRUE;
87 case ',':
88 return ARES_TRUE;
89 case ';':
90 return ARES_TRUE;
91 case '=':
92 return ARES_TRUE;
93 default:
94 break;
95 }
96 return ARES_FALSE;
97 }
98
99 /* These don't actually appear to be referenced in any logic */
100 #if 0
101 static ares_bool_t ares_uri_chis_gendelim(char x)
102 {
103 switch (x) {
104 case ':':
105 return ARES_TRUE;
106 case '/':
107 return ARES_TRUE;
108 case '?':
109 return ARES_TRUE;
110 case '#':
111 return ARES_TRUE;
112 case '[':
113 return ARES_TRUE;
114 case ']':
115 return ARES_TRUE;
116 case '@':
117 return ARES_TRUE;
118 default:
119 break;
120 }
121 return ARES_FALSE;
122 }
123
124
125 static ares_bool_t ares_uri_chis_reserved(char x)
126 {
127 return ares_uri_chis_gendelim(x) || ares_uri_chis_subdelim(x);
128 }
129 #endif
130
ares_uri_chis_unreserved(char x)131 static ares_bool_t ares_uri_chis_unreserved(char x)
132 {
133 switch (x) {
134 case '-':
135 return ARES_TRUE;
136 case '.':
137 return ARES_TRUE;
138 case '_':
139 return ARES_TRUE;
140 case '~':
141 return ARES_TRUE;
142 default:
143 break;
144 }
145 return ares_isalpha(x) || ares_isdigit(x);
146 }
147
ares_uri_chis_scheme(char x)148 static ares_bool_t ares_uri_chis_scheme(char x)
149 {
150 switch (x) {
151 case '+':
152 return ARES_TRUE;
153 case '-':
154 return ARES_TRUE;
155 case '.':
156 return ARES_TRUE;
157 default:
158 break;
159 }
160 return ares_isalpha(x) || ares_isdigit(x);
161 }
162
ares_uri_chis_authority(char x)163 static ares_bool_t ares_uri_chis_authority(char x)
164 {
165 /* This one here isn't well defined. We are going to include the valid
166 * characters of the subfields plus known delimiters */
167 return ares_uri_chis_unreserved(x) || ares_uri_chis_subdelim(x) || x == '%' ||
168 x == '[' || x == ']' || x == '@' || x == ':';
169 }
170
ares_uri_chis_userinfo(char x)171 static ares_bool_t ares_uri_chis_userinfo(char x)
172 {
173 /* NOTE: we don't include ':' here since we are using that as our
174 * username/password delimiter */
175 return ares_uri_chis_unreserved(x) || ares_uri_chis_subdelim(x);
176 }
177
ares_uri_chis_path(char x)178 static ares_bool_t ares_uri_chis_path(char x)
179 {
180 switch (x) {
181 case ':':
182 return ARES_TRUE;
183 case '@':
184 return ARES_TRUE;
185 /* '/' isn't in the spec as a path character since its technically a
186 * delimiter but we're not splitting on '/' so we accept it as valid */
187 case '/':
188 return ARES_TRUE;
189 default:
190 break;
191 }
192 return ares_uri_chis_unreserved(x) || ares_uri_chis_subdelim(x);
193 }
194
ares_uri_chis_path_enc(char x)195 static ares_bool_t ares_uri_chis_path_enc(char x)
196 {
197 return ares_uri_chis_path(x) || x == '%';
198 }
199
ares_uri_chis_query(char x)200 static ares_bool_t ares_uri_chis_query(char x)
201 {
202 switch (x) {
203 case '/':
204 return ARES_TRUE;
205 case '?':
206 return ARES_TRUE;
207 default:
208 break;
209 }
210
211 /* Exclude & and = used as delimiters, they're valid characters in the
212 * set, just not for the individual pieces */
213 return ares_uri_chis_path(x) && x != '&' && x != '=';
214 }
215
ares_uri_chis_query_enc(char x)216 static ares_bool_t ares_uri_chis_query_enc(char x)
217 {
218 return ares_uri_chis_query(x) || x == '%';
219 }
220
ares_uri_chis_fragment(char x)221 static ares_bool_t ares_uri_chis_fragment(char x)
222 {
223 switch (x) {
224 case '/':
225 return ARES_TRUE;
226 case '?':
227 return ARES_TRUE;
228 default:
229 break;
230 }
231 return ares_uri_chis_path(x);
232 }
233
ares_uri_chis_fragment_enc(char x)234 static ares_bool_t ares_uri_chis_fragment_enc(char x)
235 {
236 return ares_uri_chis_fragment(x) || x == '%';
237 }
238
ares_uri_create(void)239 ares_uri_t *ares_uri_create(void)
240 {
241 ares_uri_t *uri = ares_malloc_zero(sizeof(*uri));
242
243 if (uri == NULL) {
244 return NULL;
245 }
246
247 uri->query = ares_htable_dict_create();
248 if (uri->query == NULL) {
249 ares_free(uri);
250 return NULL;
251 }
252
253 return uri;
254 }
255
ares_uri_destroy(ares_uri_t * uri)256 void ares_uri_destroy(ares_uri_t *uri)
257 {
258 if (uri == NULL) {
259 return;
260 }
261
262 ares_free(uri->username);
263 ares_free(uri->password);
264 ares_free(uri->path);
265 ares_free(uri->fragment);
266 ares_htable_dict_destroy(uri->query);
267 ares_free(uri);
268 }
269
ares_uri_scheme_is_valid(const char * uri)270 static ares_bool_t ares_uri_scheme_is_valid(const char *uri)
271 {
272 size_t i;
273
274 if (ares_strlen(uri) == 0) {
275 return ARES_FALSE;
276 }
277
278 if (!ares_isalpha(*uri)) {
279 return ARES_FALSE;
280 }
281
282 for (i = 0; uri[i] != 0; i++) {
283 if (!ares_uri_chis_scheme(uri[i])) {
284 return ARES_FALSE;
285 }
286 }
287 return ARES_TRUE;
288 }
289
ares_uri_str_isvalid(const char * str,size_t max_len,ares_bool_t (* ischr)(char))290 static ares_bool_t ares_uri_str_isvalid(const char *str, size_t max_len,
291 ares_bool_t (*ischr)(char))
292 {
293 size_t i;
294
295 if (str == NULL) {
296 return ARES_FALSE;
297 }
298
299 for (i = 0; i != max_len && str[i] != 0; i++) {
300 if (!ischr(str[i])) {
301 return ARES_FALSE;
302 }
303 }
304 return ARES_TRUE;
305 }
306
ares_uri_set_scheme(ares_uri_t * uri,const char * scheme)307 ares_status_t ares_uri_set_scheme(ares_uri_t *uri, const char *scheme)
308 {
309 if (uri == NULL) {
310 return ARES_EFORMERR;
311 }
312
313 if (!ares_uri_scheme_is_valid(scheme)) {
314 return ARES_EBADSTR;
315 }
316
317 ares_strcpy(uri->scheme, scheme, sizeof(uri->scheme));
318 ares_str_lower(uri->scheme);
319
320 return ARES_SUCCESS;
321 }
322
ares_uri_get_scheme(const ares_uri_t * uri)323 const char *ares_uri_get_scheme(const ares_uri_t *uri)
324 {
325 if (uri == NULL) {
326 return NULL;
327 }
328
329 return uri->scheme;
330 }
331
ares_uri_set_username_own(ares_uri_t * uri,char * username)332 static ares_status_t ares_uri_set_username_own(ares_uri_t *uri, char *username)
333 {
334 if (uri == NULL) {
335 return ARES_EFORMERR;
336 }
337
338 if (username != NULL && (!ares_str_isprint(username, ares_strlen(username)) ||
339 ares_strlen(username) == 0)) {
340 return ARES_EBADSTR;
341 }
342
343
344 ares_free(uri->username);
345 uri->username = username;
346 return ARES_SUCCESS;
347 }
348
ares_uri_set_username(ares_uri_t * uri,const char * username)349 ares_status_t ares_uri_set_username(ares_uri_t *uri, const char *username)
350 {
351 ares_status_t status;
352 char *temp = NULL;
353
354 if (uri == NULL) {
355 return ARES_EFORMERR;
356 }
357
358 if (username != NULL) {
359 temp = ares_strdup(username);
360 if (temp == NULL) {
361 return ARES_ENOMEM;
362 }
363 }
364
365 status = ares_uri_set_username_own(uri, temp);
366 if (status != ARES_SUCCESS) {
367 ares_free(temp);
368 }
369
370 return status;
371 }
372
ares_uri_get_username(const ares_uri_t * uri)373 const char *ares_uri_get_username(const ares_uri_t *uri)
374 {
375 if (uri == NULL) {
376 return NULL;
377 }
378
379 return uri->username;
380 }
381
ares_uri_set_password_own(ares_uri_t * uri,char * password)382 static ares_status_t ares_uri_set_password_own(ares_uri_t *uri, char *password)
383 {
384 if (uri == NULL) {
385 return ARES_EFORMERR;
386 }
387
388 if (password != NULL && !ares_str_isprint(password, ares_strlen(password))) {
389 return ARES_EBADSTR;
390 }
391
392 ares_free(uri->password);
393 uri->password = password;
394 return ARES_SUCCESS;
395 }
396
ares_uri_set_password(ares_uri_t * uri,const char * password)397 ares_status_t ares_uri_set_password(ares_uri_t *uri, const char *password)
398 {
399 ares_status_t status;
400 char *temp = NULL;
401
402 if (uri == NULL) {
403 return ARES_EFORMERR;
404 }
405
406 if (password != NULL) {
407 temp = ares_strdup(password);
408 if (temp == NULL) {
409 return ARES_ENOMEM;
410 }
411 }
412
413 status = ares_uri_set_password_own(uri, temp);
414 if (status != ARES_SUCCESS) {
415 ares_free(temp);
416 }
417
418 return status;
419 }
420
ares_uri_get_password(const ares_uri_t * uri)421 const char *ares_uri_get_password(const ares_uri_t *uri)
422 {
423 if (uri == NULL) {
424 return NULL;
425 }
426
427 return uri->password;
428 }
429
ares_uri_set_host(ares_uri_t * uri,const char * host)430 ares_status_t ares_uri_set_host(ares_uri_t *uri, const char *host)
431 {
432 struct ares_addr addr;
433 size_t addrlen;
434 char hoststr[256];
435 char *ll_scope;
436
437 if (uri == NULL || ares_strlen(host) == 0 ||
438 ares_strlen(host) >= sizeof(hoststr)) {
439 return ARES_EFORMERR;
440 }
441
442 ares_strcpy(hoststr, host, sizeof(hoststr));
443
444 /* Look for '%' which could be a link-local scope for ipv6 addresses and
445 * parse it off */
446 ll_scope = strchr(hoststr, '%');
447 if (ll_scope != NULL) {
448 *ll_scope = 0;
449 ll_scope++;
450 if (!ares_str_isalnum(ll_scope)) {
451 return ARES_EBADNAME;
452 }
453 }
454
455 /* If its an IP address, normalize it */
456 memset(&addr, 0, sizeof(addr));
457 addr.family = AF_UNSPEC;
458 if (ares_dns_pton(hoststr, &addr, &addrlen) != NULL) {
459 char ipaddr[INET6_ADDRSTRLEN];
460 ares_inet_ntop(addr.family, &addr.addr, ipaddr, sizeof(ipaddr));
461 /* Only IPv6 is allowed to have a scope */
462 if (ll_scope != NULL && addr.family != AF_INET6) {
463 return ARES_EBADNAME;
464 }
465
466 if (ll_scope != NULL) {
467 snprintf(uri->host, sizeof(uri->host), "%s%%%s", ipaddr, ll_scope);
468 } else {
469 ares_strcpy(uri->host, ipaddr, sizeof(uri->host));
470 }
471 return ARES_SUCCESS;
472 }
473
474 /* If its a hostname, make sure its a valid charset */
475 if (!ares_is_hostname(host)) {
476 return ARES_EBADNAME;
477 }
478
479 ares_strcpy(uri->host, host, sizeof(uri->host));
480 return ARES_SUCCESS;
481 }
482
ares_uri_get_host(const ares_uri_t * uri)483 const char *ares_uri_get_host(const ares_uri_t *uri)
484 {
485 if (uri == NULL) {
486 return NULL;
487 }
488
489 return uri->host;
490 }
491
ares_uri_set_port(ares_uri_t * uri,unsigned short port)492 ares_status_t ares_uri_set_port(ares_uri_t *uri, unsigned short port)
493 {
494 if (uri == NULL) {
495 return ARES_EFORMERR;
496 }
497 uri->port = port;
498 return ARES_SUCCESS;
499 }
500
ares_uri_get_port(const ares_uri_t * uri)501 unsigned short ares_uri_get_port(const ares_uri_t *uri)
502 {
503 if (uri == NULL) {
504 return 0;
505 }
506 return uri->port;
507 }
508
509 /* URI spec says path normalization is a requirement */
ares_uri_path_normalize(const char * path)510 static char *ares_uri_path_normalize(const char *path)
511 {
512 ares_status_t status;
513 ares_array_t *arr = NULL;
514 ares_buf_t *outpath = NULL;
515 ares_buf_t *inpath = NULL;
516 ares_ssize_t i;
517 size_t j;
518 size_t len;
519
520 inpath =
521 ares_buf_create_const((const unsigned char *)path, ares_strlen(path));
522 if (inpath == NULL) {
523 status = ARES_ENOMEM;
524 goto done;
525 }
526
527 outpath = ares_buf_create();
528 if (outpath == NULL) {
529 status = ARES_ENOMEM;
530 goto done;
531 }
532
533 status = ares_buf_split_str_array(inpath, (const unsigned char *)"/", 1,
534 ARES_BUF_SPLIT_TRIM, 0, &arr);
535 if (status != ARES_SUCCESS) {
536 return NULL;
537 }
538
539 for (i = 0; i < (ares_ssize_t)ares_array_len(arr); i++) {
540 const char **strptr = ares_array_at(arr, (size_t)i);
541 const char *str = *strptr;
542
543 if (ares_streq(str, ".")) {
544 ares_array_remove_at(arr, (size_t)i);
545 i--;
546 } else if (ares_streq(str, "..")) {
547 if (i != 0) {
548 ares_array_remove_at(arr, (size_t)i - 1);
549 i--;
550 }
551 ares_array_remove_at(arr, (size_t)i);
552 i--;
553 }
554 }
555
556 status = ares_buf_append_byte(outpath, '/');
557 if (status != ARES_SUCCESS) {
558 goto done;
559 }
560
561 len = ares_array_len(arr);
562 for (j = 0; j < len; j++) {
563 const char **strptr = ares_array_at(arr, j);
564 const char *str = *strptr;
565 status = ares_buf_append_str(outpath, str);
566 if (status != ARES_SUCCESS) {
567 goto done;
568 }
569
570 /* Path separator, but on the last entry, we need to check if it was
571 * originally terminated or not because they have different meanings */
572 if (j != len - 1 || path[ares_strlen(path) - 1] == '/') {
573 status = ares_buf_append_byte(outpath, '/');
574 if (status != ARES_SUCCESS) {
575 goto done;
576 }
577 }
578 }
579
580 done:
581 ares_array_destroy(arr);
582 ares_buf_destroy(inpath);
583 if (status != ARES_SUCCESS) {
584 ares_buf_destroy(outpath);
585 return NULL;
586 }
587
588 return ares_buf_finish_str(outpath, NULL);
589 }
590
ares_uri_set_path(ares_uri_t * uri,const char * path)591 ares_status_t ares_uri_set_path(ares_uri_t *uri, const char *path)
592 {
593 char *temp = NULL;
594
595 if (uri == NULL) {
596 return ARES_EFORMERR;
597 }
598
599 if (path != NULL && !ares_str_isprint(path, ares_strlen(path))) {
600 return ARES_EBADSTR;
601 }
602
603 if (path != NULL) {
604 temp = ares_uri_path_normalize(path);
605 if (temp == NULL) {
606 return ARES_ENOMEM;
607 }
608 }
609
610 ares_free(uri->path);
611 uri->path = temp;
612
613 return ARES_SUCCESS;
614 }
615
ares_uri_get_path(const ares_uri_t * uri)616 const char *ares_uri_get_path(const ares_uri_t *uri)
617 {
618 if (uri == NULL) {
619 return NULL;
620 }
621
622 return uri->path;
623 }
624
ares_uri_set_query_key(ares_uri_t * uri,const char * key,const char * val)625 ares_status_t ares_uri_set_query_key(ares_uri_t *uri, const char *key,
626 const char *val)
627 {
628 if (uri == NULL || key == NULL || *key == 0) {
629 return ARES_EFORMERR;
630 }
631
632 if (!ares_str_isprint(key, ares_strlen(key)) ||
633 (val != NULL && !ares_str_isprint(val, ares_strlen(val)))) {
634 return ARES_EBADSTR;
635 }
636
637 if (!ares_htable_dict_insert(uri->query, key, val)) {
638 return ARES_ENOMEM;
639 }
640 return ARES_SUCCESS;
641 }
642
ares_uri_del_query_key(ares_uri_t * uri,const char * key)643 ares_status_t ares_uri_del_query_key(ares_uri_t *uri, const char *key)
644 {
645 if (uri == NULL || key == NULL || *key == 0 ||
646 !ares_str_isprint(key, ares_strlen(key))) {
647 return ARES_EFORMERR;
648 }
649
650 if (!ares_htable_dict_remove(uri->query, key)) {
651 return ARES_ENOTFOUND;
652 }
653
654 return ARES_SUCCESS;
655 }
656
ares_uri_get_query_key(const ares_uri_t * uri,const char * key)657 const char *ares_uri_get_query_key(const ares_uri_t *uri, const char *key)
658 {
659 if (uri == NULL || key == NULL || *key == 0 ||
660 !ares_str_isprint(key, ares_strlen(key))) {
661 return NULL;
662 }
663
664 return ares_htable_dict_get_direct(uri->query, key);
665 }
666
ares_uri_get_query_keys(const ares_uri_t * uri,size_t * num)667 char **ares_uri_get_query_keys(const ares_uri_t *uri, size_t *num)
668 {
669 if (uri == NULL || num == NULL) {
670 return NULL;
671 }
672
673 return ares_htable_dict_keys(uri->query, num);
674 }
675
ares_uri_set_fragment_own(ares_uri_t * uri,char * fragment)676 static ares_status_t ares_uri_set_fragment_own(ares_uri_t *uri, char *fragment)
677 {
678 if (uri == NULL) {
679 return ARES_EFORMERR;
680 }
681
682 if (fragment != NULL && !ares_str_isprint(fragment, ares_strlen(fragment))) {
683 return ARES_EBADSTR;
684 }
685
686 ares_free(uri->fragment);
687 uri->fragment = fragment;
688 return ARES_SUCCESS;
689 }
690
ares_uri_set_fragment(ares_uri_t * uri,const char * fragment)691 ares_status_t ares_uri_set_fragment(ares_uri_t *uri, const char *fragment)
692 {
693 ares_status_t status;
694 char *temp = NULL;
695
696 if (uri == NULL) {
697 return ARES_EFORMERR;
698 }
699
700 if (fragment != NULL) {
701 temp = ares_strdup(fragment);
702 if (temp == NULL) {
703 return ARES_ENOMEM;
704 }
705 }
706
707 status = ares_uri_set_fragment_own(uri, temp);
708 if (status != ARES_SUCCESS) {
709 ares_free(temp);
710 }
711
712 return status;
713 }
714
ares_uri_get_fragment(const ares_uri_t * uri)715 const char *ares_uri_get_fragment(const ares_uri_t *uri)
716 {
717 if (uri == NULL) {
718 return NULL;
719 }
720 return uri->fragment;
721 }
722
ares_uri_encode_buf(ares_buf_t * buf,const char * str,ares_bool_t (* ischr)(char))723 static ares_status_t ares_uri_encode_buf(ares_buf_t *buf, const char *str,
724 ares_bool_t (*ischr)(char))
725 {
726 size_t i;
727
728 if (buf == NULL || str == NULL) {
729 return ARES_EFORMERR;
730 }
731
732 for (i = 0; str[i] != 0; i++) {
733 if (ischr(str[i])) {
734 if (ares_buf_append_byte(buf, (unsigned char)str[i]) != ARES_SUCCESS) {
735 return ARES_ENOMEM;
736 }
737 } else {
738 if (ares_buf_append_byte(buf, '%') != ARES_SUCCESS) {
739 return ARES_ENOMEM;
740 }
741 if (ares_buf_append_num_hex(buf, (size_t)str[i], 2) != ARES_SUCCESS) {
742 return ARES_ENOMEM;
743 }
744 }
745 }
746 return ARES_SUCCESS;
747 }
748
ares_uri_write_scheme(const ares_uri_t * uri,ares_buf_t * buf)749 static ares_status_t ares_uri_write_scheme(const ares_uri_t *uri,
750 ares_buf_t *buf)
751 {
752 ares_status_t status;
753
754 status = ares_buf_append_str(buf, uri->scheme);
755 if (status != ARES_SUCCESS) {
756 return status;
757 }
758
759 status = ares_buf_append_str(buf, "://");
760
761 return status;
762 }
763
ares_uri_write_authority(const ares_uri_t * uri,ares_buf_t * buf)764 static ares_status_t ares_uri_write_authority(const ares_uri_t *uri,
765 ares_buf_t *buf)
766 {
767 ares_status_t status;
768 ares_bool_t is_ipv6 = ARES_FALSE;
769
770 if (ares_strlen(uri->username)) {
771 status = ares_uri_encode_buf(buf, uri->username, ares_uri_chis_userinfo);
772 if (status != ARES_SUCCESS) {
773 return status;
774 }
775 }
776
777 if (ares_strlen(uri->password)) {
778 status = ares_buf_append_byte(buf, ':');
779 if (status != ARES_SUCCESS) {
780 return status;
781 }
782
783 status = ares_uri_encode_buf(buf, uri->password, ares_uri_chis_userinfo);
784 if (status != ARES_SUCCESS) {
785 return status;
786 }
787 }
788
789 if (ares_strlen(uri->username) || ares_strlen(uri->password)) {
790 status = ares_buf_append_byte(buf, '@');
791 if (status != ARES_SUCCESS) {
792 return status;
793 }
794 }
795
796 /* We need to write ipv6 addresses with [ ] */
797 if (strchr(uri->host, '%') != NULL) {
798 /* If we have a % in the name, it must be ipv6 link local scope, so we
799 * don't need to check anything else */
800 is_ipv6 = ARES_TRUE;
801 } else {
802 /* Parse the host to see if it is an ipv6 address */
803 struct ares_addr addr;
804 size_t addrlen;
805 memset(&addr, 0, sizeof(addr));
806 addr.family = AF_INET6;
807 if (ares_dns_pton(uri->host, &addr, &addrlen) != NULL) {
808 is_ipv6 = ARES_TRUE;
809 }
810 }
811
812 if (is_ipv6) {
813 status = ares_buf_append_byte(buf, '[');
814 if (status != ARES_SUCCESS) {
815 return status;
816 }
817 }
818
819 status = ares_buf_append_str(buf, uri->host);
820 if (status != ARES_SUCCESS) {
821 return status;
822 }
823
824 if (is_ipv6) {
825 status = ares_buf_append_byte(buf, ']');
826 if (status != ARES_SUCCESS) {
827 return status;
828 }
829 }
830
831 if (uri->port > 0) {
832 status = ares_buf_append_byte(buf, ':');
833 if (status != ARES_SUCCESS) {
834 return status;
835 }
836 status = ares_buf_append_num_dec(buf, uri->port, 0);
837 if (status != ARES_SUCCESS) {
838 return status;
839 }
840 }
841
842 return status;
843 }
844
ares_uri_write_path(const ares_uri_t * uri,ares_buf_t * buf)845 static ares_status_t ares_uri_write_path(const ares_uri_t *uri, ares_buf_t *buf)
846 {
847 ares_status_t status;
848
849 if (ares_strlen(uri->path) == 0) {
850 return ARES_SUCCESS;
851 }
852
853 if (*uri->path != '/') {
854 status = ares_buf_append_byte(buf, '/');
855 if (status != ARES_SUCCESS) {
856 return status;
857 }
858 }
859
860 status = ares_uri_encode_buf(buf, uri->path, ares_uri_chis_path);
861 if (status != ARES_SUCCESS) {
862 return status;
863 }
864
865 return ARES_SUCCESS;
866 }
867
ares_uri_write_query(const ares_uri_t * uri,ares_buf_t * buf)868 static ares_status_t ares_uri_write_query(const ares_uri_t *uri,
869 ares_buf_t *buf)
870 {
871 ares_status_t status;
872 char **keys;
873 size_t num_keys = 0;
874 size_t i;
875
876 if (ares_htable_dict_num_keys(uri->query) == 0) {
877 return ARES_SUCCESS;
878 }
879
880 keys = ares_uri_get_query_keys(uri, &num_keys);
881 if (keys == NULL || num_keys == 0) {
882 return ARES_ENOMEM;
883 }
884
885 status = ares_buf_append_byte(buf, '?');
886 if (status != ARES_SUCCESS) {
887 goto done;
888 }
889
890 for (i = 0; i < num_keys; i++) {
891 const char *val;
892
893 if (i != 0) {
894 status = ares_buf_append_byte(buf, '&');
895 if (status != ARES_SUCCESS) {
896 goto done;
897 }
898 }
899
900 status = ares_uri_encode_buf(buf, keys[i], ares_uri_chis_query);
901 if (status != ARES_SUCCESS) {
902 goto done;
903 }
904
905 val = ares_uri_get_query_key(uri, keys[i]);
906 if (val != NULL) {
907 status = ares_buf_append_byte(buf, '=');
908 if (status != ARES_SUCCESS) {
909 goto done;
910 }
911
912 status = ares_uri_encode_buf(buf, val, ares_uri_chis_query);
913 if (status != ARES_SUCCESS) {
914 goto done;
915 }
916 }
917 }
918
919 done:
920 ares_free_array(keys, num_keys, ares_free);
921 return status;
922 }
923
ares_uri_write_fragment(const ares_uri_t * uri,ares_buf_t * buf)924 static ares_status_t ares_uri_write_fragment(const ares_uri_t *uri,
925 ares_buf_t *buf)
926 {
927 ares_status_t status;
928
929 if (!ares_strlen(uri->fragment)) {
930 return ARES_SUCCESS;
931 }
932
933 status = ares_buf_append_byte(buf, '#');
934 if (status != ARES_SUCCESS) {
935 return status;
936 }
937
938 status = ares_uri_encode_buf(buf, uri->fragment, ares_uri_chis_fragment);
939 if (status != ARES_SUCCESS) {
940 return status;
941 }
942
943 return ARES_SUCCESS;
944 }
945
ares_uri_write_buf(const ares_uri_t * uri,ares_buf_t * buf)946 ares_status_t ares_uri_write_buf(const ares_uri_t *uri, ares_buf_t *buf)
947 {
948 ares_status_t status;
949 size_t orig_len;
950
951 if (uri == NULL || buf == NULL) {
952 return ARES_EFORMERR;
953 }
954
955 if (ares_strlen(uri->scheme) == 0 || ares_strlen(uri->host) == 0) {
956 return ARES_ENODATA;
957 }
958
959 orig_len = ares_buf_len(buf);
960
961 status = ares_uri_write_scheme(uri, buf);
962 if (status != ARES_SUCCESS) {
963 goto done;
964 }
965
966 status = ares_uri_write_authority(uri, buf);
967 if (status != ARES_SUCCESS) {
968 goto done;
969 }
970
971 status = ares_uri_write_path(uri, buf);
972 if (status != ARES_SUCCESS) {
973 goto done;
974 }
975
976 status = ares_uri_write_query(uri, buf);
977 if (status != ARES_SUCCESS) {
978 goto done;
979 }
980
981 status = ares_uri_write_fragment(uri, buf);
982 if (status != ARES_SUCCESS) {
983 goto done;
984 }
985
986 done:
987 if (status != ARES_SUCCESS) {
988 ares_buf_set_length(buf, orig_len);
989 }
990 return status;
991 }
992
ares_uri_write(char ** out,const ares_uri_t * uri)993 ares_status_t ares_uri_write(char **out, const ares_uri_t *uri)
994 {
995 ares_buf_t *buf;
996 ares_status_t status;
997
998 if (out == NULL || uri == NULL) {
999 return ARES_EFORMERR;
1000 }
1001
1002 *out = NULL;
1003
1004 buf = ares_buf_create();
1005 if (buf == NULL) {
1006 return ARES_ENOMEM;
1007 }
1008
1009 status = ares_uri_write_buf(uri, buf);
1010 if (status != ARES_SUCCESS) {
1011 ares_buf_destroy(buf);
1012 return status;
1013 }
1014
1015 *out = ares_buf_finish_str(buf, NULL);
1016 return ARES_SUCCESS;
1017 }
1018
1019 #define xdigit_val(x) \
1020 ((x >= '0' && x <= '9') \
1021 ? (x - '0') \
1022 : ((x >= 'A' && x <= 'F') ? (x - 'A' + 10) : (x - 'a' + 10)))
1023
ares_uri_decode_inplace(char * str,ares_bool_t is_query,ares_bool_t must_be_printable,size_t * out_len)1024 static ares_status_t ares_uri_decode_inplace(char *str, ares_bool_t is_query,
1025 ares_bool_t must_be_printable,
1026 size_t *out_len)
1027 {
1028 size_t i;
1029 size_t len = 0;
1030
1031 for (i = 0; str[i] != 0; i++) {
1032 if (is_query && str[i] == '+') {
1033 str[len++] = ' ';
1034 continue;
1035 }
1036
1037 if (str[i] != '%') {
1038 str[len++] = str[i];
1039 continue;
1040 }
1041
1042 if (!ares_isxdigit(str[i + 1]) || !ares_isxdigit(str[i + 2])) {
1043 return ARES_EBADSTR;
1044 }
1045
1046 str[len] = (char)(xdigit_val(str[i + 1]) << 4 | xdigit_val(str[i + 2]));
1047
1048 if (must_be_printable && !ares_isprint(str[len])) {
1049 return ARES_EBADSTR;
1050 }
1051
1052 len++;
1053
1054 i += 2;
1055 }
1056
1057 str[len] = 0;
1058
1059 *out_len = len;
1060 return ARES_SUCCESS;
1061 }
1062
ares_uri_parse_scheme(ares_uri_t * uri,ares_buf_t * buf)1063 static ares_status_t ares_uri_parse_scheme(ares_uri_t *uri, ares_buf_t *buf)
1064 {
1065 ares_status_t status;
1066 size_t bytes;
1067 char scheme[sizeof(uri->scheme)];
1068
1069 ares_buf_tag(buf);
1070
1071 bytes =
1072 ares_buf_consume_until_seq(buf, (const unsigned char *)"://", 3, ARES_TRUE);
1073 if (bytes == SIZE_MAX || bytes > sizeof(uri->scheme)) {
1074 return ARES_EBADSTR;
1075 }
1076
1077 status = ares_buf_tag_fetch_string(buf, scheme, sizeof(scheme));
1078 if (status != ARES_SUCCESS) {
1079 return status;
1080 }
1081
1082 status = ares_uri_set_scheme(uri, scheme);
1083 if (status != ARES_SUCCESS) {
1084 return status;
1085 }
1086
1087 /* Consume :// */
1088 ares_buf_consume(buf, 3);
1089
1090 return ARES_SUCCESS;
1091 }
1092
ares_uri_parse_userinfo(ares_uri_t * uri,ares_buf_t * buf)1093 static ares_status_t ares_uri_parse_userinfo(ares_uri_t *uri, ares_buf_t *buf)
1094 {
1095 size_t userinfo_len;
1096 size_t username_len;
1097 ares_bool_t has_password = ARES_FALSE;
1098 char *temp = NULL;
1099 ares_status_t status;
1100 size_t len;
1101
1102 ares_buf_tag(buf);
1103
1104 /* Search for @, if its not found, return */
1105 userinfo_len = ares_buf_consume_until_charset(buf, (const unsigned char *)"@",
1106 1, ARES_TRUE);
1107
1108 if (userinfo_len == SIZE_MAX) {
1109 return ARES_SUCCESS;
1110 }
1111
1112 /* Rollback since now we know there really is userinfo */
1113 ares_buf_tag_rollback(buf);
1114
1115 /* Search for ':', if it isn't found or its past the '@' then we only have
1116 * a username and no password */
1117 ares_buf_tag(buf);
1118 username_len = ares_buf_consume_until_charset(buf, (const unsigned char *)":",
1119 1, ARES_TRUE);
1120 if (username_len < userinfo_len) {
1121 has_password = ARES_TRUE;
1122 status = ares_buf_tag_fetch_strdup(buf, &temp);
1123 if (status != ARES_SUCCESS) {
1124 goto done;
1125 }
1126
1127 status = ares_uri_decode_inplace(temp, ARES_FALSE, ARES_TRUE, &len);
1128 if (status != ARES_SUCCESS) {
1129 goto done;
1130 }
1131
1132 status = ares_uri_set_username_own(uri, temp);
1133 if (status != ARES_SUCCESS) {
1134 goto done;
1135 }
1136 temp = NULL;
1137
1138 /* Consume : */
1139 ares_buf_consume(buf, 1);
1140 }
1141
1142 ares_buf_tag(buf);
1143 ares_buf_consume_until_charset(buf, (const unsigned char *)"@", 1, ARES_TRUE);
1144 status = ares_buf_tag_fetch_strdup(buf, &temp);
1145 if (status != ARES_SUCCESS) {
1146 goto done;
1147 }
1148
1149 status = ares_uri_decode_inplace(temp, ARES_FALSE, ARES_TRUE, &len);
1150 if (status != ARES_SUCCESS) {
1151 goto done;
1152 }
1153
1154 if (has_password) {
1155 status = ares_uri_set_password_own(uri, temp);
1156 } else {
1157 status = ares_uri_set_username_own(uri, temp);
1158 }
1159 if (status != ARES_SUCCESS) {
1160 goto done;
1161 }
1162 temp = NULL;
1163
1164 /* Consume @ */
1165 ares_buf_consume(buf, 1);
1166
1167 done:
1168 ares_free(temp);
1169 return status;
1170 }
1171
ares_uri_parse_hostport(ares_uri_t * uri,ares_buf_t * buf)1172 static ares_status_t ares_uri_parse_hostport(ares_uri_t *uri, ares_buf_t *buf)
1173 {
1174 unsigned char b;
1175 char host[256];
1176 char port[6];
1177 size_t len;
1178 ares_status_t status;
1179
1180 status = ares_buf_peek_byte(buf, &b);
1181 if (status != ARES_SUCCESS) {
1182 return status;
1183 }
1184
1185 /* Bracketed syntax for ipv6 addresses */
1186 if (b == '[') {
1187 ares_buf_consume(buf, 1);
1188 ares_buf_tag(buf);
1189 len = ares_buf_consume_until_charset(buf, (const unsigned char *)"]", 1,
1190 ARES_TRUE);
1191 if (len == SIZE_MAX) {
1192 return ARES_EBADSTR;
1193 }
1194
1195 status = ares_buf_tag_fetch_string(buf, host, sizeof(host));
1196 if (status != ARES_SUCCESS) {
1197 return status;
1198 }
1199 /* Consume ']' */
1200 ares_buf_consume(buf, 1);
1201 } else {
1202 /* Either ipv4 or hostname */
1203 ares_buf_tag(buf);
1204 ares_buf_consume_until_charset(buf, (const unsigned char *)":", 1,
1205 ARES_FALSE);
1206
1207 status = ares_buf_tag_fetch_string(buf, host, sizeof(host));
1208 if (status != ARES_SUCCESS) {
1209 return status;
1210 }
1211 }
1212
1213 status = ares_uri_set_host(uri, host);
1214 if (status != ARES_SUCCESS) {
1215 return status;
1216 }
1217
1218 /* No port if nothing left to consume */
1219 if (!ares_buf_len(buf)) {
1220 return status;
1221 }
1222
1223 status = ares_buf_peek_byte(buf, &b);
1224 if (status != ARES_SUCCESS) {
1225 return status;
1226 }
1227
1228 /* Only valid extra character at this point is ':' */
1229 if (b != ':') {
1230 return ARES_EBADSTR;
1231 }
1232 ares_buf_consume(buf, 1);
1233
1234 len = ares_buf_len(buf);
1235 if (len == 0 || len > sizeof(port) - 1) {
1236 return ARES_EBADSTR;
1237 }
1238
1239 status = ares_buf_fetch_bytes(buf, (unsigned char *)port, len);
1240 if (status != ARES_SUCCESS) {
1241 return status;
1242 }
1243 port[len] = 0;
1244
1245 if (!ares_str_isnum(port)) {
1246 return ARES_EBADSTR;
1247 }
1248
1249 status = ares_uri_set_port(uri, (unsigned short)atoi(port));
1250 if (status != ARES_SUCCESS) {
1251 return status;
1252 }
1253
1254 return ARES_SUCCESS;
1255 }
1256
ares_uri_parse_authority(ares_uri_t * uri,ares_buf_t * buf)1257 static ares_status_t ares_uri_parse_authority(ares_uri_t *uri, ares_buf_t *buf)
1258 {
1259 ares_status_t status;
1260 size_t bytes;
1261 ares_buf_t *auth = NULL;
1262 const unsigned char *ptr;
1263 size_t ptr_len;
1264
1265 ares_buf_tag(buf);
1266
1267 bytes = ares_buf_consume_until_charset(buf, (const unsigned char *)"/?#", 3,
1268 ARES_FALSE);
1269 if (bytes == 0) {
1270 return ARES_EBADSTR;
1271 }
1272
1273 status = ares_buf_tag_fetch_constbuf(buf, &auth);
1274 if (status != ARES_SUCCESS) {
1275 goto done;
1276 }
1277
1278 ptr = ares_buf_peek(auth, &ptr_len);
1279 if (!ares_uri_str_isvalid((const char *)ptr, ptr_len,
1280 ares_uri_chis_authority)) {
1281 status = ARES_EBADSTR;
1282 goto done;
1283 }
1284
1285 status = ares_uri_parse_userinfo(uri, auth);
1286 if (status != ARES_SUCCESS) {
1287 goto done;
1288 }
1289
1290 status = ares_uri_parse_hostport(uri, auth);
1291 if (status != ARES_SUCCESS) {
1292 goto done;
1293 }
1294
1295 /* NOTE: the /, ?, or # is still in the buffer at this point so it can
1296 * be used to determine what parser should be called next */
1297
1298 done:
1299 ares_buf_destroy(auth);
1300 return status;
1301 }
1302
ares_uri_parse_path(ares_uri_t * uri,ares_buf_t * buf)1303 static ares_status_t ares_uri_parse_path(ares_uri_t *uri, ares_buf_t *buf)
1304 {
1305 unsigned char b;
1306 char *path = NULL;
1307 ares_status_t status;
1308 size_t len;
1309
1310 if (ares_buf_len(buf) == 0) {
1311 return ARES_SUCCESS;
1312 }
1313
1314 status = ares_buf_peek_byte(buf, &b);
1315 if (status != ARES_SUCCESS) {
1316 return status;
1317 }
1318
1319 /* Not a path, must be one of the others */
1320 if (b != '/') {
1321 return ARES_SUCCESS;
1322 }
1323
1324 ares_buf_tag(buf);
1325 ares_buf_consume_until_charset(buf, (const unsigned char *)"?#", 2,
1326 ARES_FALSE);
1327 status = ares_buf_tag_fetch_strdup(buf, &path);
1328 if (status != ARES_SUCCESS) {
1329 goto done;
1330 }
1331
1332 if (!ares_uri_str_isvalid(path, SIZE_MAX, ares_uri_chis_path_enc)) {
1333 status = ARES_EBADSTR;
1334 goto done;
1335 }
1336
1337 status = ares_uri_decode_inplace(path, ARES_FALSE, ARES_TRUE, &len);
1338 if (status != ARES_SUCCESS) {
1339 goto done;
1340 }
1341
1342 status = ares_uri_set_path(uri, path);
1343 if (status != ARES_SUCCESS) {
1344 goto done;
1345 }
1346
1347 done:
1348 ares_free(path);
1349 return status;
1350 }
1351
ares_uri_parse_query_buf(ares_uri_t * uri,ares_buf_t * buf)1352 static ares_status_t ares_uri_parse_query_buf(ares_uri_t *uri, ares_buf_t *buf)
1353 {
1354 ares_status_t status = ARES_SUCCESS;
1355 char *key = NULL;
1356 char *val = NULL;
1357
1358 while (ares_buf_len(buf) > 0) {
1359 unsigned char b = 0;
1360 size_t len;
1361
1362 ares_buf_tag(buf);
1363
1364 /* Its valid to have only a key with no value, so we search for both
1365 * delims */
1366 len = ares_buf_consume_until_charset(buf, (const unsigned char *)"&=", 2,
1367 ARES_FALSE);
1368 if (len == 0) {
1369 /* If we're here, we have a zero length key which is invalid */
1370 status = ARES_EBADSTR;
1371 goto done;
1372 }
1373
1374 if (ares_buf_len(buf) > 0) {
1375 /* Determine if we stopped on & or = */
1376 status = ares_buf_peek_byte(buf, &b);
1377 if (status != ARES_SUCCESS) {
1378 goto done;
1379 }
1380 }
1381
1382 status = ares_buf_tag_fetch_strdup(buf, &key);
1383 if (status != ARES_SUCCESS) {
1384 goto done;
1385 }
1386
1387 if (!ares_uri_str_isvalid(key, SIZE_MAX, ares_uri_chis_query_enc)) {
1388 status = ARES_EBADSTR;
1389 goto done;
1390 }
1391
1392 status = ares_uri_decode_inplace(key, ARES_TRUE, ARES_TRUE, &len);
1393 if (status != ARES_SUCCESS) {
1394 goto done;
1395 }
1396
1397 /* Fetch Value */
1398 if (b == '=') {
1399 /* Skip delimiter */
1400 ares_buf_consume(buf, 1);
1401 ares_buf_tag(buf);
1402 len = ares_buf_consume_until_charset(buf, (const unsigned char *)"&", 1,
1403 ARES_FALSE);
1404 if (len > 0) {
1405 status = ares_buf_tag_fetch_strdup(buf, &val);
1406 if (status != ARES_SUCCESS) {
1407 goto done;
1408 }
1409
1410 if (!ares_uri_str_isvalid(val, SIZE_MAX, ares_uri_chis_query_enc)) {
1411 status = ARES_EBADSTR;
1412 goto done;
1413 }
1414
1415 status = ares_uri_decode_inplace(val, ARES_TRUE, ARES_TRUE, &len);
1416 if (status != ARES_SUCCESS) {
1417 goto done;
1418 }
1419 }
1420 }
1421
1422 if (b != 0) {
1423 /* Consume '&' */
1424 ares_buf_consume(buf, 1);
1425 }
1426
1427 status = ares_uri_set_query_key(uri, key, val);
1428 if (status != ARES_SUCCESS) {
1429 goto done;
1430 }
1431
1432 ares_free(key);
1433 key = NULL;
1434 ares_free(val);
1435 val = NULL;
1436 }
1437
1438 done:
1439 ares_free(key);
1440 ares_free(val);
1441 return status;
1442 }
1443
ares_uri_parse_query(ares_uri_t * uri,ares_buf_t * buf)1444 static ares_status_t ares_uri_parse_query(ares_uri_t *uri, ares_buf_t *buf)
1445 {
1446 unsigned char b;
1447 ares_status_t status;
1448 ares_buf_t *query = NULL;
1449 size_t len;
1450
1451 if (ares_buf_len(buf) == 0) {
1452 return ARES_SUCCESS;
1453 }
1454
1455 status = ares_buf_peek_byte(buf, &b);
1456 if (status != ARES_SUCCESS) {
1457 return status;
1458 }
1459
1460 /* Not a query, must be one of the others */
1461 if (b != '?') {
1462 return ARES_SUCCESS;
1463 }
1464
1465 /* Only possible terminator is fragment indicator of '#' */
1466 ares_buf_consume(buf, 1);
1467 ares_buf_tag(buf);
1468 len = ares_buf_consume_until_charset(buf, (const unsigned char *)"#", 1,
1469 ARES_FALSE);
1470 if (len == 0) {
1471 /* No data, return */
1472 return ARES_SUCCESS;
1473 }
1474
1475 status = ares_buf_tag_fetch_constbuf(buf, &query);
1476 if (status != ARES_SUCCESS) {
1477 return status;
1478 }
1479
1480 status = ares_uri_parse_query_buf(uri, query);
1481 ares_buf_destroy(query);
1482
1483 return status;
1484 }
1485
ares_uri_parse_fragment(ares_uri_t * uri,ares_buf_t * buf)1486 static ares_status_t ares_uri_parse_fragment(ares_uri_t *uri, ares_buf_t *buf)
1487 {
1488 unsigned char b;
1489 char *fragment = NULL;
1490 ares_status_t status;
1491 size_t len;
1492
1493 if (ares_buf_len(buf) == 0) {
1494 return ARES_SUCCESS;
1495 }
1496
1497 status = ares_buf_peek_byte(buf, &b);
1498 if (status != ARES_SUCCESS) {
1499 return status;
1500 }
1501
1502 /* Not a fragment, must be one of the others */
1503 if (b != '#') {
1504 return ARES_SUCCESS;
1505 }
1506
1507 ares_buf_consume(buf, 1);
1508
1509 if (ares_buf_len(buf) == 0) {
1510 return ARES_SUCCESS;
1511 }
1512
1513 /* Rest of the buffer is the fragment */
1514 status = ares_buf_fetch_str_dup(buf, ares_buf_len(buf), &fragment);
1515 if (status != ARES_SUCCESS) {
1516 goto done;
1517 }
1518
1519 if (!ares_uri_str_isvalid(fragment, SIZE_MAX, ares_uri_chis_fragment_enc)) {
1520 status = ARES_EBADSTR;
1521 goto done;
1522 }
1523
1524 status = ares_uri_decode_inplace(fragment, ARES_FALSE, ARES_TRUE, &len);
1525 if (status != ARES_SUCCESS) {
1526 goto done;
1527 }
1528
1529 status = ares_uri_set_fragment_own(uri, fragment);
1530 if (status != ARES_SUCCESS) {
1531 goto done;
1532 }
1533 fragment = NULL;
1534
1535 done:
1536 ares_free(fragment);
1537 return status;
1538 }
1539
ares_uri_parse_buf(ares_uri_t ** out,ares_buf_t * buf)1540 ares_status_t ares_uri_parse_buf(ares_uri_t **out, ares_buf_t *buf)
1541 {
1542 ares_status_t status;
1543 ares_uri_t *uri = NULL;
1544 size_t orig_pos;
1545
1546 if (out == NULL || buf == NULL) {
1547 return ARES_EFORMERR;
1548 }
1549
1550 *out = NULL;
1551
1552 orig_pos = ares_buf_get_position(buf);
1553
1554 uri = ares_uri_create();
1555 if (uri == NULL) {
1556 status = ARES_ENOMEM;
1557 goto done;
1558 }
1559
1560 status = ares_uri_parse_scheme(uri, buf);
1561 if (status != ARES_SUCCESS) {
1562 goto done;
1563 }
1564
1565 status = ares_uri_parse_authority(uri, buf);
1566 if (status != ARES_SUCCESS) {
1567 goto done;
1568 }
1569
1570 status = ares_uri_parse_path(uri, buf);
1571 if (status != ARES_SUCCESS) {
1572 goto done;
1573 }
1574
1575 status = ares_uri_parse_query(uri, buf);
1576 if (status != ARES_SUCCESS) {
1577 goto done;
1578 }
1579
1580 status = ares_uri_parse_fragment(uri, buf);
1581 if (status != ARES_SUCCESS) {
1582 goto done;
1583 }
1584
1585 done:
1586 if (status != ARES_SUCCESS) {
1587 ares_buf_set_position(buf, orig_pos);
1588 ares_uri_destroy(uri);
1589 } else {
1590 *out = uri;
1591 }
1592 return status;
1593 }
1594
ares_uri_parse(ares_uri_t ** out,const char * str)1595 ares_status_t ares_uri_parse(ares_uri_t **out, const char *str)
1596 {
1597 ares_status_t status;
1598 ares_buf_t *buf = NULL;
1599
1600 if (out == NULL || str == NULL) {
1601 return ARES_EFORMERR;
1602 }
1603
1604 *out = NULL;
1605
1606 buf = ares_buf_create();
1607 if (buf == NULL) {
1608 status = ARES_ENOMEM;
1609 goto done;
1610 }
1611
1612 status = ares_buf_append_str(buf, str);
1613 if (status != ARES_SUCCESS) {
1614 goto done;
1615 }
1616
1617 status = ares_uri_parse_buf(out, buf);
1618 if (status != ARES_SUCCESS) {
1619 goto done;
1620 }
1621
1622 done:
1623 ares_buf_destroy(buf);
1624
1625 return status;
1626 }
1627