Coverage Report

Created: 2026-03-24 06:37

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.07k
{
75
6.07k
  int rc;
76
77
6.07k
  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.07k
  if (alabel)
85
2.25k
    {
86
2.25k
      size_t alabellen = strlen ((char *) alabel), u32len =
87
2.25k
  IDN2_LABEL_MAX_LENGTH * 4;
88
2.25k
      uint32_t u32[IDN2_DOMAIN_MAX_LENGTH * 4];
89
2.25k
      uint8_t *tmp;
90
2.25k
      uint8_t u8[IDN2_DOMAIN_MAX_LENGTH + 1];
91
2.25k
      size_t u8len;
92
93
2.25k
      if (alabellen > IDN2_LABEL_MAX_LENGTH)
94
14
  return IDN2_TOO_BIG_LABEL;
95
96
2.23k
      if (alabellen <= 4)
97
13
  return IDN2_INVALID_ALABEL;
98
2.22k
      if (alabel[0] != 'x'
99
2.22k
    || alabel[1] != 'n' || alabel[2] != '-' || alabel[3] != '-')
100
0
  return IDN2_INVALID_ALABEL;
101
102
2.22k
      if (!_idn2_ascii_p (alabel, alabellen))
103
7
  return IDN2_INVALID_ALABEL;
104
105
2.21k
      rc = idn2_punycode_decode ((char *) alabel + 4, alabellen - 4,
106
2.21k
         u32, &u32len);
107
2.21k
      if (rc != IDN2_OK)
108
179
  return rc;
109
110
2.03k
      u8len = sizeof (u8);
111
2.03k
      if (u32_to_u8 (u32, u32len, u8, &u8len) == NULL)
112
13
  return IDN2_ENCODING_ERROR;
113
2.02k
      u8[u8len] = '\0';
114
115
2.02k
      if (ulabel)
116
0
  {
117
0
    if (strcmp ((char *) ulabel, (char *) u8) != 0)
118
0
      return IDN2_UALABEL_MISMATCH;
119
0
  }
120
121
2.02k
      rc = idn2_register_u8 (u8, NULL, &tmp, 0);
122
2.02k
      if (rc != IDN2_OK)
123
1.41k
  return rc;
124
125
615
      rc = strcmp ((char *) alabel, (char *) tmp);
126
615
      free (tmp);
127
615
      if (rc != 0)
128
477
  return IDN2_UALABEL_MISMATCH;
129
130
138
      if (insertname)
131
138
  {
132
138
    uint8_t *m = (uint8_t *) strdup ((char *) alabel);
133
138
    if (!m)
134
0
      return IDN2_MALLOC;
135
136
138
    *insertname = m;
137
138
  }
138
138
    }
139
3.82k
  else        /* ulabel only */
140
3.82k
    {
141
3.82k
      size_t ulabellen = u8_strlen (ulabel);
142
3.82k
      uint32_t *u32;
143
3.82k
      size_t u32len;
144
3.82k
      size_t tmpl;
145
3.82k
      uint8_t tmp[IDN2_LABEL_MAX_LENGTH + 1];
146
147
3.82k
      if (_idn2_ascii_p (ulabel, ulabellen))
148
1.79k
  {
149
1.79k
    if (ulabellen > IDN2_LABEL_MAX_LENGTH)
150
12
      return IDN2_TOO_BIG_LABEL;
151
152
1.78k
    if (insertname)
153
1.78k
      {
154
1.78k
        uint8_t *m = (uint8_t *) strdup ((char *) ulabel);
155
1.78k
        if (!m)
156
0
    return IDN2_MALLOC;
157
1.78k
        *insertname = m;
158
1.78k
      }
159
1.78k
    return IDN2_OK;
160
1.78k
  }
161
162
2.02k
      rc = _idn2_u8_to_u32_nfc (ulabel, ulabellen, &u32, &u32len,
163
2.02k
        flags & IDN2_NFC_INPUT);
164
2.02k
      if (rc != IDN2_OK)
165
0
  return rc;
166
167
2.02k
      rc = _idn2_label_test (TEST_NFC
168
2.02k
           | TEST_DISALLOWED
169
2.02k
           | TEST_UNASSIGNED
170
2.02k
           | TEST_2HYPHEN
171
2.02k
           | TEST_HYPHEN_STARTEND
172
2.02k
           | TEST_LEADING_COMBINING
173
2.02k
           | TEST_CONTEXTJ_RULE
174
2.02k
           | TEST_CONTEXTO_RULE | TEST_BIDI, u32, u32len);
175
2.02k
      if (rc != IDN2_OK)
176
1.41k
  {
177
1.41k
    free (u32);
178
1.41k
    return rc;
179
1.41k
  }
180
181
615
      tmp[0] = 'x';
182
615
      tmp[1] = 'n';
183
615
      tmp[2] = '-';
184
615
      tmp[3] = '-';
185
186
615
      tmpl = IDN2_LABEL_MAX_LENGTH - 4;
187
615
      rc = idn2_punycode_encode (u32, u32len, (char *) tmp + 4, &tmpl);
188
615
      free (u32);
189
615
      if (rc != IDN2_OK)
190
0
  return rc;
191
192
615
      tmp[4 + tmpl] = '\0';
193
194
615
      if (insertname)
195
615
  {
196
615
    uint8_t *m = (uint8_t *) strdup ((char *) tmp);
197
615
    if (!m)
198
0
      return IDN2_MALLOC;
199
615
    *insertname = m;
200
615
  }
201
615
    }
202
203
753
  return IDN2_OK;
204
6.07k
}
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.50k
{
239
4.50k
  uint8_t *utf8ulabel = NULL;
240
4.50k
  int rc;
241
242
4.50k
  if (ulabel)
243
2.25k
    {
244
2.25k
      const char *encoding = locale_charset ();
245
246
2.25k
      utf8ulabel = u8_strconv_from_encoding (ulabel, encoding, iconveh_error);
247
248
2.25k
      if (utf8ulabel == NULL)
249
453
  {
250
453
    if (errno == ENOMEM)
251
0
      return IDN2_MALLOC;
252
453
    return IDN2_ICONV_FAIL;
253
453
  }
254
2.25k
    }
255
256
4.04k
  rc = idn2_register_u8 (utf8ulabel, (const uint8_t *) alabel,
257
4.04k
       (uint8_t **) insertname, flags | IDN2_NFC_INPUT);
258
259
4.04k
  free (utf8ulabel);
260
261
4.04k
  return rc;
262
4.50k
}