• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

proftpd / proftpd / 28042938174

23 Jun 2026 05:02PM UTC coverage: 92.464% (-0.6%) from 93.027%
28042938174

push

github

web-flow
It is possible for a client to say that it wishes to use the "sk-ssh-ed25519@openssh.com" algorithm for SSH user publickey authentication, but to _actually_ provide an "ssh-ed25519" public key.

The issue is that, when verifying the type of the public key provided by the client, we do not enforce that the provided key algorith matches the expected public key algorithm.

Thanks to Fabian Wahle of Hap Security for reporting this issue.

48661 of 52627 relevant lines covered (92.46%)

234.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.78
/tests/api/parser.c
1
/*
2
 * ProFTPD - FTP server testsuite
3
 * Copyright (c) 2014-2026 The ProFTPD Project team
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
17
 *
18
 * As a special exemption, The ProFTPD Project team and other respective
19
 * copyright holders give permission to link this program with OpenSSL, and
20
 * distribute the resulting executable, without including the source code for
21
 * OpenSSL in the source distribution.
22
 */
23

24
/* Parser API tests. */
25

26
#include "tests.h"
27

28
static pool *p = NULL;
29

30
static const char *config_path = "/tmp/prt-parser.conf";
31
static const char *config_path2 = "/tmp/prt-parser2.conf";
32
static const char *config_path3 = "/tmp/prt-parser3.d";
33
static const char *config_tmp_path = "/tmp/prt-parser.conf~";
34

35
static void set_up(void) {
11✔
36
  (void) unlink(config_path);
11✔
37
  (void) unlink(config_path2);
11✔
38
  (void) rmdir(config_path3);
11✔
39
  (void) unlink(config_tmp_path);
11✔
40

41
  if (p == NULL) {
11✔
42
    p = permanent_pool = make_sub_pool(NULL);
11✔
43
  }
44

45
  init_fs();
11✔
46
  pr_fs_statcache_set_policy(PR_TUNABLE_FS_STATCACHE_SIZE,
11✔
47
    PR_TUNABLE_FS_STATCACHE_MAX_AGE, 0);
48
  init_stash();
11✔
49
  modules_init();
11✔
50

51
  if (getenv("TEST_VERBOSE") != NULL) {
11✔
52
    pr_trace_set_levels("config", 1, 20);
11✔
53
  }
54
}
11✔
55

56
static void tear_down(void) {
11✔
57
  (void) pr_parser_cleanup();
11✔
58

59
  (void) unlink(config_path);
11✔
60
  (void) unlink(config_path2);
11✔
61
  (void) rmdir(config_path3);
11✔
62
  (void) unlink(config_tmp_path);
11✔
63

64
  if (getenv("TEST_VERBOSE") != NULL) {
11✔
65
    pr_trace_set_levels("config", 0, 0);
11✔
66
  }
67

68
  if (p) {
11✔
69
    destroy_pool(p);
11✔
70
    p = permanent_pool = NULL;
11✔
71
  }
72
}
11✔
73

74
/* Tests */
75

76
START_TEST (parser_prepare_test) {
1✔
77
  int res;
78
  xaset_t *parsed_servers = NULL;
1✔
79

80
  res = pr_parser_prepare(NULL, NULL);
1✔
81
  ck_assert_msg(res == 0, "Failed to handle null arguments: %s", strerror(errno));
1✔
82

83
  res = pr_parser_prepare(p, NULL);
1✔
84
  ck_assert_msg(res == 0, "Failed to handle null parsed_servers: %s",
2✔
85
    strerror(errno));
86

87
  res = pr_parser_prepare(NULL, &parsed_servers);
1✔
88
  ck_assert_msg(res == 0, "Failed to handle null pool: %s", strerror(errno));
2✔
89

90
  (void) pr_parser_cleanup();
1✔
91
}
92
END_TEST
1✔
93

94
START_TEST (parser_cleanup_test) {
1✔
95
  int res;
96
  server_rec *ctx;
97

98
  mark_point();
1✔
99
  res = pr_parser_cleanup();
1✔
100
  ck_assert_msg(res == 0, "Failed to handle unprepared parser: %s",
1✔
101
    strerror(errno));
102

103
  pr_parser_prepare(NULL, NULL);
1✔
104

105
  mark_point();
1✔
106
  ctx = pr_parser_server_ctxt_open("127.0.0.1");
1✔
107
  ck_assert_msg(ctx != NULL, "Failed to open server context: %s",
2✔
108
    strerror(errno));
109

110
  mark_point();
1✔
111
  res = pr_parser_cleanup();
1✔
112
  ck_assert_msg(res < 0, "Failed to handle existing contexts");
2✔
113
  ck_assert_msg(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
2✔
114
    strerror(errno), errno);
115

116
  mark_point();
1✔
117
  (void) pr_parser_server_ctxt_close();
1✔
118
  res = pr_parser_cleanup();
1✔
119
  ck_assert_msg(res == 0, "Failed to cleanup parser: %s", strerror(errno));
2✔
120
}
121
END_TEST
1✔
122

