Coverage Report

Created: 2025-07-01 06:25

/src/nss/lib/util/portreg.c
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
/*
6
 * shexp.c: shell-like wildcard match routines
7
 *
8
 * See shexp.h for public documentation.
9
 */
10
11
#include "seccomon.h"
12
#include "portreg.h"
13
14
/* ----------------------------- shexp_valid ------------------------------ */
15
16
static int
17
_valid_subexp(const char *exp, char stop1, char stop2)
18
0
{
19
0
    register int x;
20
0
    int nsc = 0; /* Number of special characters */
21
0
    int np;      /* Number of pipe characters in union */
22
0
    int tld = 0; /* Number of tilde characters */
23
24
0
    for (x = 0; exp[x] && (exp[x] != stop1) && (exp[x] != stop2); ++x) {
25
0
        switch (exp[x]) {
26
0
            case '~':
27
0
                if (tld) /* at most one exclusion */
28
0
                    return INVALID_SXP;
29
0
                if (stop1) /* no exclusions within unions */
30
0
                    return INVALID_SXP;
31
0
                if (!exp[x + 1]) /* exclusion cannot be last character */
32
0
                    return INVALID_SXP;
33
0
                if (!x) /* exclusion cannot be first character */
34
0
                    return INVALID_SXP;
35
0
                ++tld;
36
            /* fall through */
37
0
            case '*':
38
0
            case '?':
39
0
            case '$':
40
0
                ++nsc;
41
0
                break;
42
0
            case '[':
43
0
                ++nsc;
44
0
                if ((!exp[++x]) || (exp[x] == ']'))
45
0
                    return INVALID_SXP;
46
0
                for (; exp[x] && (exp[x] != ']'); ++x) {
47
0
                    if (exp[x] == '\\' && !exp[++x])
48
0
                        return INVALID_SXP;
49
0
                }
50
0
                if (!exp[x])
51
0
                    return INVALID_SXP;
52
0
                break;
53
0
            case '(':
54
0
                ++nsc;
55
0
                if (stop1) /* no nested unions */
56
0
                    return INVALID_SXP;
57
0
                np = -1;
58
0
                do {
59
0
                    int t = _valid_subexp(&exp[++x], ')', '|');
60
0
                    if (t == 0 || t == INVALID_SXP)
61
0
                        return INVALID_SXP;
62
0
                    x += t;
63
0
                    if (!exp[x])
64
0
                        return INVALID_SXP;
65
0
                    ++np;
66
0
                } while (exp[x] == '|');
67
0
                if (np < 1) /* must be at least one pipe */
68
0
                    return INVALID_SXP;
69
0
                break;
70
0
            case ')':
71
0
            case '|':
72
0
            case ']':
73
0
                return INVALID_SXP;
74
0
            case '\\':
75
0
                ++nsc;
76
0
                if (!exp[++x])
77
0
                    return INVALID_SXP;
78
0
                break;
79
0
            default:
80
0
                break;
81
0
        }
82
0
    }
83
0
    if ((!stop1) && (!nsc)) /* must be at least one special character */
84
0
        return NON_SXP;
85
0
    return ((exp[x] == stop1 || exp[x] == stop2) ? x : INVALID_SXP);
86
0
}
87
88
int
89
PORT_RegExpValid(const char *exp)
90
0
{
91
0
    int x;
92
93
0
    x = _valid_subexp(exp, '\0', '\0');
94
0
    return (x < 0 ? x : VALID_SXP);
95
0
}
96
97
/* ----------------------------- shexp_match ----------------------------- */
98
99
0
#define MATCH 0
100
0
#define NOMATCH 1
101
0
#define ABORTED -1
102
103
static int
104
_shexp_match(const char *str, const char *exp, PRBool case_insensitive,
105
             unsigned int level);
106
107
/* Count characters until we reach a NUL character or either of the
108
 * two delimiter characters, stop1 or stop2.  If we encounter a bracketed
109
 * expression, look only for NUL or ']' inside it.  Do not look for stop1
110
 * or stop2 inside it. Return ABORTED if bracketed expression is unterminated.
111
 * Handle all escaping.
112
 * Return index in input string of first stop found, or ABORTED if not found.
113
 * If "dest" is non-NULL, copy counted characters to it and NUL terminate.
114
 */
