Coverage Report

Created: 2026-01-13 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libidn2/lib/register.c
Line
Count
Source
1
/* register.c - implementation of IDNA2008 register functions
2
   Copyright (C) 2011-2025 Simon Josefsson
3
4
   Libidn2 is free software: you can redistribute it and/or modify it
5
   under the terms of either:
6
7
     * the GNU Lesser General Public License as published by the Free
8
       Software Foundation; either version 3 of the License, or (at
9
       your option) any later version.
10
11
   or
12
13
     * the GNU General Public License as published by the Free
14
       Software Foundation; either version 2 of the License, or (at
15
       your option) any later version.
16
17
   or both in parallel, as here.
18
19
   This program is distributed in the hope that it will be useful,
20
   but WITHOUT ANY WARRANTY; without even the implied warranty of
21
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
   GNU General Public License for more details.
23
24
   You should have received copies of the GNU General Public License and
25
   the GNU Lesser General Public License along with this program.  If
26
   not, see <http://www.gnu.org/licenses/>.
27
*/
28
29
#include <config.h>
30
31
#include "idn2.h"
32
33
#include <errno.h>    /* errno */
34
#include <stdlib.h>   /* free */
35
36
#include <unitypes.h>
37
#include <uniconv.h>    /* u8_strconv_from_locale */
38
#include <unistr.h>   /* u32_to_u8 */
39
40
#include "idna.h"   /* _idn2_label_test */
41
42
/**
43
 * idn2_register_u8:
44
 * @ulabel: input zero-terminated UTF-8 and Unicode NFC string, or NULL.
45
 * @alabel: input zero-terminated ACE encoded string (xn--), or NULL.
46
 * @insertname: newly allocated output variable with name to register in DNS.
47
 * @flags: optional #idn2_flags to modify behaviour.
48
 *
49
 * Perform IDNA2008 register string conversion on domain label @ulabel
50
 * and @alabel, as described in section 4 of RFC 5891.  Note that the
51
 * input @ulabel must be encoded in UTF-8 and be in Unicode NFC form.
52
 *
53
 * Pass %IDN2_NFC_INPUT in @flags to convert input @ulabel to NFC form
54
 * before further processing.
55
 *
56
 * It is recommended to supply both @ulabel and @alabel for better
57
 * error checking, but supplying just one of them will work.  Passing
58
 * in only @alabel is better than only @ulabel.  See RFC 5891 section
59
 * 4 for more information.
60
 *
61
 * After version 0.11: @insertname may be NULL to test conversion of @src
62
 * without allocating memory.
63
 *
64
 * Returns: On successful conversion %IDN2_OK is returned, when the
65
 *   given @ulabel and @alabel does not match each other
66
 *   %IDN2_UALABEL_MISMATCH is returned, when either of the input
67
 *   labels are too long %IDN2_TOO_BIG_LABEL is returned, when @alabel
68
 *   does does not appear to be a proper A-label %IDN2_INVALID_ALABEL
69
 *   is returned, or another error code is returned.
70
 **/
71
int
72
idn2_register_u8 (const uint8_t *ulabel, const uint8_t *alabel,
73
      uint8_t **insertname, int flags)