123
START_TEST (parser_server_ctxt_test) {
1✔
124
  server_rec *ctx, *res;
125

126
  ctx = pr_parser_server_ctxt_get();
1✔
127
  ck_assert_msg(ctx == NULL, "Found server context unexpectedly");
1✔
128
  ck_assert_msg(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
2✔
129
    strerror(errno), errno);
130

131
  pr_parser_prepare(p, NULL);
1✔
132

133
  mark_point();
1✔
134
  res = pr_parser_server_ctxt_close();
1✔
135
  ck_assert_msg(res == NULL, "Closed server context unexpectedly");
2✔
136
  ck_assert_msg(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
2✔
137
    strerror(errno), errno);
138

139
  mark_point();
1✔
140
  res = pr_parser_server_ctxt_open("127.0.0.1");
1✔
141
  ck_assert_msg(res != NULL, "Failed to open server context: %s",
2✔
142
    strerror(errno));
143

144
  mark_point();
1✔
145
  ctx = pr_parser_server_ctxt_get();
1✔
146
  ck_assert_msg(ctx != NULL, "Failed to get current server context: %s",
2✔
147
    strerror(errno));
148
  ck_assert_msg(ctx == res, "Expected server context %p, got %p", res, ctx);
2✔
149

150
  mark_point();
1✔
151
  (void) pr_parser_server_ctxt_close();
1✔
152
  (void) pr_parser_cleanup();
1✔
153
}
154
END_TEST
1✔
155

156
START_TEST (parser_server_ctxt_push_test) {
1✔
157
  int res;
158
  server_rec *ctx, *ctx2;
159

160
  res = pr_parser_server_ctxt_push(NULL);
1✔
161
  ck_assert_msg(res < 0, "Failed to handle null argument");
1✔
162
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
163
    strerror(errno), errno);
164

165
  ctx = pcalloc(p, sizeof(server_rec));
1✔
166

167
  mark_point();
1✔
168
  res = pr_parser_server_ctxt_push(ctx);
1✔
169
  ck_assert_msg(res < 0, "Failed to handle unprepared parser");
2✔
170
  ck_assert_msg(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
2✔
171
    strerror(errno), errno);
172

173
  pr_parser_prepare(p, NULL);
1✔
174

175
  mark_point();
1✔
176
  res = pr_parser_server_ctxt_push(ctx);
1✔
177
  ck_assert_msg(res == 0, "Failed to push server rec: %s", strerror(errno));
2✔
178

179
  mark_point();
1✔
180
  ctx2 = pr_parser_server_ctxt_get();
1✔
181
  ck_assert_msg(ctx2 != NULL, "Failed to get current server context: %s",
2✔
182
    strerror(errno));
183
  ck_assert_msg(ctx2 == ctx, "Expected server context %p, got %p", ctx, ctx2);
2✔
184

185
  (void) pr_parser_server_ctxt_close();
1✔
186
  (void) pr_parser_cleanup();
1✔
187
}
188
END_TEST
1✔
189

190
START_TEST (parser_config_ctxt_test) {
1✔
191
  int is_empty = FALSE;
1✔
192
  config_rec *ctx, *res;
193

194
  ctx = pr_parser_config_ctxt_get();
1✔
195
  ck_assert_msg(ctx == NULL, "Found config context unexpectedly");
1✔
196
  ck_assert_msg(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
2✔
197
    strerror(errno), errno);
198

199
  pr_parser_prepare(p, NULL);
1✔
200
  pr_parser_server_ctxt_open("127.0.0.1");
1✔
201

202
  res = pr_parser_config_ctxt_open(NULL);
1✔
203
  ck_assert_msg(res == NULL, "Failed to handle null arguments");
2✔
204
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
205
    strerror(errno), errno);
206

207
  mark_point();
1✔
208
  res = pr_parser_config_ctxt_open("<TestSuite>");
1✔
209
  ck_assert_msg(res != NULL, "Failed to open config context: %s",
2✔
210
    strerror(errno));
211

212
  mark_point();
1✔
213
  ctx = pr_parser_config_ctxt_get();
1✔
214
  ck_assert_msg(ctx != NULL, "Failed to get current config context: %s",
2✔
215
    strerror(errno));
216
  ck_assert_msg(ctx == res, "Expected config context %p, got %p", res, ctx);
2✔
217

218
  mark_point();
1✔
219
  (void) pr_parser_config_ctxt_close(&is_empty);