115
static int
116
_scan_and_copy(const char *exp, char stop1, char stop2, char *dest)
117
0
{
118
0
    register int sx; /* source index */
119
0
    register char cc;
120
121
0
    for (sx = 0; (cc = exp[sx]) && cc != stop1 && cc != stop2; sx++) {
122
0
        if (cc == '\\') {
123
0
            if (!exp[++sx])
124
0
                return ABORTED; /* should be impossible */
125
0
        } else if (cc == '[') {
126
0
            while ((cc = exp[++sx]) && cc != ']') {
127
0
                if (cc == '\\' && !exp[++sx])
128
0
                    return ABORTED;
129
0
            }
130
0
            if (!cc)
131
0
                return ABORTED; /* should be impossible */
132
0
        }
133
0
    }
134
0
    if (dest && sx) {
135
        /* Copy all but the closing delimiter. */
136
0
        memcpy(dest, exp, sx);
137
0
        dest[sx] = 0;
138
0
    }
139
0
    return cc ? sx : ABORTED; /* index of closing delimiter */
140
0
}
141
142
/* On input, exp[0] is the opening parenthesis of a union.
143
 * See if any of the alternatives in the union matches as a pattern.
144
 * The strategy is to take each of the alternatives, in turn, and append
145
 * the rest of the expression (after the closing ')' that marks the end of
146
 * this union) to that alternative, and then see if the resultant expression
147
 * matches the input string.  Repeat this until some alternative matches,
148
 * or we have an abort.
149
 */
150
static int
151
_handle_union(const char *str, const char *exp, PRBool case_insensitive,
152
              unsigned int level)
153
0
{
154
0
    register int sx; /* source index */
155
0
    int cp;          /* source index of closing parenthesis */
156
0
    int count;
157
0
    int ret = NOMATCH;
158
0
    char *e2;
159
160
    /* Find the closing parenthesis that ends this union in the expression */
161
0
    cp = _scan_and_copy(exp, ')', '\0', NULL);
162
0
    if (cp == ABORTED || cp < 4) /* must be at least "(a|b" before ')' */
163
0
        return ABORTED;
164
0
    ++cp; /* now index of char after closing parenthesis */
165
0
    e2 = (char *)PORT_Alloc(1 + strlen(exp));
166
0
    if (!e2)
167
0
        return ABORTED;
168
0
    for (sx = 1;; ++sx) {
169
        /* Here, exp[sx] is one character past the preceding '(' or '|'. */
170
        /* Copy everything up to the next delimiter to e2 */
171
0
        count = _scan_and_copy(exp + sx, ')', '|', e2);
172
0
        if (count == ABORTED || !count) {
173
0
            ret = ABORTED;
174
0
            break;
175
0
        }
176
0
        sx += count;
177
        /* Append everything after closing parenthesis to e2. This is safe. */
178
0
        strcpy(e2 + count, exp + cp);
179
0
        ret = _shexp_match(str, e2, case_insensitive, level + 1);
180
0
        if (ret != NOMATCH || !exp[sx] || exp[sx] == ')')
181
0
            break;
182
0
    }
183
0
    PORT_Free(e2);
184
0
    if (sx < 2)
185
0
        ret = ABORTED;
186
0
    return ret;
187
0
}
188
189
/* returns 1 if val is in range from start..end, case insensitive. */
190
static int
191
_is_char_in_range(int start, int end, int val)
192
0
{
193
0
    char map[256];
194
0
    memset(map, 0, sizeof map);
195
0
    while (start <= end)
196
0
        map[tolower(start++)] = 1;
197
0
    return map[tolower(val)];
198
0
}
199
200
static int
201
_shexp_match(const char *str, const char *exp, PRBool case_insensitive,
202
             unsigned int level)
