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

proftpd / proftpd / 26127302613

19 May 2026 09:51PM UTC coverage: 93.024% (+0.4%) from 92.635%
26127302613

push

github

51329 of 55178 relevant lines covered (93.02%)

215.14 hits per line

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

97.53
/tests/api/response.c
1
/*
2
 * ProFTPD - FTP server testsuite
3
 * Copyright (c) 2011-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
/* Response API tests */
25

26
#include "tests.h"
27

28
extern pr_response_t *resp_list, *resp_err_list;
29

30
static pool *p = NULL;
31

32
static void set_up(void) {
33
  if (p == NULL) {
12✔
34
    p = permanent_pool = make_sub_pool(NULL);
12✔
35
  }
12✔
36

37
  init_netio();
38
  init_inet();
12✔
39

12✔
40
  if (getenv("TEST_VERBOSE") != NULL) {
41
    pr_trace_set_levels("netio", 1, 20);
12✔
42
    pr_trace_set_levels("response", 1, 20);
12✔
43
  }
12✔
44
}
45

12✔
46
static void tear_down(void) {
47
  pr_response_register_handler(NULL);
12✔
48

12✔
49
  if (session.c != NULL) {
50
    pr_inet_close(p, session.c);
12✔
51
    session.c = NULL;
×
52
  }
×
53

54
  pr_unregister_netio(PR_NETIO_STRM_CTRL);
55

12✔
56
  if (getenv("TEST_VERBOSE") != NULL) {
57
    pr_trace_set_levels("netio", 0, 0);
12✔
58
    pr_trace_set_levels("response", 0, 0);
12✔
59
  }
12✔
60

61
  if (p) {
62
    destroy_pool(p);
12✔
63
    p = permanent_pool = NULL;
12✔
64
  }
12✔
65
}
66

12✔
67
START_TEST (response_pool_get_test) {
68
  pool *res;
1✔
69

1✔
70
  res = pr_response_get_pool();
71
  ck_assert_msg(res == NULL, "Response pool not null as expected");
1✔
72
}
1✔
73
END_TEST
1✔
74

75
START_TEST (response_pool_set_test) {
76
  pool *res;
1✔
77

1✔
78
  pr_response_set_pool(p);
79
  res = pr_response_get_pool();
1✔
80
  ck_assert_msg(res == p, "Response pool not %p as expected", p);
1✔
81
}
1✔
82
END_TEST
1✔
83

84
START_TEST (response_add_test) {
85
  int res;
1✔
86
  const char *last_resp_code = NULL, *last_resp_msg = NULL;
1✔
87
  char *resp_code = R_200, *resp_msg = "OK";
1✔
88

1✔
89
  pr_response_set_pool(NULL);
90

1✔
91
  mark_point();
92
  pr_response_add(resp_code, "%s", resp_msg);
1✔
93

1✔
94
  pr_response_set_pool(p);
95

1✔
96
  mark_point();
97
  pr_response_add(NULL, NULL);
1✔
98

1✔
99
  mark_point();
100
  pr_response_add(NULL, "%s", resp_msg);
1✔
101

1✔
102
  mark_point();
103
  pr_response_add(resp_code, "%s", resp_msg);
1✔
104
  pr_response_add(NULL, "%s", resp_msg);
1✔
105

1✔
106
  res = pr_response_get_last(p, &last_resp_code, &last_resp_msg);
107
  ck_assert_msg(res == 0, "Failed to get last values: %d (%s)", errno,
1✔
108
    strerror(errno));
1✔
109

110
  ck_assert_msg(last_resp_code != NULL, "Last response code unexpectedly null");
111
  ck_assert_msg(strcmp(last_resp_code, resp_code) == 0,
1✔
112
    "Expected response code '%s', got '%s'", resp_code, last_resp_code);
1✔
113

114
  ck_assert_msg(last_resp_msg != NULL, "Last response message unexpectedly null");
115
  ck_assert_msg(strcmp(last_resp_msg, resp_msg) == 0,
1✔
116
    "Expected response message '%s', got '%s'", resp_msg, last_resp_msg);
1✔
117
}
118
END_TEST
1✔
119

