• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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