74
6.19k
{
75
6.19k
  int rc;
76
77
6.19k
  if (ulabel == NULL && alabel == NULL)
78
0
    {
79
0
      if (insertname)
80
0
  *insertname = NULL;
81
0
      return IDN2_OK;
82
0
    }
83
84
6.19k
  if (alabel)
85
2.30k
    {
86
2.30k
      size_t alabellen = strlen ((char *) alabel), u32len =
87
2.30k
  IDN2_LABEL_MAX_LENGTH * 4;
88
2.30k
      uint32_t u32[IDN2_DOMAIN_MAX_LENGTH * 4];
89
2.30k
      uint8_t *tmp;
90
2.30k
      uint8_t u8[IDN2_DOMAIN_MAX_LENGTH + 1];
91
2.30k
      size_t u8len;
92
93
2.30k
      if (alabellen > IDN2_LABEL_MAX_LENGTH)
94
20
  return IDN2_TOO_BIG_LABEL;
95
96
2.28k
      if (alabellen <= 4)
97
12
  return IDN2_INVALID_ALABEL;
98
2.27k
      if (alabel[0] != 'x'
99
2.27k
    || alabel[1] != 'n' || alabel[2] != '-' || alabel[3] != '-')
100
0
  return IDN2_INVALID_ALABEL;
101
102
2.27k
      if (!_idn2_ascii_p (alabel, alabellen))
103
8
  return IDN2_INVALID_ALABEL;
104
105
2.26k
      rc = idn2_punycode_decode ((char *) alabel + 4, alabellen - 4,
106
2.26k
         u32, &u32len);
107
2.26k
      if (rc != IDN2_OK)
108
177
  return rc;
109
110
2.08k
      u8len = sizeof (u8);
111
2.08k
      if (u32_to_u8 (u32, u32len, u8, &u8len) == NULL)
112
12
  return IDN2_ENCODING_ERROR;
113
2.07k
      u8[u8len] = '\0';
114
115
2.07k
      if (ulabel)
116
0
  {
117
0
    if (strcmp ((char *) ulabel, (char *) u8) != 0)
118
0
      return IDN2_UALABEL_MISMATCH;
119
0
  }
120
121
2.07k
      rc = idn2_register_u8 (u8, NULL, &tmp, 0);
122
2.07k
      if (rc != IDN2_OK)
123
1.44k
  return rc;
124
125
625
      rc = strcmp ((char *) alabel, (char *) tmp);
126
625
      free (tmp);
127
625
      if (rc != 0)
128
468
  return IDN2_UALABEL_MISMATCH;
129
130
157
      if (insertname)
131
157
  {
132
157
    uint8_t *m = (uint8_t *) strdup ((char *) alabel);
133
157
    if (!m)
134
0
      return IDN2_MALLOC;
135
136
157
    *insertname = m;
137
157
  }
138
157
    }
139
3.89k
  else        /* ulabel only */
140
3.89k
    {
141
3.89k
      size_t ulabellen = u8_strlen (ulabel);
142
3.89k
      uint32_t *u32;
143
3.89k
      size_t u32len;
144
3.89k
      size_t tmpl;
145
3.89k
      uint8_t tmp[IDN2_LABEL_MAX_LENGTH + 1];
146
147
3.89k
      if (_idn2_ascii_p (ulabel, ulabellen))
148
1.81k
  {
149
1.81k
    if (ulabellen > IDN2_LABEL_MAX_LENGTH)
150
14
      return IDN2_TOO_BIG_LABEL;
151
152
1.80k
    if (insertname)
153
1.80k
      {
154
1.80k
        uint8_t *m = (uint8_t *) strdup ((char *) ulabel);
155
1.80k
        if (!m)
156
0
    return IDN2_MALLOC;
157
1.80k
        *insertname = m;
158
1.80k
      }
159
1.80k
    return IDN2_OK;
160
1.80k
  }
161
162
2.07k
      rc = _idn2_u8_to_u32_nfc (ulabel, ulabellen, &u32, &u32len,
163
2.07k
        flags & IDN2_NFC_INPUT);
164
2.07k
      if (rc != IDN2_OK)
165
0
  return rc;
166
167
2.07k
      rc = _idn2_label_test (TEST_NFC
168
2.07k
           | TEST_DISALLOWED
169
2.07k
           | TEST_UNASSIGNED
170
2.07k
           | TEST_2HYPHEN
171
2.07k
           | TEST_HYPHEN_STARTEND
172
2.07k
           | TEST_LEADING_COMBINING
173
2.07k
           | TEST_CONTEXTJ_RULE
174
2.07k
           | TEST_CONTEXTO_RULE | TEST_BIDI, u32, u32len);
175
2.07k
      if (rc != IDN2_OK)
176
1.44k
  {
177
1.44k
    free (u32);
178
1.44k
    return rc;
179
1.44k
  }
180
181
625
      tmp[0] = 'x';
182
625
      tmp[1] = 'n';
183
625
      tmp[2] = '-';
184
625
      tmp[3] = '-';
185
186
625
      tmpl = IDN2_LABEL_MAX_LENGTH - 4;
187
625
      rc = idn2_punycode_encode (u32, u32len, (char *) tmp + 4, &tmpl);
188
625
      free (u32);
189
625
      if (rc != IDN2_OK)
190
0
  return rc;
191
192
625
      tmp[4 + tmpl] = '\0';
193
194
625
      if (insertname)
195
625
  {
196
625
    uint8_t *m = (uint8_t *) strdup ((char *) tmp);
197
625
    if (!m)
198
0
      return IDN2_MALLOC;
199
625
    *insertname = m;
200
625
  }
201
625
    }
202
203
782
  return IDN2_OK;
204
6.19k
}
205
206
/**
207
 * idn2_register_ul:
208
 * @ulabel: input zero-terminated locale encoded string, or NULL.
209
 * @alabel: input zero-terminated ACE encoded string (xn--), or NULL.
210
 * @insertname: newly allocated output variable with name to register in DNS.
211
 * @flags: optional #idn2_flags to modify behaviour.
212
 *
213
 * Perform IDNA2008 register string conversion on domain label @ulabel
214
 * and @alabel, as described in section 4 of RFC 5891.  Note that the
215
 * input @ulabel is assumed to be encoded in the locale's default
216
 * coding system, and will be transcoded to UTF-8 and NFC normalized
217
 * by this function.
218
 *
219
 * It is recommended to supply both @ulabel and @alabel for better
220
 * error checking, but supplying just one of them will work.  Passing
221
 * in only @alabel is better than only @ulabel.  See RFC 5891 section
222
 * 4 for more information.
223
 *
224
 * After version 0.11: @insertname may be NULL to test conversion of @src
225
 * without allocating memory.
226
 *
227
 * Returns: On successful conversion %IDN2_OK is returned, when the
228
 *   given @ulabel and @alabel does not match each other
229
 *   %IDN2_UALABEL_MISMATCH is returned, when either of the input
230
 *   labels are too long %IDN2_TOO_BIG_LABEL is returned, when @alabel
231
 *   does does not appear to be a proper A-label %IDN2_INVALID_ALABEL
232
 *   is returned, when @ulabel locale to UTF-8 conversion failed
233
 *   %IDN2_ICONV_FAIL is returned, or another error code is returned.
234
 **/
235
int
236
idn2_register_ul (const char *ulabel, const char *alabel,
237
      char **insertname, int flags)
238
4.60k
{
239
4.60k
  uint8_t *utf8ulabel = NULL;
240
4.60k
  int rc;
241
242
4.60k
  if (ulabel)
243
2.30k
    {
244
2.30k
      const char *encoding = locale_charset ();
245
246
2.30k
      utf8ulabel = u8_strconv_from_encoding (ulabel, encoding, iconveh_error);
247
248
2.30k
      if (utf8ulabel == NULL)
249
486
  {
250
486
    if (errno == ENOMEM)
251
0
      return IDN2_MALLOC;
252
486
    return IDN2_ICONV_FAIL;
253
486
  }
254
2.30k
    }
255
256
4.12k
  rc = idn2_register_u8 (utf8ulabel, (const uint8_t *) alabel,
257
4.12k
       (uint8_t **) insertname, flags | IDN2_NFC_INPUT);
258
259
4.12k
  free (utf8ulabel);
260
261
4.12k
  return rc;
262
4.60k
}