1✔
220
  ck_assert_msg(is_empty == TRUE, "Expected config context to be empty");
2✔
221

222
  mark_point();
1✔
223
  res = pr_parser_config_ctxt_open("<Global>");
1✔
224
  ck_assert_msg(res != NULL, "Failed to open config context: %s",
2✔
225
    strerror(errno));
226
  (void) pr_parser_config_ctxt_close(&is_empty);
1✔
227
  ck_assert_msg(is_empty == TRUE, "Expected config context to be empty");
2✔
228

229
  (void) pr_parser_server_ctxt_close();
1✔
230
  (void) pr_parser_cleanup();
1✔
231
}
232
END_TEST
1✔
233

234
START_TEST (parser_config_ctxt_push_test) {
1✔
235
  int res;
236
  config_rec *ctx, *ctx2;
237

238
  res = pr_parser_config_ctxt_push(NULL);
1✔
239
  ck_assert_msg(res < 0, "Failed to handle null argument");
1✔
240
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
241
    strerror(errno), errno);
242

243
  ctx = pcalloc(p, sizeof(config_rec));
1✔
244

245
  mark_point();
1✔
246
  res = pr_parser_config_ctxt_push(ctx);
1✔
247
  ck_assert_msg(res < 0, "Failed to handle unprepared parser");
2✔
248
  ck_assert_msg(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
2✔
249
    strerror(errno), errno);
250

251
  pr_parser_prepare(p, NULL);
1✔
252

253
  mark_point();
1✔
254
  res = pr_parser_config_ctxt_push(ctx);
1✔
255
  ck_assert_msg(res == 0, "Failed to push config rec: %s", strerror(errno));
2✔
256

257
  mark_point();
1✔
258
  ctx2 = pr_parser_config_ctxt_get();
1✔
259
  ck_assert_msg(ctx2 != NULL, "Failed to get current config context: %s",
2✔
260
    strerror(errno));
261
  ck_assert_msg(ctx2 == ctx, "Expected config context %p, got %p", ctx, ctx2);
2✔
262

263
  (void) pr_parser_config_ctxt_close(NULL);
1✔
264
  (void) pr_parser_cleanup();
1✔
265
}
266
END_TEST
1✔
267

268
START_TEST (parser_get_lineno_test) {
1✔
269
  unsigned int res;
270

271
  res = pr_parser_get_lineno();
1✔
272
  ck_assert_msg(res == 0, "Expected 0, got %u", res);
1✔
273

274
  res = pr_parser_get_lineno();
1✔
275
  ck_assert_msg(res == 0, "Expected 0, got %u", res);
2✔
276
}
277
END_TEST
1✔
278

279
START_TEST (parser_read_line_test) {
1✔
280
  char *res;
281

282
  res = pr_parser_read_line(NULL, 0);
1✔
283
  ck_assert_msg(res == NULL, "Failed to handle null arguments");
1✔
284
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
285
    strerror(errno), errno);
286
}
287
END_TEST
1✔
288

289
START_TEST (parser_parse_line_test) {
1✔
290
  cmd_rec *cmd;
291
  char *text;
292
  unsigned int lineno;
293

294
  mark_point();
1✔
295
  cmd = pr_parser_parse_line(NULL, NULL, 0);
1✔
296
  ck_assert_msg(cmd == NULL, "Failed to handle null arguments");
1✔
297
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
298
    strerror(errno), errno);
299

300
  mark_point();
1✔
301
  cmd = pr_parser_parse_line(p, NULL, 0);
1✔
302
  ck_assert_msg(cmd == NULL, "Failed to handle null input");
2✔
303
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
304
    strerror(errno), errno);
305

306
  cmd = pr_parser_parse_line(p, "", 0);
1✔
307
  ck_assert_msg(cmd == NULL, "Failed to handle empty input");
2✔
308
  ck_assert_msg(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
2✔
309
    strerror(errno), errno);
310

311
  pr_parser_prepare(p, NULL);
1✔
312
  pr_parser_server_ctxt_open("127.0.0.1");
1✔
313

314
  text = pstrdup(p, "FooBar");
1✔
315
  cmd = pr_parser_parse_line(p, text, 0);
1✔
316
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
317
    strerror(errno));
318
  ck_assert_msg(cmd->argc == 1, "Expected 1, got %d", cmd->argc);
2✔
319
  ck_assert_msg(strcmp(cmd->argv[0], text) == 0,
2✔
320
    "Expected '%s', got '%s'", text, (char *) cmd->argv[0]);
321
  lineno = pr_parser_get_lineno();
1✔
322
  ck_assert_msg(lineno != 1, "Expected lineno 1, got %u", lineno);
2✔
323

324
  text = pstrdup(p, "FooBar baz quxx");
1✔
325
  cmd = pr_parser_parse_line(p, text, 0);
