#define _POSIX_C_SOURCE 200809L #include #include #include /*!max:re2c*/ struct input { /*!stags:re2c format = "const char *@@;\n"; */ }; static void init_input(struct input *in) { /*!stags:re2c format = "in->@@ = 0;\n"; */ } struct tls_uri { char *scheme; char *userinfo; char *host; char *port; char *path; char *encoded_path; char *query; char *encoded_query; char *fragment; }; #define YYCTYPE char #define YYPEEK() *cur #define YYSKIP() if (++cur > lim) return 3; #define YYBACKUP() mar = cur #define YYRESTORE() cur = mar #define YYSTAGP(p) p = cur #define YYSTAGN(p) p = 0 static int parse_uri(const char *cur, struct tls_uri *uri) { const char *s1, *u1, *h1, *h3, *h5, *r1, *p1, *p3, *q1, *f1, *s2, *u2, *h2, *h4, *h6, *r2, *p2, *p4, *q2, *f2; long c; const char *mar, *lim; struct input inp; struct input *in = &inp; init_input(in); lim = cur + strlen(cur); c = 0; loop: /*!re2c re2c:yyfill:enable = 0; re2c:tags:expression = "in->@@"; end = "\x00"; eol = "\n"; alpha = [a-zA-Z]; digit = [0-9]; hexdigit = [0-9a-fA-F]; unreserved = alpha | digit | [-._~]; pct_encoded = "%" hexdigit{2}; sub_delims = [!$&'()*+,;=]; pchar = unreserved | pct_encoded | sub_delims | [:@]; scheme = @s1 alpha (alpha | digit | [-+.])* @s2; userinfo = @u1 (unreserved | pct_encoded | sub_delims | ":")* @u2; dec_octet = digit | [\x31-\x39] digit | "1" digit{2} | "2" [\x30-\x34] digit | "25" [\x30-\x35]; ipv4address = dec_octet "." dec_octet "." dec_octet "." dec_octet; h16 = hexdigit{1,4}; ls32 = h16 ":" h16 | ipv4address; ipv6address = (h16 ":"){6} ls32 | "::" (h16 ":"){5} ls32 | ( h16)? "::" (h16 ":"){4} ls32 | ((h16 ":"){0,1} h16)? "::" (h16 ":"){3} ls32 | ((h16 ":"){0,2} h16)? "::" (h16 ":"){2} ls32 | ((h16 ":"){0,3} h16)? "::" h16 ":" ls32 | ((h16 ":"){0,4} h16)? "::" ls32 | ((h16 ":"){0,5} h16)? "::" h16 | ((h16 ":"){0,6} h16)? "::"; ipvfuture = "v" hexdigit+ "." (unreserved | sub_delims | ":" )+; ip_literal = "[" ( ipv6address | ipvfuture ) "]"; reg_name = (unreserved | pct_encoded | sub_delims)*; host = @h1 ip_literal @h2 | @h3 ipv4address @h4 | @h5 reg_name @h6; port = @r1 digit* @r2; authority = (userinfo "@")? host (":" port)?; path_abempty = ("/" pchar*)*; path_absolute = "/" (pchar+ ("/" pchar*)*)?; path_rootless = pchar+ ("/" pchar*)*; path_empty = ""; hier_part = "//" authority @p1 path_abempty @p2 | @p3 (path_absolute | path_rootless | path_empty) @p4; query = @q1 (pchar | [/?])* @q2; fragment = @f1 (pchar | [/?])* @f2; uri = scheme ":" hier_part ("?" query)? ("#" fragment)?; * { return 1; } end { return 0; } eol { goto loop; } uri { uri->scheme = strndup(s1, (size_t)(s2 - s1)); if (u1) uri->userinfo = strndup(u1, (size_t)(u2 - u1)); if (h1) uri->host = strndup(h1, (size_t)(h2 - h1)); if (h3) uri->host = strndup(h3, (size_t)(h4 - h3)); if (h5) uri->host = strndup(h5, (size_t)(h6 - h5)); if (r1) uri->port = strndup(r1, (size_t)(r2 - r1)); if (p1) uri->path = strndup(p1, (size_t)(p2 - p1)); if (p3) uri->path = strndup(p3, (size_t)(p4 - p3)); if (q1) uri->query = strndup(q1, (size_t)(q2 - q1)); if (f1) uri->fragment = strndup(f1, (size_t)(f2 - f1)); return 0; } */ } static int hex(char *s) { int r = 0; int i; for (i = 0; i < 2; i++) { r *= 16; switch (s[i]) { case '0': break; case '1': r += 1; break; case '2': r += 2; break; case '3': r += 3; break; case '4': r += 4; break; case '5': r += 5; break; case '6': r += 6; break; case '7': r += 7; break; case '8': r += 8; break; case '9': r += 9; break; case 'A': case 'a': r += 11; break; case 'B': case 'b': r += 11; break; case 'C': case 'c': r += 12; break; case 'D': case 'd': r += 13; break; case 'E': case 'e': r += 14; break; case 'F': case 'f': r += 15; break; default: /* This is an error, but should have been caught at the lexing stage */ break; } } return r; } static void percent_decode(char *s) { char *d = s; if (!s) { return; } while (*s) { switch (*s) { case '%': if (*(s+1) && *(s+2)) { *d++ = hex(s+1); s += 3; } break; default: *d++ = *s++; break; } } *d = 0; } int tls_parse_uri(char *s, struct tls_uri *uri) { int rv; rv = parse_uri(s, uri); if (rv != 0) { return rv; } if (uri->port == NULL) { if (!strcmp(uri->scheme, "http")) { uri->port = strdup("80"); } else if (!strcmp(uri->scheme, "https")) { uri->port = strdup("443"); } else if (!strcmp(uri->scheme, "ftp")) { uri->port = strdup("21"); } else { uri->port = strdup("0"); } } if (uri->path) { uri->encoded_path = strdup(uri->path); } if (uri->query) { uri->encoded_query = strdup(uri->query); } percent_decode(uri->host); percent_decode(uri->path); percent_decode(uri->query); if (uri->path == NULL || uri->path[0] == 0) { free(uri->path); uri->path = strdup("/"); } return rv; } #define MARK fprintf(stderr, "%s %s:%d\n", __FILE__, __func__, __LINE__) void tls_free_uri(struct tls_uri *uri) { if (!uri) return; free(uri->host); uri->host = 0; free(uri->path); uri->path = 0; free(uri->query); uri->query = 0; free(uri->port); uri->port = 0; free(uri->scheme); uri->scheme = 0; free(uri->fragment); uri->fragment = 0; free(uri->encoded_path); uri->encoded_path = 0; free(uri->encoded_query); uri->encoded_query = 0; }