From c7b33a418d9426ee311db45473cb20bad94df26a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tim=20R=C3=BChsen?= <tim.ruehsen@gmx.de>
Date: Mon, 10 Feb 2020 15:10:54 +0100
Subject: [PATCH] Implement full roundtrip for lookup functionality
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

With TR64 enabled (default), 'â˜ºi' was converted to 'xn-- o-oia59s'.
The output contains an illegal space and thus could not be decoded any more.

References:
https://gitlab.com/libidn/libidn2/issues/78
https://gitlab.isc.org/isc-projects/bind9/issues/1610

Reported-by: Chris Malton
---
 lib/lookup.c        | 33 +++++++++++++++++++++++----------
 tests/test-lookup.c |  5 +++++
 3 files changed, 29 insertions(+), 10 deletions(-)
 
diff --git a/lib/lookup.c b/lib/lookup.c
index a55f82b..5e5ff12 100644
--- a/lib/lookup.c
+++ b/lib/lookup.c
@@ -1,5 +1,6 @@
 /* lookup.c - implementation of IDNA2008 lookup functions
    Copyright (C) 2011-2017 Simon Josefsson
+   Copyright (C) 2017-2020 Tim Ruehsen
 
    Libidn2 is free software: you can redistribute it and/or modify it
    under the terms of either:
@@ -123,7 +124,7 @@ label (const uint8_t * src, size_t srclen, uint8_t * dst, size_t * dstlen,
        int flags)
 {
   size_t plen;
-  uint32_t *p;
+  uint32_t *p = NULL;
   const uint8_t *src_org = NULL;
   uint8_t *src_allocated = NULL;
   int rc, check_roundtrip = 0;
@@ -187,10 +188,7 @@ label (const uint8_t * src, size_t srclen, uint8_t * dst, size_t * dstlen,
 	p, plen);
 
       if (rc != IDN2_OK)
-	{
-	  free (p);
-	  goto out;
-	}
+	goto out;
     }
 
   dst[0] = 'x';
@@ -200,7 +198,6 @@ label (const uint8_t * src, size_t srclen, uint8_t * dst, size_t * dstlen,
 
   tmpl = *dstlen - 4;
   rc = _idn2_punycode_encode_internal (plen, p, &tmpl, (char *) dst + 4);
-  free (p);
   if (rc != IDN2_OK)
     goto out;
 
@@ -210,15 +207,31 @@ label (const uint8_t * src, size_t srclen, uint8_t * dst, size_t * dstlen,
   if (check_roundtrip)
     {
       if (srclen_org != *dstlen || c_strncasecmp ((char *) src_org, (char *) dst, srclen_org))
-      {
-        rc = IDN2_ALABEL_ROUNDTRIP_FAILED;
-	goto out;
-      }
+        {
+	  rc = IDN2_ALABEL_ROUNDTRIP_FAILED;
+	  goto out;
+        }
+    }
+  else if (!(flags & IDN2_NO_ALABEL_ROUNDTRIP))
+    {
+      rc = _idn2_punycode_decode_internal (*dstlen - 4, (char *) dst + 4, &label32_len, label_u32);
+      if (rc)
+        {
+	  rc = IDN2_ALABEL_ROUNDTRIP_FAILED;
+	  goto out;
+        }
+
+      if (plen != label32_len || u32_cmp (p, label_u32, label32_len))
+        {
+	  rc = IDN2_ALABEL_ROUNDTRIP_FAILED;
+	  goto out;
+        }
     }
 
   rc = IDN2_OK;
 
 out:
+  free (p);
   free (src_allocated);
   return rc;
 }
diff --git a/tests/test-lookup.c b/tests/test-lookup.c
index aa75e18..2b49cb1 100644
--- a/tests/test-lookup.c
+++ b/tests/test-lookup.c
@@ -1,5 +1,6 @@
 /* test-lookup.c --- Self tests for IDNA processing
    Copyright (C) 2011-2017 Simon Josefsson
+   Copyright (C) 2017-2020 Tim Ruehsen
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -838,6 +839,10 @@ static const struct idna idna[] = {
   {"\xc3\xa4_x", "xn--_x-uia", IDN2_OK, IDN2_TRANSITIONAL},
   /* failing lookup round-trip */
   {"xn--te_", "", IDN2_ALABEL_ROUNDTRIP_FAILED},
+  /* failing lookup round-trip: â˜º -> xn-- o-oia59s (illegal space in output, see https://gitlab.com/libidn/libidn2/issues/78) */
+  {"\xc3\xa2\xcb\x9c\xc2\xba", "", IDN2_DISALLOWED, IDN2_NO_TR46},
+  {"\xc3\xa2\xcb\x9c\xc2\xba", "", IDN2_ALABEL_ROUNDTRIP_FAILED, IDN2_TRANSITIONAL},
+  {"\xc3\xa2\xcb\x9c\xc2\xba", "", IDN2_ALABEL_ROUNDTRIP_FAILED, IDN2_NONTRANSITIONAL},
 };
 
 static int ok = 0, failed = 0;
-- 
1.8.3.1

