source: src/router/proftpd/src/regexp.c @ 17876

Last change on this file since 17876 was 17876, checked in by BrainSlayer, 20 months ago

update proftp

File size: 12.0 KB
Line 
1/*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 1997, 1998 Public Flood Software
4 * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
5 * Copyright (c) 2001-2011 The ProFTPD Project team
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
20 *
21 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
22 * and other respective copyright holders give permission to link this program
23 * with OpenSSL, and distribute the resulting executable, without including
24 * the source code for OpenSSL in the source distribution.
25 */
26
27/* Regex management code
28 * $Id: regexp.c,v 1.19 2011/08/02 17:10:47 castaglia Exp $
29 */
30
31#include "conf.h"
32
33#ifdef PR_USE_REGEX
34
35#ifdef PR_USE_PCRE
36struct regexp_rec {
37  pool *regex_pool;
38
39  /* Owning module */
40  module *m;
41
42  /* Copy of the original regular expression pattern */
43  const char *pattern;
44
45  /* For callers wishing to use POSIX REs */
46  regex_t *re;
47
48  /* For calles wishing to use PCRE REs */
49  pcre *pcre;
50  pcre_extra *pcre_extra;
51
52  const char *pcre_errstr;
53};
54
55static unsigned long pcre_match_limit = 0;
56static unsigned long pcre_match_limit_recursion = 0;
57
58#else /* !PR_USE_PCRE */
59struct regexp_rec {
60  pool *regex_pool;
61
62  /* Owning module */
63  module *m;
64
65  /* Copy of the original regular expression pattern */
66  const char *pattern;
67
68  /* For callers wishing to use POSIX REs */
69  regex_t *re;
70};
71
72#endif /* PR_USE_PCRE */
73
74static pool *regexp_pool = NULL;
75static array_header *regexp_list = NULL;
76
77static const char *trace_channel = "regexp";
78
79static void regexp_cleanup(void) {
80  /* Only perform this cleanup if necessary */
81  if (regexp_pool) {
82    register unsigned int i = 0;
83    pr_regex_t **pres = (pr_regex_t **) regexp_list->elts;
84
85    for (i = 0; i < regexp_list->nelts; i++) {
86      if (pres[i]) {
87
88#ifdef PR_USE_PCRE
89        if (pres[i]->pcre != NULL) {
90          /* This frees memory associated with this pointer by regcomp(3). */
91          pcre_free(pres[i]->pcre);
92          pres[i]->pcre = NULL;
93        }
94#endif /* PR_USE_PCRE */
95
96        if (pres[i]->re != NULL) {
97          /* This frees memory associated with this pointer by regcomp(3). */
98          regfree(pres[i]->re);
99          pres[i]->re = NULL;
100        }
101
102        /* This frees the memory allocated for the object itself. */
103        destroy_pool(pres[i]->regex_pool);
104      }
105    }
106
107    destroy_pool(regexp_pool);
108    regexp_pool = NULL;
109    regexp_list = NULL;
110  }
111}
112
113static void regexp_exit_ev(const void *event_data, void *user_data) {
114  regexp_cleanup();
115  return;
116}
117
118static void regexp_restart_ev(const void *event_data, void *user_data) {
119  regexp_cleanup();
120  return;
121}
122
123pr_regex_t *pr_regexp_alloc(module *m) {
124  pr_regex_t *pre = NULL;
125  pool *re_pool = NULL;
126
127  /* If no regex-tracking list has been allocated, create one.  Register a
128   * cleanup handler for this pool, to free up the data in the list.
129   */
130  if (regexp_pool == NULL) {
131    regexp_pool = make_sub_pool(permanent_pool);
132    pr_pool_tag(regexp_pool, "Regexp Pool");
133    regexp_list = make_array(regexp_pool, 0, sizeof(pr_regex_t *));
134  }
135
136  re_pool = pr_pool_create_sz(regexp_pool, 128);
137  pr_pool_tag(re_pool, "regexp pool");
138
139  pre = pcalloc(re_pool, sizeof(pr_regex_t));
140  pre->regex_pool = re_pool;
141  pre->m = m;
142
143  /* Add this pointer to the array. */
144  *((pr_regex_t **) push_array(regexp_list)) = pre;
145
146  return pre;
147}
148
149void pr_regexp_free(module *m, pr_regex_t *pre) {
150  register unsigned int i = 0;
151  pr_regex_t **pres = NULL;
152
153  if (regexp_list == NULL) {
154    return;
155  }
156
157  pres = (pr_regex_t **) regexp_list->elts;
158
159  for (i = 0; i < regexp_list->nelts; i++) {
160    if ((pre != NULL && pres[i] == pre) ||
161        (m != NULL && pres[i]->m == m)) {
162
163#ifdef PR_USE_PCRE
164      if (pres[i]->pcre != NULL) {
165        /* This frees memory associated with this pointer by regcomp(3). */
166        pcre_free(pres[i]->pcre);
167        pres[i]->pcre = NULL;
168      }
169#endif /* PR_USE_PCRE */
170
171      if (pres[i]->re != NULL) {
172        /* This frees memory associated with this pointer by regcomp(3). */
173        regfree(pres[i]->re);
174        pres[i]->re = NULL;
175      }
176
177      /* This frees the memory allocated for the object itself. */
178      destroy_pool(pres[i]->regex_pool);
179
180      pres[i] = NULL;
181    }
182  }
183}
184
185#ifdef PR_USE_PCRE
186static int regexp_compile_pcre(pr_regex_t *pre, const char *pattern,
187    int flags) {
188  int err_offset;
189
190  if (pre == NULL ||
191      pattern == NULL) {
192    errno = EINVAL;
193    return -1;
194  }
195
196  pr_trace_msg(trace_channel, 9, "compiling pattern '%s' into PCRE regex",
197    pattern);
198  pre->pattern = pstrdup(pre->regex_pool, pattern);
199
200  pre->pcre = pcre_compile(pattern, flags, &(pre->pcre_errstr), &err_offset,
201    NULL);
202
203  if (pre->pcre == NULL) {
204    pr_trace_msg(trace_channel, 4,
205      "error compiling pattern '%s' into PCRE regex: %s", pattern,
206      pre->pcre_errstr);
207    return -1;
208  }
209
210  /* Study the pattern as well, just in case. */
211  pr_trace_msg(trace_channel, 9, "studying pattern '%s' for PCRE extra data",
212    pattern);
213  pre->pcre_extra = pcre_study(pre->pcre, 0, &(pre->pcre_errstr));
214  return 0;
215}
216#endif /* PR_USE_PCRE */
217
218int pr_regexp_compile_posix(pr_regex_t *pre, const char *pattern, int flags) {
219  int res;
220
221  if (pre == NULL ||
222      pattern == NULL) {
223    errno = EINVAL;
224    return -1;
225  }
226
227  if (pre->re != NULL) {
228    regfree(pre->re);
229    pre->re = NULL;
230  }
231
232  pr_trace_msg(trace_channel, 9, "compiling pattern '%s' into POSIX regex",
233    pattern);
234  pre->pattern = pstrdup(pre->regex_pool, pattern);
235
236  pre->re = pcalloc(pre->regex_pool, sizeof(regex_t));
237  res = regcomp(pre->re, pattern, flags);
238
239  return res;
240}
241
242int pr_regexp_compile(pr_regex_t *pre, const char *pattern, int flags) {
243#ifdef PR_USE_PCRE
244  int pcre_flags = 0;
245
246  /* Provide a simple mapping of POSIX regcomp(3) flags to
247   * PCRE pcre_compile() flags.  The ProFTPD code tends not to use many
248   * of these flags.
249   */
250  if (flags & REG_ICASE) {
251    pcre_flags |= PCRE_CASELESS;
252  }
253
254  return regexp_compile_pcre(pre, pattern, pcre_flags);
255#else
256  return pr_regexp_compile_posix(pre, pattern, flags);
257#endif /* PR_USE_PCRE */
258}
259
260size_t pr_regexp_error(int errcode, const pr_regex_t *pre, char *buf,
261  size_t bufsz) {
262
263  if (pre == NULL ||
264      buf == NULL ||
265      bufsz == 0) {
266    return 0;
267  }
268
269#ifdef PR_USE_PCRE
270  if (pre->pcre_errstr != NULL) {
271    sstrncpy(buf, pre->pcre_errstr, bufsz);
272    return strlen(pre->pcre_errstr) + 1;
273  }
274#endif /* PR_USE_PCRE */
275
276  if (pre->re != NULL) {
277    /* Make sure the given buffer is always zeroed out first. */
278    memset(buf, '\0', bufsz);
279    return regerror(errcode, pre->re, buf, bufsz-1);
280  }
281
282  return 0;
283}
284
285const char *pr_regexp_get_pattern(const pr_regex_t *pre) {
286  if (pre == NULL) {
287    errno = EINVAL;
288    return NULL;
289  }
290
291  if (pre->pattern == NULL) {
292    errno = ENOENT;
293    return NULL;
294  }
295
296  return pre->pattern;
297}
298
299#ifdef PR_USE_PCRE
300static int regexp_exec_pcre(pr_regex_t *pre, const char *str,
301    size_t nmatches, regmatch_t *matches, int flags, unsigned long match_limit,
302    unsigned long match_limit_recursion) {
303  if (pre == NULL ||
304      str == NULL) {
305    errno = EINVAL;
306    return -1;
307  }
308
309  if (pre->pcre != NULL) {
310    int res;
311    size_t str_len;
312
313    str_len = strlen(str);
314
315    /* Use the default match limits, if set and if the caller did not
316     * explicitly provide limits.
317     */
318    if (match_limit == 0) {
319      match_limit = pcre_match_limit;
320    }
321
322    if (match_limit_recursion == 0) {
323      match_limit_recursion = pcre_match_limit_recursion;
324    }
325
326    if (match_limit > 0) {
327      if (pre->pcre_extra == NULL) {
328        pre->pcre_extra = pcalloc(pre->regex_pool, sizeof(pcre_extra));
329      }
330
331      pre->pcre_extra->flags |= PCRE_EXTRA_MATCH_LIMIT;
332      pre->pcre_extra->match_limit = match_limit;
333    }
334
335    if (match_limit_recursion > 0) {
336      if (pre->pcre_extra == NULL) {
337        pre->pcre_extra = pcalloc(pre->regex_pool, sizeof(pcre_extra));
338      }
339
340      pre->pcre_extra->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
341      pre->pcre_extra->match_limit_recursion = match_limit_recursion;
342    }
343
344    pr_trace_msg(trace_channel, 9,
345      "executing PCRE regex '%s' against subject '%s'",
346      pr_regexp_get_pattern(pre), str);
347    res = pcre_exec(pre->pcre, pre->pcre_extra, str, str_len, 0, flags,
348      NULL, 0);
349
350    if (res < 0) {
351      if (pr_trace_get_level(trace_channel) >= 9) {
352        const char *reason = "unknown";
353
354        switch (res) {
355          case PCRE_ERROR_NOMATCH:
356            reason = "subject did not match pattern";
357            break;
358
359          case PCRE_ERROR_NULL:
360            reason = "null regex or subject";
361            break;
362
363          case PCRE_ERROR_BADOPTION:
364            reason = "unsupported options bit";
365            break;
366
367          case PCRE_ERROR_BADMAGIC:
368            reason = "bad magic number in regex";
369            break;
370
371          case PCRE_ERROR_UNKNOWN_OPCODE:
372          case PCRE_ERROR_INTERNAL:
373            reason = "internal PCRE error or corrupted regex";
374            break;
375
376          case PCRE_ERROR_NOMEMORY:
377            reason = "not enough memory for backreferences";
378            break;
379
380          case PCRE_ERROR_MATCHLIMIT:
381            reason = "match limit reached/exceeded";
382            break;
383
384          case PCRE_ERROR_RECURSIONLIMIT:
385            reason = "match limit recursion reached/exceeded";
386            break;
387
388          case PCRE_ERROR_BADUTF8:
389            reason = "invalid UTF8 subject used";
390            break;
391
392          case PCRE_ERROR_PARTIAL:
393            reason = "subject matched only partially; PCRE_PARTIAL flag not used";
394            break;
395        }
396
397        pr_trace_msg(trace_channel, 9,
398          "PCRE regex '%s' failed to match subject '%s': %s",
399          pr_regexp_get_pattern(pre), str, reason);
400
401      } else {
402        pr_trace_msg(trace_channel, 9,
403          "PCRE regex '%s' successfully match subject '%s'",
404          pr_regexp_get_pattern(pre), str);
405      }
406    }
407
408    return res;
409  }
410
411  errno = EINVAL;
412  return -1;
413}
414#endif /* PR_USE_PCRE */
415
416static int regexp_exec_posix(pr_regex_t *pre, const char *str,
417    size_t nmatches, regmatch_t *matches, int flags) {
418
419  pr_trace_msg(trace_channel, 9,
420    "executing POSIX regex '%s' against subject '%s'",
421    pr_regexp_get_pattern(pre), str);
422  return regexec(pre->re, str, nmatches, matches, flags);
423}
424
425int pr_regexp_exec(pr_regex_t *pre, const char *str, size_t nmatches,
426    regmatch_t *matches, int flags, unsigned long match_limit,
427    unsigned long match_limit_recursion) {
428
429  if (pre == NULL ||
430      str == NULL) {
431    errno = EINVAL;
432    return -1;
433  }
434
435#ifdef PR_USE_PCRE
436  if (pre->pcre != NULL) {
437    return regexp_exec_pcre(pre, str, nmatches, matches, flags, match_limit,
438      match_limit_recursion);
439  }
440#endif /* PR_USE_PCRE */
441
442  return regexp_exec_posix(pre, str, nmatches, matches, flags);
443}
444
445int pr_regexp_set_limits(unsigned long match_limit,
446    unsigned long match_limit_recursion) {
447
448#ifdef PR_USE_PCRE
449  pcre_match_limit = match_limit;
450  pcre_match_limit_recursion = match_limit_recursion;
451#endif
452
453  return 0;
454}
455
456void init_regexp(void) {
457
458  /* Register a restart handler for the regexp pool, so that when restarting,
459   * regfree(3) is called on each of the regex_t pointers in a
460   * regex_t-tracking array, thus preventing memory leaks on a long-running
461   * daemon.
462   *
463   * This registration is done here so that it only happens once.
464   */
465  pr_event_register(NULL, "core.restart", regexp_restart_ev, NULL);
466  pr_event_register(NULL, "core.exit", regexp_exit_ev, NULL);
467
468#ifdef PR_USE_PCRE
469  pr_log_debug(DEBUG2, "using PCRE %s", pcre_version());
470#endif /* PR_USE_PCRE */
471}
472
473#endif
Note: See TracBrowser for help on using the repository browser.