120
START_TEST (response_add_err_test) {
121
  int res;
1✔
122
  const char *last_resp_code = NULL, *last_resp_msg = NULL;
1✔
123
  char *resp_code = R_450, *resp_msg = "Busy";
1✔
124

1✔
125
  pr_response_set_pool(NULL);
126

1✔
127
  mark_point();
128
  pr_response_add(resp_code, "%s", resp_msg);
1✔
129

1✔
130
  pr_response_set_pool(p);
131

1✔
132
  mark_point();
133
  pr_response_add_err(NULL, NULL);
1✔
134

1✔
135
  mark_point();
136
  pr_response_add_err(resp_code, "%s", resp_msg);
1✔
137

1✔
138
  res = pr_response_get_last(p, &last_resp_code, &last_resp_msg);
139
  ck_assert_msg(res == 0, "Failed to get last values: %d (%s)", errno,
1✔
140
    strerror(errno));
1✔
141

142
  ck_assert_msg(last_resp_code != NULL, "Last response code unexpectedly null");
143
  ck_assert_msg(strcmp(last_resp_code, resp_code) == 0,
1✔
144
    "Expected response code '%s', got '%s'", resp_code, last_resp_code);
1✔
145

146
  ck_assert_msg(last_resp_msg != NULL, "Last response message unexpectedly null");
147
  ck_assert_msg(strcmp(last_resp_msg, resp_msg) == 0,
1✔
148
    "Expected response message '%s', got '%s'", resp_msg, last_resp_msg);
1✔
149
}
150
END_TEST
1✔
151

152
START_TEST (response_get_last_test) {
153
  int res;
1✔
154
  const char *resp_code = NULL, *resp_msg = NULL;
1✔
155

1✔
156
  res = pr_response_get_last(NULL, NULL, NULL);
157
  ck_assert_msg(res == -1, "Failed to handle null pool");
1✔
158
  ck_assert_msg(errno == EINVAL, "Expected errno %d (%s), got %d (%s)",
1✔
159
    EINVAL, strerror(EINVAL), errno, strerror(errno));
1✔
160

161
  res = pr_response_get_last(p, NULL, NULL);
162
  ck_assert_msg(res == -1, "Failed to handle null argument");
1✔
163
  ck_assert_msg(errno == EINVAL, "Expected errno %d (%s), got %d (%s)",
1✔
164
    EINVAL, strerror(EINVAL), errno, strerror(errno));
1✔
165

166
  res = pr_response_get_last(p, &resp_code, &resp_msg);
167
  ck_assert_msg(res == 0, "Failed to get last values: %d (%s)", errno,
1✔
168
    strerror(errno));
1✔
169

170
  ck_assert_msg(resp_code == NULL,
171
    "Last response code not null as expected: %s", resp_code);
1✔
172
  ck_assert_msg(resp_msg == NULL,
173
    "Last response message not null as expected: %s", resp_msg);
1✔
174
}
175
END_TEST
1✔
176

177
START_TEST (response_block_test) {
178
  int res;
1✔
179

1✔
180
  res = pr_response_block(-1);
181
  ck_assert_msg(res == -1, "Failed to handle invalid argument");
1✔
182
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)",
1✔
183
    EINVAL, strerror(errno), errno);
1✔
184

185
  res = pr_response_block(TRUE);
186
  ck_assert_msg(res == 0, "Failed to block responses: %s", strerror(errno));
1✔
187

1✔
188
  res = pr_response_block(FALSE);
189
  ck_assert_msg(res == 0, "Failed to unblock responses: %s", strerror(errno));
1✔
190
}
1✔
191
END_TEST
1✔
192

193
START_TEST (response_blocked_test) {
194
  int res;
1✔
195

1✔
196
  (void) pr_response_block(TRUE);
197
  res = pr_response_blocked();
1✔
198
  ck_assert_msg(res == TRUE, "Failed to detect blocked responses");
1✔
199

1✔
200
  (void) pr_response_block(FALSE);
201
  res = pr_response_blocked();
1✔
202
  ck_assert_msg(res == FALSE, "Failed to detect unblocked responses");
1✔
203
}
1✔
204
END_TEST
1✔
205

