1 /*
2  * ProFTPD - FTP server testsuite
3  * Copyright (c) 2011-2020 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, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  *
19  * As a special exemption, The ProFTPD Project team and other respective
20  * copyright holders give permission to link this program with OpenSSL, and
21  * distribute the resulting executable, without including the source code for
22  * OpenSSL in the source distribution.
23  */
24 
25 /* Response API tests */
26 
27 #include "tests.h"
28 
29 extern pr_response_t *resp_list, *resp_err_list;
30 
31 static pool *p = NULL;
32 
set_up(void)33 static void set_up(void) {
34   if (p == NULL) {
35     p = permanent_pool = make_sub_pool(NULL);
36   }
37 
38   init_netio();
39   init_inet();
40 
41   if (getenv("TEST_VERBOSE") != NULL) {
42     pr_trace_set_levels("netio", 1, 20);
43     pr_trace_set_levels("response", 1, 20);
44   }
45 }
46 
tear_down(void)47 static void tear_down(void) {
48   pr_response_register_handler(NULL);
49 
50   if (session.c != NULL) {
51     pr_inet_close(p, session.c);
52     session.c = NULL;
53   }
54 
55   pr_unregister_netio(PR_NETIO_STRM_CTRL);
56 
57   if (getenv("TEST_VERBOSE") != NULL) {
58     pr_trace_set_levels("netio", 0, 0);
59     pr_trace_set_levels("response", 0, 0);
60   }
61 
62   if (p) {
63     destroy_pool(p);
64     p = permanent_pool = NULL;
65   }
66 }
67 
START_TEST(response_pool_get_test)68 START_TEST (response_pool_get_test) {
69   pool *res;
70 
71   res = pr_response_get_pool();
72   fail_unless(res == NULL, "Response pool not null as expected");
73 }
74 END_TEST
75 
START_TEST(response_pool_set_test)76 START_TEST (response_pool_set_test) {
77   pool *res;
78 
79   pr_response_set_pool(p);
80   res = pr_response_get_pool();
81   fail_unless(res == p, "Response pool not %p as expected", p);
82 }
83 END_TEST
84 
START_TEST(response_add_test)85 START_TEST (response_add_test) {
86   int res;
87   const char *last_resp_code = NULL, *last_resp_msg = NULL;
88   char *resp_code = R_200, *resp_msg = "OK";
89 
90   pr_response_set_pool(NULL);
91 
92   mark_point();
93   pr_response_add(resp_code, "%s", resp_msg);
94 
95   pr_response_set_pool(p);
96 
97   mark_point();
98   pr_response_add(NULL, NULL);
99 
100   mark_point();
101   pr_response_add(NULL, "%s", resp_msg);
102 
103   mark_point();
104   pr_response_add(resp_code, "%s", resp_msg);
105   pr_response_add(NULL, "%s", resp_msg);
106 
107   res = pr_response_get_last(p, &last_resp_code, &last_resp_msg);
108   fail_unless(res == 0, "Failed to get last values: %d (%s)", errno,
109     strerror(errno));
110 
111   fail_unless(last_resp_code != NULL, "Last response code unexpectedly null");
112   fail_unless(strcmp(last_resp_code, resp_code) == 0,
113     "Expected response code '%s', got '%s'", resp_code, last_resp_code);
114 
115   fail_unless(last_resp_msg != NULL, "Last response message unexpectedly null");
116   fail_unless(strcmp(last_resp_msg, resp_msg) == 0,
117     "Expected response message '%s', got '%s'", resp_msg, last_resp_msg);
118 }
119 END_TEST
120 
START_TEST(response_add_err_test)121 START_TEST (response_add_err_test) {
122   int res;
123   const char *last_resp_code = NULL, *last_resp_msg = NULL;
124   char *resp_code = R_450, *resp_msg = "Busy";
125 
126   pr_response_set_pool(NULL);
127 
128   mark_point();
129   pr_response_add(resp_code, "%s", resp_msg);
130 
131   pr_response_set_pool(p);
132 
133   mark_point();
134   pr_response_add_err(NULL, NULL);
135 
136   mark_point();
137   pr_response_add_err(resp_code, "%s", resp_msg);
138 
139   res = pr_response_get_last(p, &last_resp_code, &last_resp_msg);
140   fail_unless(res == 0, "Failed to get last values: %d (%s)", errno,
141     strerror(errno));
142 
143   fail_unless(last_resp_code != NULL, "Last response code unexpectedly null");
144   fail_unless(strcmp(last_resp_code, resp_code) == 0,
145     "Expected response code '%s', got '%s'", resp_code, last_resp_code);
146 
147   fail_unless(last_resp_msg != NULL, "Last response message unexpectedly null");
148   fail_unless(strcmp(last_resp_msg, resp_msg) == 0,
149     "Expected response message '%s', got '%s'", resp_msg, last_resp_msg);
150 }
151 END_TEST
152 
START_TEST(response_get_last_test)153 START_TEST (response_get_last_test) {
154   int res;
155   const char *resp_code = NULL, *resp_msg = NULL;
156 
157   res = pr_response_get_last(NULL, NULL, NULL);
158   fail_unless(res == -1, "Failed to handle null pool");
159   fail_unless(errno == EINVAL, "Expected errno %d (%s), got %d (%s)",
160     EINVAL, strerror(EINVAL), errno, strerror(errno));
161 
162   res = pr_response_get_last(p, NULL, NULL);
163   fail_unless(res == -1, "Failed to handle null argument");
164   fail_unless(errno == EINVAL, "Expected errno %d (%s), got %d (%s)",
165     EINVAL, strerror(EINVAL), errno, strerror(errno));
166 
167   res = pr_response_get_last(p, &resp_code, &resp_msg);
168   fail_unless(res == 0, "Failed to get last values: %d (%s)", errno,
169     strerror(errno));
170 
171   fail_unless(resp_code == NULL,
172     "Last response code not null as expected: %s", resp_code);
173   fail_unless(resp_msg == NULL,
174     "Last response message not null as expected: %s", resp_msg);
175 }
176 END_TEST
177 
START_TEST(response_block_test)178 START_TEST (response_block_test) {
179   int res;
180 
181   res = pr_response_block(-1);
182   fail_unless(res == -1, "Failed to handle invalid argument");
183   fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)",
184     EINVAL, strerror(errno), errno);
185 
186   res = pr_response_block(TRUE);
187   fail_unless(res == 0, "Failed to block responses: %s", strerror(errno));
188 
189   res = pr_response_block(FALSE);
190   fail_unless(res == 0, "Failed to unblock responses: %s", strerror(errno));
191 }
192 END_TEST
193 
START_TEST(response_blocked_test)194 START_TEST (response_blocked_test) {
195   int res;
196 
197   (void) pr_response_block(TRUE);
198   res = pr_response_blocked();
199   fail_unless(res == TRUE, "Failed to detect blocked responses");
200 
201   (void) pr_response_block(FALSE);
202   res = pr_response_blocked();
203   fail_unless(res == FALSE, "Failed to detect unblocked responses");
204 }
205 END_TEST
206 
START_TEST(response_clear_test)207 START_TEST (response_clear_test) {
208   mark_point();
209   pr_response_clear(NULL);
210 
211   pr_response_set_pool(p);
212   pr_response_add(R_200, "%s", "OK");
213   pr_response_clear(&resp_list);
214 }
215 END_TEST
216 
response_netio_poll_cb(pr_netio_stream_t * nstrm)217 static int response_netio_poll_cb(pr_netio_stream_t *nstrm) {
218   /* Always return >0, to indicate that we haven't timed out, AND that there
219    * is a writable fd available.
220    */
221   return 7;
222 }
223 
response_netio_write_cb(pr_netio_stream_t * nstrm,char * buf,size_t buflen)224 static int response_netio_write_cb(pr_netio_stream_t *nstrm, char *buf,
225     size_t buflen) {
226   return buflen;
227 }
228 
229 static unsigned int resp_nlines = 0;
230 static char *resp_line = NULL;
231 
response_handler_cb(pool * cb_pool,const char * fmt,...)232 static char *response_handler_cb(pool *cb_pool, const char *fmt, ...) {
233   char buf[PR_RESPONSE_BUFFER_SIZE] = {'\0'};
234   va_list msg;
235 
236   va_start(msg, fmt);
237   vsnprintf(buf, sizeof(buf), fmt, msg);
238   va_end(msg);
239 
240   buf[sizeof(buf)-1] = '\0';
241 
242   resp_nlines++;
243   resp_line = pstrdup(cb_pool, buf);
244   return resp_line;
245 }
246 
START_TEST(response_flush_test)247 START_TEST (response_flush_test) {
248   int res, sockfd = -2;
249   conn_t *conn;
250   pr_netio_t *netio;
251 
252   mark_point();
253   pr_response_flush(NULL);
254 
255   netio = pr_alloc_netio2(p, NULL, "testsuite");
256   netio->poll = response_netio_poll_cb;
257   netio->write = response_netio_write_cb;
258 
259   res = pr_register_netio(netio, PR_NETIO_STRM_CTRL);
260   fail_unless(res == 0, "Failed to register custom ctrl NetIO: %s",
261     strerror(errno));
262 
263   conn = pr_inet_create_conn(p, sockfd, NULL, INPORT_ANY, FALSE);
264   session.c = conn;
265 
266   pr_response_register_handler(response_handler_cb);
267 
268   resp_nlines = 0;
269   resp_line = NULL;
270   pr_response_set_pool(p);
271 
272   pr_response_add(R_200, "%s", "OK");
273   pr_response_add(R_DUP, "%s", "Still OK");
274   pr_response_add(R_DUP, "%s", "OK already!");
275   pr_response_flush(&resp_list);
276 
277   pr_response_register_handler(NULL);
278   pr_inet_close(p, session.c);
279   session.c = NULL;
280   pr_unregister_netio(PR_NETIO_STRM_CTRL);
281 
282   fail_unless(resp_nlines == 3, "Expected 3 response lines flushed, got %u",
283     resp_nlines);
284 }
285 END_TEST
286 
START_TEST(response_send_test)287 START_TEST (response_send_test) {
288   int res, sockfd = -2;
289   conn_t *conn;
290   pr_netio_t *netio;
291 
292   netio = pr_alloc_netio2(p, NULL, "testsuite");
293   netio->poll = response_netio_poll_cb;
294   netio->write = response_netio_write_cb;
295 
296   res = pr_register_netio(netio, PR_NETIO_STRM_CTRL);
297   fail_unless(res == 0, "Failed to register custom ctrl NetIO: %s",
298     strerror(errno));
299 
300   conn = pr_inet_create_conn(p, sockfd, NULL, INPORT_ANY, FALSE);
301   session.c = conn;
302 
303   pr_response_register_handler(response_handler_cb);
304 
305   resp_nlines = 0;
306   resp_line = NULL;
307   pr_response_set_pool(p);
308 
309   pr_response_send(R_200, "%s", "OK");
310 
311   pr_response_register_handler(NULL);
312   pr_inet_close(p, session.c);
313   session.c = NULL;
314   pr_unregister_netio(PR_NETIO_STRM_CTRL);
315 
316   fail_unless(resp_nlines == 1, "Expected 1 response line flushed, got %u",
317     resp_nlines);
318 }
319 END_TEST
320 
START_TEST(response_send_async_test)321 START_TEST (response_send_async_test) {
322   int res, sockfd = -2;
323   conn_t *conn;
324   pr_netio_t *netio;
325 
326   netio = pr_alloc_netio2(p, NULL, "testsuite");
327   netio->poll = response_netio_poll_cb;
328   netio->write = response_netio_write_cb;
329 
330   res = pr_register_netio(netio, PR_NETIO_STRM_CTRL);
331   fail_unless(res == 0, "Failed to register custom ctrl NetIO: %s",
332     strerror(errno));
333 
334   conn = pr_inet_create_conn(p, sockfd, NULL, INPORT_ANY, FALSE);
335   session.c = conn;
336 
337   pr_response_register_handler(response_handler_cb);
338 
339   resp_nlines = 0;
340   resp_line = NULL;
341   pr_response_set_pool(p);
342 
343   pr_response_send_async(R_200, "%s", "OK");
344 
345   pr_response_register_handler(NULL);
346   pr_inet_close(p, session.c);
347   session.c = NULL;
348   pr_unregister_netio(PR_NETIO_STRM_CTRL);
349 
350   fail_unless(resp_nlines == 1, "Expected 1 response line flushed, got %u",
351     resp_nlines);
352   fail_unless(resp_line != NULL, "Expected response line");
353   fail_unless(strcmp(resp_line, "200 OK\r\n") == 0,
354     "Expected '200 OK', got '%s'", resp_line);
355 }
356 END_TEST
357 
START_TEST(response_send_raw_test)358 START_TEST (response_send_raw_test) {
359   int res, sockfd = -2;
360   conn_t *conn;
361   pr_netio_t *netio;
362 
363   netio = pr_alloc_netio2(p, NULL, "testsuite");
364   netio->poll = response_netio_poll_cb;
365   netio->write = response_netio_write_cb;
366 
367   res = pr_register_netio(netio, PR_NETIO_STRM_CTRL);
368   fail_unless(res == 0, "Failed to register custom ctrl NetIO: %s",
369     strerror(errno));
370 
371   conn = pr_inet_create_conn(p, sockfd, NULL, INPORT_ANY, FALSE);
372   session.c = conn;
373 
374   pr_response_register_handler(response_handler_cb);
375 
376   resp_nlines = 0;
377   resp_line = NULL;
378   pr_response_set_pool(p);
379 
380   pr_response_send_raw("%s", "OK");
381 
382   pr_response_register_handler(NULL);
383   pr_inet_close(p, session.c);
384   session.c = NULL;
385   pr_unregister_netio(PR_NETIO_STRM_CTRL);
386 
387   fail_unless(resp_nlines == 1, "Expected 1 response line flushed, got %u",
388     resp_nlines);
389 }
390 END_TEST
391 
392 #if defined(TEST_BUG3711)
START_TEST(response_pool_bug3711_test)393 START_TEST (response_pool_bug3711_test) {
394   cmd_rec *cmd;
395   pool *resp_pool, *cmd_pool;
396   char *err_code = R_450, *err_msg = "Busy";
397 
398   resp_pool = make_sub_pool(p);
399   cmd_pool = make_sub_pool(p);
400 
401   cmd = pr_cmd_alloc(cmd_pool, 1, "foo");
402 
403   pr_response_set_pool(cmd->pool);
404   pr_response_add_err(err_code, "%s", err_msg);
405 
406   /* We expect segfaults here, so use the mark_point() function to get
407    * more accurate reporting of the problematic line of code in the
408    * error logs.
409    */
410   mark_point();
411 
412   /* We explicitly do NOT reset the Response API pool here, to emulate the
413    * behavior of Bug#3711.
414    *
415    * In the future, we could address this by proving a Pool API function
416    * that e.g. the Response API could use, to check whether the given
417    * pool is still a valid pool.  To do this, the Pool API would keep a
418    * list of allocated pools, which would then be scanned.  In practice such
419    * a list is maintained, albeit in a tree form.  And there is tracking
420    * of the root trees for pools; permanent_pool is not the only root pool
421    * which can be created/used.
422    */
423   destroy_pool(cmd_pool);
424 
425   mark_point();
426   pr_response_add_err(err_code, "%s", err_msg);
427 
428   mark_point();
429   pr_response_add_err(err_code, "%s", err_msg);
430 
431   mark_point();
432   pr_response_add_err(err_code, "%s", err_msg);
433 }
434 END_TEST
435 #endif /* TEST_BUG3711 */
436 
tests_get_response_suite(void)437 Suite *tests_get_response_suite(void) {
438   Suite *suite;
439   TCase *testcase;
440 #if defined(TEST_BUG3711)
441   int bug3711_signo = 0;
442 #endif /* TEST_BUG3711 */
443 
444   suite = suite_create("response");
445 
446   testcase = tcase_create("base");
447   tcase_add_checked_fixture(testcase, set_up, tear_down);
448 
449   tcase_add_test(testcase, response_pool_get_test);
450   tcase_add_test(testcase, response_pool_set_test);
451   tcase_add_test(testcase, response_add_test);
452   tcase_add_test(testcase, response_add_err_test);
453   tcase_add_test(testcase, response_get_last_test);
454 
455   tcase_add_test(testcase, response_block_test);
456   tcase_add_test(testcase, response_blocked_test);
457   tcase_add_test(testcase, response_clear_test);
458   tcase_add_test(testcase, response_flush_test);
459   tcase_add_test(testcase, response_send_test);
460   tcase_add_test(testcase, response_send_async_test);
461   tcase_add_test(testcase, response_send_raw_test);
462 
463 #if defined(TEST_BUG3711)
464   /* We expect this test to fail due to a segfault; see Bug#3711.
465    *
466    * Note that on some platforms (e.g. Darwin), the test case should fail
467    * with a SIGBUS rather than SIGSEGV, hence the conditional here.
468    */
469 #if defined(DARWIN9) || defined(DARWIN10) || defined(DARWIN11)
470   bug3711_signo = SIGBUS;
471 #else
472   bug3711_signo = SIGSEGV;
473 #endif
474 
475   /* Disable this test for now; it's a reproduction recipe rather than
476    * a regression test, and only generates core files which can litter
477    * the filesystems of build/test machines needlessly.
478    */
479   tcase_add_test_raise_signal(testcase, response_pool_bug3711_test,
480     bug3711_signo);
481 #endif /* TEST_BUG3711 */
482 
483   suite_add_tcase(suite, testcase);
484   return suite;
485 }
486