203
0
{
204
0
    register int x; /* input string index */
205
0
    register int y; /* expression index */
206
0
    int ret, neg;
207
208
0
    if (level > 20) /* Don't let the stack get too deep. */
209
0
        return ABORTED;
210
0
    for (x = 0, y = 0; exp[y]; ++y, ++x) {
211
0
        if ((!str[x]) && (exp[y] != '$') && (exp[y] != '*')) {
212
0
            return NOMATCH;
213
0
        }
214
0
        switch (exp[y]) {
215
0
            case '$':
216
0
                if (str[x])
217
0
                    return NOMATCH;
218
0
                --x; /* we don't want loop to increment x */
219
0
                break;
220
0
            case '*':
221
0
                while (exp[++y] == '*') {
222
0
                }
223
0
                if (!exp[y])
224
0
                    return MATCH;
225
0
                while (str[x]) {
226
0
                    ret = _shexp_match(&str[x++], &exp[y], case_insensitive,
227
0
                                       level + 1);
228
0
                    switch (ret) {
229
0
                        case NOMATCH:
230
0
                            continue;
231
0
                        case ABORTED:
232
0
                            return ABORTED;
233
0
                        default:
234
0
                            return MATCH;
235
0
                    }
236
0
                }
237
0
                if ((exp[y] == '$') && (exp[y + 1] == '\0') && (!str[x]))
238
0
                    return MATCH;
239
0
                else
240
0
                    return NOMATCH;
241
0
            case '[': {
242
0
                int start, end = 0, i;
243
0
                neg = ((exp[++y] == '^') && (exp[y + 1] != ']'));
244
0
                if (neg)
245
0
                    ++y;
246
0
                i = y;
247
0
                start = (unsigned char)(exp[i++]);
248
0
                if (start == '\\')
249
0
                    start = (unsigned char)(exp[i++]);
250
0
                if (isalnum(start) && exp[i++] == '-') {
251
0
                    end = (unsigned char)(exp[i++]);
252
0
                    if (end == '\\')
253
0
                        end = (unsigned char)(exp[i++]);
254
0
                }
255
0
                if (isalnum(end) && exp[i] == ']') {
256
                    /* This is a range form: a-b */
257
0
                    int val = (unsigned char)(str[x]);
258
0
                    if (end < start) { /* swap them */
259
0
                        start ^= end;
260
0
                        end ^= start;
261
0
                        start ^= end;
262
0
                    }
263
0
                    if (case_insensitive && isalpha(val)) {
264
0
                        val = _is_char_in_range(start, end, val);
265
0
                        if (neg == val)
266
0
                            return NOMATCH;
267
0
                    } else if (neg != ((val < start) || (val > end))) {
268
0
                        return NOMATCH;
269
0
                    }
270
0
                    y = i;
271
0
                } else {
272
                    /* Not range form */
273
0
                    int matched = 0;
274
0
                    for (; exp[y] != ']'; y++) {
275
0
                        if (exp[y] == '\\')
276
0
                            ++y;
277
0
                        if (case_insensitive) {
278
0
                            matched |= (toupper((unsigned char)str[x]) ==
279
0
                                        toupper((unsigned char)exp[y]));
280
0
                        } else {
281
0
                            matched |= (str[x] == exp[y]);
282
0
                        }
283
0
                    }
284
0
                    if (neg == matched)
285
0
                        return NOMATCH;
286
0
                }
287
0
            } break;
288
0
            case '(':
289
0
                if (!exp[y + 1])
290
0
                    return ABORTED;
291
0
                return _handle_union(&str[x], &exp[y], case_insensitive, level);
292
0
            case '?':
293
0
                break;
294
0
            case '|':
295
0
            case ']':
296
0
            case ')':
297
0
                return ABORTED;
298
0
            case '\\':
299
0
                ++y;
300
            /* fall through */
301
0
            default:
302
0
                if (case_insensitive) {
303
0
                    if (toupper((unsigned char)str[x]) != toupper((unsigned char)exp[y]))
304
0
                        return NOMATCH;
305
0
                } else {
306
0
                    if (str[x] != exp[y])
307
0
                        return NOMATCH;
308
0
                }
309
0
                break;
310
0
        }
311
0
    }
312
0
    return (str[x] ? NOMATCH : MATCH);
313
0
}
314
315
static int
316
port_RegExpMatch(const char *str, const char *xp, PRBool case_insensitive)
317
0
{
318
0
    char *exp = 0;
319
0
    int x, ret = MATCH;
320
321
0
    if (!strchr(xp, '~'))
322
0
        return _shexp_match(str, xp, case_insensitive, 0);
323
324
0
    exp = PORT_Strdup(xp);
325
0
    if (!exp)
326
0
        return NOMATCH;
327
328
0
    x = _scan_and_copy(exp, '~', '\0', NULL);
329
0
    if (x != ABORTED && exp[x] == '~') {
330
0
        exp[x++] = '\0';
331
0
        ret = _shexp_match(str, &exp[x], case_insensitive, 0);
332
0
        switch (ret) {
333
0
            case NOMATCH:
334
0
                ret = MATCH;
335
0
                break;
336
0
            case MATCH:
337
0
                ret = NOMATCH;
338
0
                break;
339
0
            default:
340
0
                break;
341
0
        }
342
0
    }
343
0
    if (ret == MATCH)
344
0
        ret = _shexp_match(str, exp, case_insensitive, 0);
345
346
0
    PORT_Free(exp);
347
0
    return ret;
348
0
}
349
350
/* ------------------------------ shexp_cmp ------------------------------- */
351
352
int
353
PORT_RegExpSearch(const char *str, const char *exp)
354
0
{
355
0
    switch (PORT_RegExpValid(exp)) {
356
0
        case INVALID_SXP:
357
0
            return -1;
358
0
        case NON_SXP:
359
0
            return (strcmp(exp, str) ? 1 : 0);
360
0
        default:
361
0
            return port_RegExpMatch(str, exp, PR_FALSE);
362
0
    }
363
0
}
364
365
int
366
PORT_RegExpCaseSearch(const char *str, const char *exp)
367
0
{
368
0
    switch (PORT_RegExpValid(exp)) {
369
0
        case INVALID_SXP:
370
0
            return -1;
371
0
        case NON_SXP:
372
0
            return (PORT_Strcasecmp(exp, str) ? 1 : 0);
373
0
        default:
374
0
            return port_RegExpMatch(str, exp, PR_TRUE);
375
0
    }
376
0
}