1✔
326
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
327
    strerror(errno));
328
  ck_assert_msg(cmd->argc == 3, "Expected 3, got %d", cmd->argc);
2✔
329
  ck_assert_msg(strcmp(cmd->argv[0], "FooBar") == 0,
2✔
330
    "Expected 'FooBar', got '%s'", (char *) cmd->argv[0]);
331
  ck_assert_msg(strcmp(cmd->arg, "baz quxx") == 0,
2✔
332
    "Expected 'baz quxx', got '%s'", cmd->arg);
333
  lineno = pr_parser_get_lineno();
1✔
334
  ck_assert_msg(lineno != 2, "Expected lineno 2, got %u", lineno);
2✔
335

336
  /* Deliberately omit the trailing '}', to test our handling of malformed
337
   * inputs.
338
   */
339
  mark_point();
1✔
340
  text = pstrdup(p, "BarBaz %{env:FOO_TEST");
1✔
341
  cmd = pr_parser_parse_line(p, text, 0);
1✔
342
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
343
    strerror(errno));
344
  ck_assert_msg(cmd->argc == 2, "Expected 2, got %d", cmd->argc);
2✔
345
  ck_assert_msg(strcmp(cmd->argv[0], "BarBaz") == 0,
2✔
346
    "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]);
347
  ck_assert_msg(strcmp(cmd->arg, "%{env:FOO_TEST") == 0,
2✔
348
    "Expected '%s', got '%s'", "%{env:FOO_TEST", cmd->arg);
349
  lineno = pr_parser_get_lineno();
1✔
350
  ck_assert_msg(lineno != 3, "Expected lineno 3, got %u", lineno);
2✔
351

352
  mark_point();
1✔
353
  pr_env_set(p, "FOO_TEST", "BAR");
1✔
354
  text = pstrdup(p, "BarBaz %{env:FOO_TEST}");
1✔
355
  cmd = pr_parser_parse_line(p, text, 0);
1✔
356
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
357
    strerror(errno));
358
  ck_assert_msg(cmd->argc == 2, "Expected 2, got %d", cmd->argc);
2✔
359
  ck_assert_msg(strcmp(cmd->argv[0], "BarBaz") == 0,
2✔
360
    "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]);
361
  ck_assert_msg(strcmp(cmd->arg, "BAR") == 0,
2✔
362
    "Expected 'BAR', got '%s'", cmd->arg);
363
  lineno = pr_parser_get_lineno();
1✔
364
  ck_assert_msg(lineno != 3, "Expected lineno 3, got %u", lineno);
2✔
365

366
  /* This time, without the requested environment variable present. */
367
  pr_env_unset(p, "FOO_TEST");
1✔
368
  cmd = pr_parser_parse_line(p, text, 0);
1✔
369
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
370
    strerror(errno));
371
  ck_assert_msg(cmd->argc == 1, "Expected 1, got %d", cmd->argc);
2✔
372
  ck_assert_msg(strcmp(cmd->argv[0], "BarBaz") == 0,
2✔
373
    "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]);
374
  ck_assert_msg(strcmp(cmd->arg, "") == 0, "Expected '', got '%s'", cmd->arg);
2✔
375
  lineno = pr_parser_get_lineno();
1✔
376
  ck_assert_msg(lineno != 3, "Expected lineno 3, got %u", lineno);
2✔
377

378
  /* This time, with a single word containing multiple environment variables
379
   * (Issue #507).
380
   */
381
  pr_env_set(p, "FOO_TEST", "Foo");
1✔
382
  pr_env_set(p, "BAR_TEST", "baR");
1✔
383
  text = pstrdup(p, "BarBaz %{env:FOO_TEST}@%{env:BAR_TEST}");
1✔
384
  cmd = pr_parser_parse_line(p, text, 0);
1✔
385
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
386
    strerror(errno));
387
  ck_assert_msg(cmd->argc == 2, "Expected 2, got %d", cmd->argc);
2✔
388
  ck_assert_msg(strcmp(cmd->argv[0], "BarBaz") == 0,
2✔
389
    "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]);
390
  ck_assert_msg(strcmp(cmd->arg, "Foo@baR") == 0,
2✔
391
    "Expected 'Foo@baR', got '%s'", cmd->arg);
392
  lineno = pr_parser_get_lineno();
1✔
393
  ck_assert_msg(lineno != 3, "Expected lineno 3, got %u", lineno);
2✔
394

395
  /* This time, with a single word containing multiple environment variables
396
   * where one of the variables is NOT present (Issue #857).
397
   */
398
  pr_env_set(p, "FOO_TEST", "Foo");
1✔
399
  text = pstrdup(p, "BarBaz %{env:FOO_TEST}@%{env:BAZ_TEST}");
1✔
400
  cmd = pr_parser_parse_line(p, text, 0);
1✔
401
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
402
    strerror(errno));
