2 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32 adapt(int delta, int numpoints, int firsttime)
41 delta += (delta / numpoints);
44 while (delta > ((BASE - TMIN) * TMAX) / 2) {
45 delta = delta / (BASE - TMIN);
48 return k + (((BASE - TMIN + 1) * delta) / (delta + SKEW));
52 copy_label(const char *s, char *out, size_t len)
57 end = strchr(s, '\0');
62 for (t = end; t >= s; --t)
69 for (; s < t; ++s, ++out) {
81 if ('A' <= c && c <= 'Z')
84 if ('a' <= c && c <= 'z')
87 if ('0' <= c && c <= '9')
94 insert(char *out, size_t len, int codepoint, size_t i, const char **err)
99 if (codepoint <= 0x7F) {
100 *err = "puny: invalid decoded character (ASCII range)";
102 } else if (codepoint <= 0x7FF) {
104 } else if (codepoint <= 0xFFFF) {
106 } else if (codepoint <= 0x10FFFF) {
109 *err = "puny: invalid decoded character";
113 if ((t = utf8_nth(out, i)) == NULL) {
114 *err = "puny: invalid insert position";
118 if (t + l >= out + len) {
119 *err = "puny: insert would overflow";
123 memmove(t + l, t, strlen(t));
127 t[1] = ( codepoint & 0x3F) + 0x80;
128 t[0] = ((codepoint >> 6) & 0x1F) + 0xC0;
131 t[2] = ( codepoint & 0x3F) + 0x80;
132 t[1] = ((codepoint >> 6) & 0x3F) + 0x80;
133 t[0] = ((codepoint >> 12) & 0x0F) + 0xE0;
136 t[3] = ( codepoint & 0x3F) + 0x80;
137 t[2] = ((codepoint >> 6) & 0x3F) + 0x80;
138 t[1] = ((codepoint >> 12) & 0x3F) + 0x80;
139 t[0] = ((codepoint >> 18) & 0x07) + 0xF0;
146 decode(const char *str, char *out, size_t len, const char **err)
150 unsigned int oldi, bias, w, k, digit, t;
151 unsigned int numpoints;
154 if (!starts_with(str, "xn--")) {
155 strncpy(out, str, len);
162 if (strchr(str, '-') != NULL) {
163 if ((s = copy_label(str, out, len)) == NULL) {
164 *err = "puny: invalid label";
172 numpoints = strlen(out);
182 for (k = BASE; ; k += BASE) {
184 *err = "puny: label truncated?";
187 /* fail eventually? */
188 digit = digit_value(*s);
191 /* fail on overflow */
196 else if (k >= bias + TMAX)
206 bias = adapt(i - oldi, numpoints+1, oldi == 0);
207 n += i / (numpoints+1); /* fail on overflow */
208 i = i % (numpoints+1);
210 if (!insert(out, len, n, i, err))
220 end_of_label(const char *hostname)
222 for (; *hostname != '\0' && *hostname != '.'; ++hostname)
228 puny_decode(const char *hostname, char *out, size_t len, const char **err)
230 char label[LABEL_LEN];
235 if (hostname == NULL)
240 end = end_of_label(s);
242 if (l >= sizeof(label)) {
243 *err = "label too long";
250 if (!decode(label, out, len, err))
256 if (strlcat(out, ".", len) >= len) {
257 *err = "domain name too long";
263 *err = "domain name too long";