Fix loss of undercores in domain names when using libidn2.

libidn2 strips underscores from international domain names
when encoding them. Indeed, it strips underscores even if
no encoding is necessary, which breaks SRV records.

Don't submit domain names to IDN encoding if they contain
one or more underscores to fix this.
This commit is contained in:
Simon Kelley
2017-07-08 21:20:16 +01:00
parent 1d224949cc
commit 69a815aa8f

View File

@@ -111,6 +111,7 @@ u64 rand64(void)
return (u64)out[outleft+1] + (((u64)out[outleft]) << 32); return (u64)out[outleft+1] + (((u64)out[outleft]) << 32);
} }
/* returns 2 if names is OK but contains one or more underscores */
static int check_name(char *in) static int check_name(char *in)
{ {
/* remove trailing . /* remove trailing .
@@ -118,6 +119,7 @@ static int check_name(char *in)
size_t dotgap = 0, l = strlen(in); size_t dotgap = 0, l = strlen(in);
char c; char c;
int nowhite = 0; int nowhite = 0;
int hasuscore = 0;
if (l == 0 || l > MAXDNAME) return 0; if (l == 0 || l > MAXDNAME) return 0;
@@ -141,13 +143,17 @@ static int check_name(char *in)
return 0; return 0;
#endif #endif
else if (c != ' ') else if (c != ' ')
nowhite = 1; {
nowhite = 1;
if (c == '_')
hasuscore = 1;
}
} }
if (!nowhite) if (!nowhite)
return 0; return 0;
return 1; return hasuscore ? 2 : 1;
} }
/* Hostnames have a more limited valid charset than domain names /* Hostnames have a more limited valid charset than domain names
@@ -186,43 +192,48 @@ int legal_hostname(char *name)
char *canonicalise(char *in, int *nomem) char *canonicalise(char *in, int *nomem)
{ {
char *ret = NULL; char *ret = NULL;
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2)
int rc; int rc;
#endif
if (nomem) if (nomem)
*nomem = 0; *nomem = 0;
if (!check_name(in)) if (!(rc = check_name(in)))
return NULL; return NULL;
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) #if defined(HAVE_IDN) || defined(HAVE_LIBIDN2)
#ifdef HAVE_LIBIDN2 /* libidn2 strips underscores, so don't do IDN processing
rc = idn2_to_ascii_lz(in, &ret, IDN2_NONTRANSITIONAL); if the name has an underscore (check_name() returned 2) */
if (rc == IDN2_DISALLOWED) if (rc != 2)
rc = idn2_to_ascii_lz(in, &ret, IDN2_TRANSITIONAL);
#else
rc = idna_to_ascii_lz(in, &ret, 0);
#endif
if (rc != IDNA_SUCCESS)
{ {
if (ret) #ifdef HAVE_LIBIDN2
free(ret); rc = idn2_to_ascii_lz(in, &ret, IDN2_NONTRANSITIONAL);
if (rc == IDN2_DISALLOWED)
if (nomem && (rc == IDNA_MALLOC_ERROR || rc == IDNA_DLOPEN_ERROR)) rc = idn2_to_ascii_lz(in, &ret, IDN2_TRANSITIONAL);
#else
rc = idna_to_ascii_lz(in, &ret, 0);
#endif
if (rc != IDNA_SUCCESS)
{ {
my_syslog(LOG_ERR, _("failed to allocate memory")); if (ret)
*nomem = 1; free(ret);
if (nomem && (rc == IDNA_MALLOC_ERROR || rc == IDNA_DLOPEN_ERROR))
{
my_syslog(LOG_ERR, _("failed to allocate memory"));
*nomem = 1;
}
return NULL;
} }
return NULL; return ret;
} }
#else #endif
if ((ret = whine_malloc(strlen(in)+1))) if ((ret = whine_malloc(strlen(in)+1)))
strcpy(ret, in); strcpy(ret, in);
else if (nomem) else if (nomem)
*nomem = 1; *nomem = 1;
#endif
return ret; return ret;
} }