403
  ck_assert_msg(cmd->argc == 2, "Expected 2, got %d", cmd->argc);
2✔
404
  ck_assert_msg(strcmp(cmd->argv[0], "BarBaz") == 0,
2✔
405
    "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]);
406
  ck_assert_msg(strcmp(cmd->arg, "Foo@") == 0,
2✔
407
    "Expected 'Foo@', got '%s'", cmd->arg);
408
  lineno = pr_parser_get_lineno();
1✔
409
  ck_assert_msg(lineno != 3, "Expected lineno 3, got %u", lineno);
2✔
410

411
  text = pstrdup(p, "<FooBar baz>");
1✔
412
  cmd = pr_parser_parse_line(p, text, 0);
1✔
413
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
414
    strerror(errno));
415
  ck_assert_msg(cmd->argc == 2, "Expected 2, got %d", cmd->argc);
2✔
416
  ck_assert_msg(strcmp(cmd->argv[0], "<FooBar>") == 0,
2✔
417
    "Expected '<FooBar>', got '%s'", (char *) cmd->argv[0]);
418
  lineno = pr_parser_get_lineno();
1✔
419
  ck_assert_msg(lineno != 5, "Expected lineno 5, got %u", lineno);
2✔
420

421
  /* This time, with a single environment variable containing multiple words
422
   * (Issue #2002).
423
   */
424
  mark_point();
1✔
425
  pr_env_set(p, "FOO_TEST", "Foo bAr BAZ");
1✔
426
  text = pstrdup(p, "BarBaz %{env:FOO_TEST}");
1✔
427
  cmd = pr_parser_parse_line(p, text, 0);
1✔
428
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
429
    strerror(errno));
430
  ck_assert_msg(cmd->argc == 4, "Expected 4, got %d", cmd->argc);
2✔
431
  ck_assert_msg(strcmp(cmd->argv[0], "BarBaz") == 0,
2✔
432
    "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]);
433
  ck_assert_msg(strcmp(cmd->arg, "Foo bAr BAZ") == 0,
2✔
434
    "Expected 'Foo bAr BAZ', got '%s'", cmd->arg);
435
  lineno = pr_parser_get_lineno();
1✔
436
  ck_assert_msg(lineno != 6, "Expected lineno 6, got %u", lineno);
2✔
437

438
  mark_point();
1✔
439
  (void) pr_parser_server_ctxt_close();
1✔
440
  (void) pr_parser_cleanup();
1✔
441
}
442
END_TEST
1✔
443

444
MODRET parser_set_testsuite_enabled(cmd_rec *cmd) {
2✔
445
  return PR_HANDLED(cmd);
2✔
446
}
447

448
MODRET parser_set_testsuite_engine(cmd_rec *cmd) {
6✔
449
  return PR_HANDLED(cmd);
6✔
450
}
451

452
static module parser_module;
453

454
static conftable parser_conftab[] = {
455
  { "TestSuiteEnabled",        parser_set_testsuite_enabled, NULL },
456
  { "TestSuiteEngine",        parser_set_testsuite_engine, NULL },
457
  { NULL },
458
};
459

460
static int load_parser_module(void) {
2✔
461
  /* Load the module's config handlers. */
462
  memset(&parser_module, 0, sizeof(parser_module));
2✔
463
  parser_module.name = "parser";
2✔
464
  parser_module.conftable = parser_conftab;
2✔
465

466
  return pr_module_load_conftab(&parser_module);
2✔
467
}
468

469
START_TEST (parse_config_path_test) {
1✔
470
  int res;
471
  char *text;
472
  const char *path;
473
  struct stat st;
474
  unsigned long include_opts;
475
  pr_fh_t *fh;
476

477
  (void) pr_parser_cleanup();
1✔
478

479
  mark_point();
1✔
480
  res = parse_config_path(NULL, NULL);
1✔
481
  ck_assert_msg(res < 0, "Failed to handle null pool");
1✔
482
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
483
    strerror(errno), errno);
484

485
  mark_point();
1✔
486
  res = parse_config_path2(p, NULL, 0);
1✔
487
  ck_assert_msg(res < 0, "Failed to handle null path");
2✔
488
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
489
    strerror(errno), errno);
490

491
  mark_point();
1✔
492
  path = "foo";
1✔
493
  ck_assert_msg(res < 0, "Failed to handle relative path");
1✔
494
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
495
    strerror(errno), errno);
496

497
  mark_point();
1✔
498
  res = parse_config_path2(p, path, 1024);
1✔
499
  ck_assert_msg(res < 0, "Failed to handle excessive depth");
2✔
500
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
501
    strerror(errno), errno);
502