206
START_TEST (response_clear_test) {
207
  mark_point();
1✔
208
  pr_response_clear(NULL);
1✔
209

1✔
210
  pr_response_set_pool(p);
211
  pr_response_add(R_200, "%s", "OK");
1✔
212
  pr_response_clear(&resp_list);
1✔
213
}
1✔
214
END_TEST
1✔
215

216
static int response_netio_poll_cb(pr_netio_stream_t *nstrm) {
217
  /* Always return >0, to indicate that we haven't timed out, AND that there
×
218
   * is a writable fd available.
219
   */
220
  return 7;
221
}
×
222

223
static int response_netio_write_cb(pr_netio_stream_t *nstrm, char *buf,
224
    size_t buflen) {
×
225
  return buflen;
226
}
×
227

228
static unsigned int resp_nlines = 0;
229
static char *resp_line = NULL;
230

231
static char *response_handler_cb(pool *cb_pool, const char *fmt, ...) {
232
  char buf[PR_RESPONSE_BUFFER_SIZE] = {'\0'};
6✔
233
  va_list msg;
6✔
234

6✔
235
  va_start(msg, fmt);
236
  vsnprintf(buf, sizeof(buf), fmt, msg);
6✔
237
  va_end(msg);
6✔
238

6✔
239
  buf[sizeof(buf)-1] = '\0';
240

6✔
241
  resp_nlines++;
242
  resp_line = pstrdup(cb_pool, buf);
6✔
243
  return resp_line;
6✔
244
}
6✔
245

246
START_TEST (response_flush_test) {
247
  int res, sockfd = -2;
1✔
248
  conn_t *conn;
1✔
249
  pr_netio_t *netio;
1✔
250

1✔
251
  mark_point();
252
  pr_response_flush(NULL);
1✔
253

1✔
254
  netio = pr_alloc_netio2(p, NULL, "testsuite");
255
  netio->poll = response_netio_poll_cb;
1✔
256
  netio->write = response_netio_write_cb;
1✔
257

1✔
258
  res = pr_register_netio(netio, PR_NETIO_STRM_CTRL);
259
  ck_assert_msg(res == 0, "Failed to register custom ctrl NetIO: %s",
1✔
260
    strerror(errno));
1✔
261

262
  conn = pr_inet_create_conn(p, sockfd, NULL, INPORT_ANY, FALSE);
263
  session.c = conn;
1✔
264

1✔
265
  pr_response_register_handler(response_handler_cb);
266

1✔
267
  resp_nlines = 0;
268
  resp_line = NULL;
1✔
269
  pr_response_set_pool(p);
1✔
270

1✔
271
  pr_response_add(R_200, "%s", "OK");
272
  pr_response_add(R_DUP, "%s", "Still OK");
1✔
273
  pr_response_add(R_DUP, "%s", "OK already!");
1✔
274
  pr_response_flush(&resp_list);
1✔
275

1✔
276
  pr_response_register_handler(NULL);
277
  pr_inet_close(p, session.c);
1✔
278
  session.c = NULL;
1✔
279
  pr_unregister_netio(PR_NETIO_STRM_CTRL);
1✔
280

1✔
281
  ck_assert_msg(resp_nlines == 3, "Expected 3 response lines flushed, got %u",
282
    resp_nlines);
1✔
283
}
284
END_TEST
1✔
285

