+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
+}
+
+int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
+#ifdef HAVE_LIBIDN
+ size_t input_size, output_size;
+ _cleanup_free_ uint32_t *input = NULL;
+ _cleanup_free_ char *result = NULL;
+ uint32_t *output = NULL;
+ size_t w;
+
+ /* To be invoked after unescaping */
+
+ assert(encoded);
+ assert(decoded);
+
+ if (encoded_size < sizeof(IDNA_ACE_PREFIX)-1)
+ return 0;
+
+ if (memcmp(encoded, IDNA_ACE_PREFIX, sizeof(IDNA_ACE_PREFIX) -1) != 0)
+ return 0;
+
+ input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
+ if (!input)
+ return -ENOMEM;
+
+ output_size = input_size;
+ output = newa(uint32_t, output_size);
+
+ idna_to_unicode_44i(input, input_size, output, &output_size, 0);
+
+ result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w);
+ if (!result)
+ return -ENOMEM;
+ if (w <= 0)
+ return 0;
+ if (w+1 > decoded_max)
+ return -EINVAL;
+
+ memcpy(decoded, result, w+1);
+ return w;
+#else
+ return 0;
+#endif
+}
+