503
  mark_point();
1✔
504
  res = parse_config_path2(p, path, 0);
1✔
505
  ck_assert_msg(res < 0, "Failed to handle invalid path");
2✔
506
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
507
    strerror(errno), errno);
508

509
  mark_point();
1✔
510

511
  /* Note that `/tmp/` may be a large/wide directory on some systems; we
512
   * thus make a more predictable directory for our testing.
513
   */
514
  res = mkdir(config_path3, 0775);
1✔
515
  ck_assert_msg(res == 0, "Failed to mkdir '%s': %s", config_path3,
2✔
516
    strerror(errno));
517

518
  path = config_path3;
1✔
519
  res = lstat(path, &st);
1✔
520
  ck_assert_msg(res == 0, "Failed lstat(2) on '%s': %s", path, strerror(errno));
2✔
521

522
  mark_point();
1✔
523
  res = parse_config_path2(p, path, 0);
1✔
524
  if (S_ISLNK(st.st_mode)) {
1✔
525
    ck_assert_msg(res < 0, "Failed to handle uninitialized parser");
×
526
    ck_assert_msg(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
×
527
      strerror(errno), errno);
528

529
  } else if (S_ISDIR(st.st_mode)) {
1✔
530
    ck_assert_msg(res == 0, "Failed to handle empty directory");
1✔
531
  }
532

533
  mark_point();
1✔
534
  pr_parser_prepare(p, NULL);
1✔
535
  pr_parser_server_ctxt_open("127.0.0.1");
1✔
536

537
  res = parse_config_path2(p, path, 0);
1✔
538
  if (S_ISLNK(st.st_mode)) {
1✔
539
    ck_assert_msg(res < 0, "Failed to handle directory-only path");
×
540
    ck_assert_msg(errno == EISDIR, "Expected EISDIR (%d), got %s (%d)", EISDIR,
×
541
      strerror(errno), errno);
542

543
  } else if (S_ISDIR(st.st_mode)) {
1✔
544
    ck_assert_msg(res == 0, "Failed to handle empty directory");
1✔
545
  }
546

547
  res = rmdir(config_path3);
1✔
548
  ck_assert_msg(res == 0, "Failed to rmdir '%s': %s", config_path3,
1✔
549
    strerror(errno));
550

551
  mark_point();
1✔
552
  path = config_path;
1✔
553
  res = parse_config_path2(p, path, 0);
1✔
554
  ck_assert_msg(res < 0, "Failed to handle nonexistent file");
2✔
555
  ck_assert_msg(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
2✔
556
    strerror(errno), errno);
557

558
  include_opts = pr_parser_set_include_opts(PR_PARSER_INCLUDE_OPT_IGNORE_WILDCARDS);
1✔
559
  mark_point();
1✔
560
  path = "/tmp*/foo.conf";
1✔
561
  res = parse_config_path2(p, path, 0);
1✔
562
  ck_assert_msg(res < 0, "Failed to handle directory-only path");
2✔
563
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
564
    strerror(errno), errno);
565
  (void) pr_parser_set_include_opts(include_opts);
1✔
566

567
  /* On Mac, `/tmp` is a symlink.  And currently, parse_config_path() does
568
   * not allow following of symlinked directories.  So this MIGHT fail, if
569
   * we're on a Mac.
570
   */
571
  res = lstat("/tmp", &st);
1✔
572
  ck_assert_msg(res == 0, "Failed lstat(2) on '/tmp': %s", strerror(errno));
2✔
573

574
  mark_point();
1✔
575
  path = "/tmp/prt*foo*bar*.conf";
1✔
576
  res = parse_config_path2(p, path, 0);
1✔
577

578
  if (S_ISLNK(st.st_mode)) {
1✔
579
    ck_assert_msg(res < 0, "Failed to handle nonexistent file");
×
580
    ck_assert_msg(errno == ENOTDIR, "Expected ENOTDIR (%d), got %s (%d)", ENOTDIR,
×
581
      strerror(errno), errno);
582

583
    include_opts = pr_parser_set_include_opts(PR_PARSER_INCLUDE_OPT_ALLOW_SYMLINKS);
×
584

585
    /* By default, we ignore the case where there are no matching files. */
586
    res = parse_config_path2(p, path, 0);
×
587
    ck_assert_msg(res == 0, "Failed to handle nonexistent file: %s",
×
588
      strerror(errno));
589

590
    (void) pr_parser_set_include_opts(include_opts);
×
591

592
  } else {
593
    /* By default, we ignore the case where there are no matching files. */
594
    ck_assert_msg(res == 0, "Failed to handle nonexistent file: %s",
1✔
595
      strerror(errno));
596
  }
597

598
  /* Load the module's config handlers. */
599
  res = load_parser_module();