286
START_TEST (response_send_test) {
287
  int res, sockfd = -2;
1✔
288
  conn_t *conn;
1✔
289
  pr_netio_t *netio;
1✔
290

1✔
291
  netio = pr_alloc_netio2(p, NULL, "testsuite");
292
  netio->poll = response_netio_poll_cb;
1✔
293
  netio->write = response_netio_write_cb;
1✔
294

1✔
295
  res = pr_register_netio(netio, PR_NETIO_STRM_CTRL);
296
  ck_assert_msg(res == 0, "Failed to register custom ctrl NetIO: %s",
1✔
297
    strerror(errno));
1✔
298

299
  conn = pr_inet_create_conn(p, sockfd, NULL, INPORT_ANY, FALSE);
300
  session.c = conn;
1✔
301

1✔
302
  pr_response_register_handler(response_handler_cb);
303

1✔
304
  resp_nlines = 0;
305
  resp_line = NULL;
1✔
306
  pr_response_set_pool(p);
1✔
307

1✔
308
  pr_response_send(R_200, "%s", "OK");
309

1✔
310
  pr_response_register_handler(NULL);
311
  pr_inet_close(p, session.c);
1✔
312
  session.c = NULL;
1✔
313
  pr_unregister_netio(PR_NETIO_STRM_CTRL);
1✔
314

1✔
315
  ck_assert_msg(resp_nlines == 1, "Expected 1 response line flushed, got %u",
316
    resp_nlines);
1✔
317
}
318
END_TEST
1✔
319

320
START_TEST (response_send_async_test) {
321
  int res, sockfd = -2;
1✔
322
  conn_t *conn;
1✔
323
  pr_netio_t *netio;
1✔
324

1✔
325
  netio = pr_alloc_netio2(p, NULL, "testsuite");
326
  netio->poll = response_netio_poll_cb;
1✔
327
  netio->write = response_netio_write_cb;
1✔
328

1✔
329
  res = pr_register_netio(netio, PR_NETIO_STRM_CTRL);
330
  ck_assert_msg(res == 0, "Failed to register custom ctrl NetIO: %s",
1✔
331
    strerror(errno));
1✔
332

333
  conn = pr_inet_create_conn(p, sockfd, NULL, INPORT_ANY, FALSE);
334
  session.c = conn;
1✔
335

1✔
336
  pr_response_register_handler(response_handler_cb);
337

1✔
338
  resp_nlines = 0;
339
  resp_line = NULL;
1✔
340
  pr_response_set_pool(p);
1✔
341

1✔
342
  pr_response_send_async(R_200, "%s", "OK");
343

1✔
344
  pr_response_register_handler(NULL);
345
  pr_inet_close(p, session.c);
1✔
346
  session.c = NULL;
1✔
347
  pr_unregister_netio(PR_NETIO_STRM_CTRL);
1✔
348

1✔
349
  ck_assert_msg(resp_nlines == 1, "Expected 1 response line flushed, got %u",
350
    resp_nlines);
1✔
351
  ck_assert_msg(resp_line != NULL, "Expected response line");
352
  ck_assert_msg(strcmp(resp_line, "200 OK\r\n") == 0,
1✔
353
    "Expected '200 OK', got '%s'", resp_line);
1✔
354
}
355
END_TEST
1✔
356

357
START_TEST (response_send_raw_test) {
358
  int res, sockfd = -2;
1✔
359
  conn_t *conn;
1✔
360
  pr_netio_t *netio;
1✔
361

1✔
362
  netio = pr_alloc_netio2(p, NULL, "testsuite");
363
  netio->poll = response_netio_poll_cb;
1✔
364
  netio->write = response_netio_write_cb;
1✔
365

1✔
366
  res = pr_register_netio(netio, PR_NETIO_STRM_CTRL);
367
  ck_assert_msg(res == 0, "Failed to register custom ctrl NetIO: %s",
1✔
368
    strerror(errno));
1✔
369

370
  conn = pr_inet_create_conn(p, sockfd, NULL, INPORT_ANY, FALSE);
371
  session.c = conn;
1✔
372

1✔
373
  pr_response_register_handler(response_handler_cb);
374

1✔
375
  resp_nlines = 0;
376
  resp_line = NULL;
1✔
377
  pr_response_set_pool(p);
1✔
378

1✔
379
  pr_response_send_raw("%s", "OK");
380

1✔
381
  pr_response_register_handler(NULL);
382
  pr_inet_close(p, session.c);
1✔
383
  session.c = NULL;
1✔
384
  pr_unregister_netio(PR_NETIO_STRM_CTRL);
1✔
385

1✔
386
  ck_assert_msg(resp_nlines == 1, "Expected 1 response line flushed, got %u",
387
    resp_nlines);
1✔
388
}
389
END_TEST
1✔
390

