int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { #ifdef HAVE_LIBIDN _cleanup_free_ uint32_t *input = NULL; size_t input_size; const char *p; bool contains_8bit = false; assert(encoded); assert(decoded); assert(decoded_max >= DNS_LABEL_MAX); if (encoded_size <= 0) return 0; for (p = encoded; p < encoded + encoded_size; p++) if ((uint8_t) *p > 127) contains_8bit = true; if (!contains_8bit) return 0; input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); if (!input) return -ENOMEM; if (idna_to_ascii_4i(input, input_size, decoded, 0) != 0) return -EINVAL; return strlen(decoded); #else return 0; #endif }
/** * idna_to_ascii_4z: * @input: zero terminated input Unicode string. * @output: pointer to newly allocated output string. * @flags: IDNA flags, e.g. IDNA_ALLOW_UNASSIGNED or IDNA_USE_STD3_ASCII_RULES. * * Convert UCS-4 domain name to ASCII string. The domain name may * contain several labels, separated by dots. The output buffer must * be deallocated by the caller. * * Return value: Returns IDNA_SUCCESS on success, or error code. **/ int idna_to_ascii_4z (const uint32_t * input, char **output, int flags) { const uint32_t *start = input; const uint32_t *end = input; char buf[64]; char *out = NULL; int rc; /* 1) Whenever dots are used as label separators, the following characters MUST be recognized as dots: U+002E (full stop), U+3002 (ideographic full stop), U+FF0E (fullwidth full stop), U+FF61 (halfwidth ideographic full stop). */ if (input[0] == 0) { /* Handle implicit zero-length root label. */ *output = malloc (1); if (!*output) return IDNA_MALLOC_ERROR; strcpy (*output, ""); return IDNA_SUCCESS; } if (DOTP (input[0]) && input[1] == 0) { /* Handle explicit zero-length root label. */ *output = malloc (2); if (!*output) return IDNA_MALLOC_ERROR; strcpy (*output, "."); return IDNA_SUCCESS; } *output = NULL; do { end = start; for (; *end && !DOTP (*end); end++) ; if (*end == '\0' && start == end) { /* Handle explicit zero-length root label. */ buf[0] = '\0'; } else { rc = idna_to_ascii_4i (start, end - start, buf, flags); if (rc != IDNA_SUCCESS) return rc; } if (out) { char *newp = realloc (out, strlen (out) + 1 + strlen (buf) + 1); if (!newp) { free (out); return IDNA_MALLOC_ERROR; } out = newp; strcat (out, "."); strcat (out, buf); } else { out = (char *) malloc (strlen (buf) + 1); if (!out) return IDNA_MALLOC_ERROR; strcpy (out, buf); } start = end + 1; } while (*end); *output = out; return IDNA_SUCCESS; }
/* ToUnicode(). May realloc() utf8in. */ static int idna_to_unicode_internal (char *utf8in, uint32_t * out, size_t * outlen, int flags) { int rc; char tmpout[64]; size_t utf8len = strlen (utf8in) + 1; size_t addlen = 0; /* * ToUnicode consists of the following steps: * * 1. If the sequence contains any code points outside the ASCII range * (0..7F) then proceed to step 2, otherwise skip to step 3. */ { size_t i; int inasciirange; inasciirange = 1; for (i = 0; utf8in[i]; i++) if (utf8in[i] & ~0x7F) inasciirange = 0; if (inasciirange) goto step3; } /* * 2. Perform the steps specified in [NAMEPREP] and fail if there is an * error. (If step 3 of ToASCII is also performed here, it will not * affect the overall behavior of ToUnicode, but it is not * necessary.) The AllowUnassigned flag is used in [NAMEPREP]. */ do { char *newp = realloc (utf8in, utf8len + addlen); if (newp == NULL) { free (utf8in); return IDNA_MALLOC_ERROR; } utf8in = newp; if (flags & IDNA_ALLOW_UNASSIGNED) rc = stringprep_nameprep (utf8in, utf8len + addlen); else rc = stringprep_nameprep_no_unassigned (utf8in, utf8len + addlen); addlen += 1; } while (rc == STRINGPREP_TOO_SMALL_BUFFER); if (rc != STRINGPREP_OK) { free (utf8in); return IDNA_STRINGPREP_ERROR; } /* 3. Verify that the sequence begins with the ACE prefix, and save a * copy of the sequence. */ step3: if (memcmp (IDNA_ACE_PREFIX, utf8in, strlen (IDNA_ACE_PREFIX)) != 0) { free (utf8in); return IDNA_NO_ACE_PREFIX; } /* 4. Remove the ACE prefix. */ memmove (utf8in, &utf8in[strlen (IDNA_ACE_PREFIX)], strlen (utf8in) - strlen (IDNA_ACE_PREFIX) + 1); /* 5. Decode the sequence using the decoding algorithm in [PUNYCODE] * and fail if there is an error. Save a copy of the result of * this step. */ (*outlen)--; /* reserve one for the zero */ rc = punycode_decode (strlen (utf8in), utf8in, outlen, out, NULL); if (rc != PUNYCODE_SUCCESS) { free (utf8in); return IDNA_PUNYCODE_ERROR; } out[*outlen] = 0; /* add zero */ /* 6. Apply ToASCII. */ rc = idna_to_ascii_4i (out, *outlen, tmpout, flags); if (rc != IDNA_SUCCESS) { free (utf8in); return rc; } /* 7. Verify that the result of step 6 matches the saved copy from * step 3, using a case-insensitive ASCII comparison. */ if (strcasecmp (utf8in, tmpout + strlen (IDNA_ACE_PREFIX)) != 0) { free (utf8in); return IDNA_ROUNDTRIP_VERIFY_ERROR; } /* 8. Return the saved copy from step 5. */ free (utf8in); return IDNA_SUCCESS; }
void doit (void) { char label[100]; uint32_t *ucs4label = NULL; uint32_t tmp[100]; size_t len, len2, i; int rc; for (i = 0; i < sizeof (idna) / sizeof (idna[0]); i++) { if (debug) printf ("IDNA entry %d: %s\n", i, idna[i].name); if (debug) { printf ("in:\n"); ucs4print (idna[i].in, idna[i].inlen); } rc = idna_to_ascii_4i (idna[i].in, idna[i].inlen, label, idna[i].flags); if (rc != idna[i].toasciirc) { fail ("IDNA entry %d failed: %d\n", i, rc); if (debug) printf ("FATAL\n"); continue; } if (debug && rc == IDNA_SUCCESS) { printf ("computed out: %s\n", label); printf ("expected out: %s\n", idna[i].out); } else if (debug) printf ("returned %d expected %d\n", rc, idna[i].toasciirc); if (rc == IDNA_SUCCESS) { if (strlen (idna[i].out) != strlen (label) || strcasecmp (idna[i].out, label) != 0) { fail ("IDNA entry %d failed\n", i); if (debug) printf ("ERROR\n"); } else if (debug) printf ("OK\n"); } else if (debug) printf ("OK\n"); if (ucs4label) free (ucs4label); ucs4label = stringprep_utf8_to_ucs4 (idna[i].out, -1, &len); if (debug) { printf ("in: %s (%d==%d)\n", idna[i].out, strlen (idna[i].out), len); ucs4print (ucs4label, len); } len2 = sizeof (tmp) / sizeof (tmp[0]); rc = idna_to_unicode_44i (ucs4label, len, tmp, &len2, idna[i].flags); if (debug) { printf ("expected out (%d):\n", rc == IDNA_SUCCESS ? idna[i].inlen : len); if (rc == IDNA_SUCCESS) ucs4print (idna[i].in, idna[i].inlen); else ucs4print (ucs4label, len); printf ("computed out (%d):\n", len2); ucs4print (tmp, len2); } if (rc != idna[i].tounicoderc) { fail ("IDNA entry %d failed: %d\n", i, rc); if (debug) printf ("FATAL\n"); continue; } if ((rc == IDNA_SUCCESS && (len2 != idna[i].inlen || memcmp (idna[i].in, tmp, len2) != 0)) || (rc != IDNA_SUCCESS && (len2 != len || memcmp (ucs4label, tmp, len) != 0))) { if (debug) { if (rc == IDNA_SUCCESS) printf ("len=%d len2=%d\n", len2, idna[i].inlen); else printf ("len=%d len2=%d\n", len, len2); } fail ("IDNA entry %d failed\n", i); if (debug) printf ("ERROR\n"); } else if (debug) printf ("OK\n\n"); } if (ucs4label) free (ucs4label); }