1✔
600
  ck_assert_msg(res == 0, "Failed to load module conftab: %s", strerror(errno));
1✔
601

602
  include_opts = pr_parser_set_include_opts(PR_PARSER_INCLUDE_OPT_ALLOW_SYMLINKS);
1✔
603

604
  /* Parse single file. */
605
  path = config_path;
1✔
606
  fh = pr_fsio_open(path, O_CREAT|O_EXCL|O_WRONLY);
1✔
607
  ck_assert_msg(fh != NULL, "Failed to open '%s': %s", path, strerror(errno));
2✔
608

609
  text = "TestSuiteEngine on\r\n";
1✔
610
  res = pr_fsio_write(fh, text, strlen(text));
1✔
611
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
612

613
  res = pr_fsio_close(fh);
1✔
614
  ck_assert_msg(res == 0, "Failed to write '%s': %s", path, strerror(errno));
2✔
615

616
  mark_point();
1✔
617
  res = parse_config_path2(p, path, 0);
1✔
618
  ck_assert_msg(res >= 0, "Failed to parse '%s': %s", path, strerror(errno));
2✔
619

620
  path = "/tmp/prt*.conf";
1✔
621
  res = parse_config_path2(p, path, 0);
1✔
622
  ck_assert_msg(res >= 0, "Failed to parse '%s': %s", path, strerror(errno));
2✔
623

624
  (void) pr_parser_set_include_opts(PR_PARSER_INCLUDE_OPT_ALLOW_SYMLINKS|PR_PARSER_INCLUDE_OPT_IGNORE_TMP_FILES|PR_PARSER_INCLUDE_OPT_IGNORE_WILDCARDS);
1✔
625

626
  path = config_tmp_path;
1✔
627
  fh = pr_fsio_open(path, O_CREAT|O_EXCL|O_WRONLY);
1✔
628
  ck_assert_msg(fh != NULL, "Failed to open '%s': %s", path, strerror(errno));
2✔
629

630
  text = "TestSuiteEnabled off\r\n";
1✔
631
  res = pr_fsio_write(fh, text, strlen(text));
1✔
632
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
633

634
  res = pr_fsio_close(fh);
1✔
635
  ck_assert_msg(res == 0, "Failed to write '%s': %s", path, strerror(errno));
2✔
636

637
  mark_point();
1✔
638
  path = "/tmp/prt*.conf*";
1✔
639
  res = parse_config_path2(p, path, 0);
1✔
640
  ck_assert_msg(res >= 0, "Failed to parse '%s': %s", path, strerror(errno));
2✔
641

642
  mark_point();
1✔
643
  path = "/t*p/prt*.conf*";
1✔
644
  res = parse_config_path2(p, path, 0);
1✔
645
  ck_assert_msg(res < 0, "Failed to handle wildcard path '%s'", path);
2✔
646
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
647
    strerror(errno), errno);
648

649
  (void) pr_parser_set_include_opts(PR_PARSER_INCLUDE_OPT_ALLOW_SYMLINKS|PR_PARSER_INCLUDE_OPT_IGNORE_TMP_FILES);
1✔
650

651
  mark_point();
1✔
652
  path = "/t*p/prt*.conf*";
1✔
653
  res = parse_config_path2(p, path, 0);
1✔
654
  ck_assert_msg(res >= 0, "Failed to parse '%s': %s", path, strerror(errno));
2✔
655

656
  (void) pr_parser_server_ctxt_close();
1✔
657
  (void) pr_parser_cleanup();
1✔
658
  (void) pr_module_unload(&parser_module);
1✔
659
  (void) pr_parser_set_include_opts(include_opts);
1✔
660
}
661
END_TEST
1✔
662

663
START_TEST (parser_parse_file_test) {
1✔
664
  int res;
665
  pr_fh_t *fh;
666
  char *text;
667

668
  (void) unlink(config_path);
1✔
669

670
  mark_point();
1✔
671
  res = pr_parser_parse_file(NULL, NULL, NULL, 0);
1✔
672
  ck_assert_msg(res < 0, "Failed to handle null arguments");
1✔
673
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
674
    strerror(errno), errno);
675

676
  mark_point();
1✔
677
  res = pr_parser_parse_file(p, config_path, NULL, 0);
1✔
678
  ck_assert_msg(res < 0, "Failed to handle invalid file");
2✔
679
  ck_assert_msg(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
2✔
680
    strerror(errno), errno);
681

682
  pr_parser_prepare(p, NULL);
1✔
683
  pr_parser_server_ctxt_open("127.0.0.1");
1✔
684

685
  mark_point();
1✔
686
  res = pr_parser_parse_file(p, config_path, NULL, 0);
1✔
687
  ck_assert_msg(res < 0, "Failed to handle invalid file");