391
#if defined(TEST_BUG3711)
392
START_TEST (response_pool_bug3711_test) {
393
  cmd_rec *cmd;
394
  pool *resp_pool, *cmd_pool;
395
  char *err_code = R_450, *err_msg = "Busy";
396

397
  resp_pool = make_sub_pool(p);
398
  cmd_pool = make_sub_pool(p);
399

400
  cmd = pr_cmd_alloc(cmd_pool, 1, "foo");
401

402
  pr_response_set_pool(cmd->pool);
403
  pr_response_add_err(err_code, "%s", err_msg);
404

405
  /* We expect segfaults here, so use the mark_point() function to get
406
   * more accurate reporting of the problematic line of code in the
407
   * error logs.
408
   */
409
  mark_point();
410

411
  /* We explicitly do NOT reset the Response API pool here, to emulate the
412
   * behavior of Bug#3711.
413
   *
414
   * In the future, we could address this by proving a Pool API function
415
   * that e.g. the Response API could use, to check whether the given
416
   * pool is still a valid pool.  To do this, the Pool API would keep a
417
   * list of allocated pools, which would then be scanned.  In practice such
418
   * a list is maintained, albeit in a tree form.  And there is tracking
419
   * of the root trees for pools; permanent_pool is not the only root pool
420
   * which can be created/used.
421
   */
422
  destroy_pool(cmd_pool);
423

424
  mark_point();
425
  pr_response_add_err(err_code, "%s", err_msg);
426

427
  mark_point();
428
  pr_response_add_err(err_code, "%s", err_msg);
429

430
  mark_point();
431
  pr_response_add_err(err_code, "%s", err_msg);
432
}
433
END_TEST
434
#endif /* TEST_BUG3711 */
435

436
Suite *tests_get_response_suite(void) {
437
  Suite *suite;
889✔
438
  TCase *testcase;
889✔
439
#if defined(TEST_BUG3711)
889✔
440
  int bug3711_signo = 0;
441
#endif /* TEST_BUG3711 */
442

443
  suite = suite_create("response");
444

889✔
445
  testcase = tcase_create("base");
446
  tcase_add_checked_fixture(testcase, set_up, tear_down);
889✔
447

889✔
448
  tcase_add_test(testcase, response_pool_get_test);
449
  tcase_add_test(testcase, response_pool_set_test);
889✔
450
  tcase_add_test(testcase, response_add_test);
889✔
451
  tcase_add_test(testcase, response_add_err_test);
889✔
452
  tcase_add_test(testcase, response_get_last_test);
889✔
453

889✔
454
  tcase_add_test(testcase, response_block_test);
455
  tcase_add_test(testcase, response_blocked_test);
889✔
456
  tcase_add_test(testcase, response_clear_test);
889✔
457
  tcase_add_test(testcase, response_flush_test);
889✔
458
  tcase_add_test(testcase, response_send_test);
889✔
459
  tcase_add_test(testcase, response_send_async_test);
889✔
460
  tcase_add_test(testcase, response_send_raw_test);
889✔
461

889✔
462
#if defined(TEST_BUG3711)
463
  /* We expect this test to fail due to a segfault; see Bug#3711.
464
   *
465
   * Note that on some platforms (e.g. Darwin), the test case should fail
466
   * with a SIGBUS rather than SIGSEGV, hence the conditional here.
467
   */
468
#if defined(DARWIN9) || defined(DARWIN10) || defined(DARWIN11)
469
  bug3711_signo = SIGBUS;
470
#else
471
  bug3711_signo = SIGSEGV;
472
#endif
473

474
  /* Disable this test for now; it's a reproduction recipe rather than
475
   * a regression test, and only generates core files which can litter
476
   * the filesystems of build/test machines needlessly.
477
   */
478
  tcase_add_test_raise_signal(testcase, response_pool_bug3711_test,
479
    bug3711_signo);
480
#endif /* TEST_BUG3711 */
481

482
  suite_add_tcase(suite, testcase);
483
  return suite;
889✔
484
}
889✔
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