2✔
688
  ck_assert_msg(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
2✔
689
    strerror(errno), errno);
690

691
  mark_point();
1✔
692
  res = pr_parser_parse_file(p, "/tmp", NULL, 0);
1✔
693
  ck_assert_msg(res < 0, "Failed to handle directory");
2✔
694
  ck_assert_msg(errno == EISDIR, "Expected EISDIR (%d), got %s (%d)", EISDIR,
2✔
695
    strerror(errno), errno);
696

697
  fh = pr_fsio_open(config_path, O_CREAT|O_EXCL|O_WRONLY);
1✔
698
  ck_assert_msg(fh != NULL, "Failed to open '%s': %s", config_path,
2✔
699
    strerror(errno));
700

701
  text = "TestSuiteEngine on\r\n";
1✔
702
  res = pr_fsio_write(fh, text, strlen(text));
1✔
703
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
704

705
  text = "TestSuiteEnabled on\n";
1✔
706
  res = pr_fsio_write(fh, text, strlen(text));
1✔
707
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
708

709
  text = "Include ";
1✔
710
  res = pr_fsio_write(fh, text, strlen(text));
1✔
711
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
712

713
  text = (char *) config_path2;
1✔
714
  res = pr_fsio_write(fh, text, strlen(text));
1✔
715
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
716

717
  text = "\n";
1✔
718
  res = pr_fsio_write(fh, text, strlen(text));
1✔
719
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
720

721
  res = pr_fsio_close(fh);
1✔
722
  ck_assert_msg(res == 0, "Failed to write '%s': %s", config_path,
2✔
723
    strerror(errno));
724

725
  fh = pr_fsio_open(config_path2, O_CREAT|O_EXCL|O_WRONLY);
1✔
726
  ck_assert_msg(fh != NULL, "Failed to open '%s': %s", config_path2,
2✔
727
    strerror(errno));
728

729
  text = "TestSuiteOptions Bebugging\n";
1✔
730
  res = pr_fsio_write(fh, text, strlen(text));
1✔
731
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
732

733
  res = pr_fsio_close(fh);
1✔
734
  ck_assert_msg(res == 0, "Failed to write '%s': %s", config_path2,
2✔
735
    strerror(errno));
736

737
  mark_point();
1✔
738

739
  /* Load the module's config handlers. */
740
  res = load_parser_module();
1✔
741
  ck_assert_msg(res == 0, "Failed to load module conftab: %s", strerror(errno));
2✔
742

743
  res = pr_parser_parse_file(p, config_path, NULL, PR_PARSER_FL_DYNAMIC_CONFIG);
1✔
744
  ck_assert_msg(res == 0, "Failed to parse '%s': %s", config_path,
2✔
745
    strerror(errno));
746

747
  res = pr_parser_parse_file(p, config_path, NULL, 0);
1✔
748
  ck_assert_msg(res < 0, "Parsed '%s' unexpectedly", config_path);
2✔
749
  ck_assert_msg(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
2✔
750
    strerror(errno), errno);
751

752
  (void) pr_parser_server_ctxt_close();
1✔
753
  (void) pr_parser_cleanup();
1✔
754
  (void) pr_module_unload(&parser_module);
1✔
755
  (void) pr_fsio_unlink(config_path);
1✔
756
  (void) pr_fsio_unlink(config_path2);
1✔
757
}
758
END_TEST
1✔
759

760
Suite *tests_get_parser_suite(void) {
916✔
761
  Suite *suite;
762
  TCase *testcase;
763

764
  suite = suite_create("parser");
916✔
765

766
  testcase = tcase_create("base");
916✔
767
  tcase_add_checked_fixture(testcase, set_up, tear_down);
916✔
768

769
  tcase_add_test(testcase, parser_prepare_test);
916✔
770
  tcase_add_test(testcase, parser_cleanup_test);
916✔
771
  tcase_add_test(testcase, parser_server_ctxt_test);
916✔
772
  tcase_add_test(testcase, parser_server_ctxt_push_test);
916✔
773
  tcase_add_test(testcase, parser_config_ctxt_test);
916✔
774
  tcase_add_test(testcase, parser_config_ctxt_push_test);
916✔
775
  tcase_add_test(testcase, parser_get_lineno_test);
916✔
776
  tcase_add_test(testcase, parser_read_line_test);
916✔
777
  tcase_add_test(testcase, parser_parse_line_test);
916✔
778
  tcase_add_test(testcase, parse_config_path_test);
916✔
779
  tcase_add_test(testcase, parser_parse_file_test);
916✔
780

781
  /* Some of these tests may take a little longer. */
782
  tcase_set_timeout(testcase, 30);
916✔
783

784
  suite_add_tcase(suite, testcase);
916✔
785
  return suite;
916✔
786
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc