xref: /freebsd/libexec/tftpd/tests/functional.c (revision a25896ca1270e25b657ceaa8d47d5699515f5c25)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2018 Alan Somers. All rights reserved.
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/socket.h>
32 #include <sys/stat.h>
33 #include <sys/wait.h>
34 
35 #include <netinet/in.h>
36 
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <signal.h>
40 #include <stdio.h>
41 #include <unistd.h>
42 
43 #include <atf-c.h>
44 #include <libutil.h>
45 
46 static const uint16_t BASEPORT = 6969;
47 static const char pidfile[] = "tftpd.pid";
48 static int protocol = PF_UNSPEC;
49 static int s = -1;	/* tftp client socket */
50 static struct sockaddr_storage addr; /* Destination address for the client */
51 static bool s_flag = false;	/* Pass -s to tftpd */
52 static bool w_flag = false;	/* Pass -w to tftpd */
53 
54 /* Helper functions*/
55 static void require_bufeq(const char *expected, ssize_t expected_len,
56     const char *actual, ssize_t len);
57 
58 /*
59  * Receive a response from tftpd
60  * @param	hdr		The reply's expected header, as a char array
61  * @param	contents	The reply's expected contents, as a char array
62  * @param	contents_len	Length of contents
63  */
64 #define RECV(hdr, contents, contents_len) do { \
65 	char buffer[1024]; \
66 	struct sockaddr_storage from; \
67 	socklen_t fromlen = sizeof(from); \
68 	ssize_t r = recvfrom(s, buffer, sizeof(buffer), 0, \
69 	    (struct sockaddr*)&from, &fromlen); \
70 	ATF_REQUIRE(r > 0); \
71 	require_bufeq((hdr), sizeof(hdr), buffer, \
72 	    MIN(r, (ssize_t)sizeof(hdr))); \
73 	require_bufeq((const char*) (contents), (contents_len), \
74 	    &buffer[sizeof(hdr)], r - sizeof(hdr)); \
75 	if (protocol == PF_INET) { \
76 		((struct sockaddr_in*)&addr)->sin_port = \
77 		    ((struct sockaddr_in*)&from)->sin_port; \
78 	} else { \
79 		((struct sockaddr_in6*)&addr)->sin6_port = \
80 		    ((struct sockaddr_in6*)&from)->sin6_port; \
81 	} \
82 } while(0)
83 
84 static void
85 recv_ack(uint16_t blocknum)
86 {
87 	char hdr[] = {0, 4, blocknum >> 8, blocknum & 0xFF};
88 	RECV(hdr, NULL, 0);
89 }
90 
91 /*
92  * Receive a data packet from tftpd
93  * @param	blocknum	Expected block number to be received
94  * @param	contents	Pointer to expected contents
95  * @param	contents_len	Length of contents expected to receive
96  */
97 static void
98 recv_data(uint16_t blocknum, const char* contents, size_t contents_len)
99 {
100 	char hdr[] = {0, 3, blocknum >> 8, blocknum & 0xFF};
101 	RECV(hdr, contents, contents_len);
102 }
103 
104 #define RECV_ERROR(code, msg) do { \
105 	char hdr[] = {0, 5, code >> 8, code & 0xFF}; \
106 	RECV(hdr, msg, sizeof(msg)); \
107 } while (0)
108 
109 /*
110  * send a command to tftpd.
111  * @param	cmd		Command to send, as a char array
112  */
113 static void
114 send_bytes(const void* cmd, ssize_t len)
115 {
116 	ssize_t r;
117 
118 	r = sendto(s, cmd, len, 0, (struct sockaddr*)(&addr), addr.ss_len);
119 	ATF_REQUIRE_EQ(r, len);
120 }
121 
122 static void
123 send_data(uint16_t blocknum, const char* contents, size_t contents_len)
124 {
125 	char buffer[1024];
126 
127 	buffer[0] = 0;	/* DATA opcode high byte */
128 	buffer[1] = 3;	/* DATA opcode low byte */
129 	buffer[2] = blocknum >> 8;
130 	buffer[3] = blocknum & 0xFF;
131 	memmove(&buffer[4], contents, contents_len);
132 	send_bytes(buffer, 4 + contents_len);
133 }
134 
135 /*
136  * send a command to tftpd.
137  * @param	cmd		Command to send, as a const string
138  *				(terminating NUL will be ignored)
139  */
140 #define SEND_STR(cmd) ATF_REQUIRE_EQ( \
141 	sendto(s, (cmd), sizeof(cmd) - 1, 0, (struct sockaddr*)(&addr), \
142 	    addr.ss_len), \
143 	sizeof(cmd) - 1)
144 
145 /*
146  * Acknowledge block blocknum
147  */
148 static void
149 send_ack(uint16_t blocknum)
150 {
151 	char packet[] = {
152 	    0, 4,	/* ACK opcode in BE */
153 	    blocknum >> 8,
154 	    blocknum & 0xFF
155 	};
156 
157 	send_bytes(packet, sizeof(packet));
158 
159 }
160 
161 /*
162  * send a read request to tftpd.
163  * @param	filename	filename as a string, absolute or relative
164  * @param	mode		either "octet" or "netascii"
165  */
166 #define SEND_RRQ(filename, mode) SEND_STR("\0\001" filename "\0" mode "\0")
167 
168 /*
169  * send a write request to tftpd.
170  * @param	filename	filename as a string, absolute or relative
171  * @param	mode		either "octet" or "netascii"
172  */
173 #define SEND_WRQ(filename, mode) SEND_STR("\0\002" filename "\0" mode "\0")
174 
175 /* Define a test case, for both IPv4 and IPv6 */
176 #define TFTPD_TC_DEFINE(name, head, ...) \
177 static void \
178 name ## _body(void); \
179 ATF_TC_WITH_CLEANUP(name ## _v4); \
180 ATF_TC_HEAD(name ## _v4, tc) \
181 { \
182 	head \
183 } \
184 ATF_TC_BODY(name ## _v4, tc) \
185 { \
186 	__VA_ARGS__; \
187 	protocol = AF_INET; \
188 	s = setup(&addr, __COUNTER__); \
189 	name ## _body(); \
190 	close(s); \
191 } \
192 ATF_TC_CLEANUP(name ## _v4, tc) \
193 { \
194 	cleanup(); \
195 } \
196 ATF_TC_WITH_CLEANUP(name ## _v6); \
197 ATF_TC_HEAD(name ## _v6, tc) \
198 { \
199 	head \
200 } \
201 ATF_TC_BODY(name ## _v6, tc) \
202 { \
203 	__VA_ARGS__; \
204 	protocol = AF_INET6; \
205 	s = setup(&addr, __COUNTER__); \
206 	name ## _body(); \
207 	close(s); \
208 } \
209 ATF_TC_CLEANUP(name ## _v6, tc) \
210 { \
211 	cleanup(); \
212 } \
213 static void \
214 name ## _body(void)
215 
216 /* Add the IPv4 and IPv6 versions of a test case */
217 #define TFTPD_TC_ADD(tp, name ) \
218 do { \
219 	ATF_TP_ADD_TC(tp, name ## _v4); \
220 	ATF_TP_ADD_TC(tp, name ## _v6); \
221 } while (0)
222 
223 /* Standard cleanup used by all testcases */
224 static void
225 cleanup(void)
226 {
227 	FILE *f;
228 	pid_t pid;
229 
230 	f = fopen(pidfile, "r");
231 	if (f == NULL)
232 		return;
233 	if (fscanf(f, "%d", &pid) == 1) {
234 		kill(pid, SIGTERM);
235 		waitpid(pid, NULL, 0);
236 	}
237 	fclose(f);
238 	unlink(pidfile);
239 }
240 
241 /* Assert that two binary buffers are identical */
242 static void
243 require_bufeq(const char *expected, ssize_t expected_len, const char *actual,
244     ssize_t len)
245 {
246 	ssize_t i;
247 
248 	ATF_REQUIRE_EQ_MSG(expected_len, len,
249 	    "Expected %zd bytes but got %zd", expected_len, len);
250 	for (i = 0; i < len; i++) {
251 		ATF_REQUIRE_EQ_MSG(actual[i], expected[i],
252 		    "Expected %#hhx at position %zd; got %hhx instead",
253 		    expected[i], i, actual[i]);
254 	}
255 }
256 
257 /*
258  * Start tftpd and return its communicating socket
259  * @param	to	Will be filled in for use with sendto
260  * @param	idx	Unique identifier of the test case
261  * @return		Socket ready to use
262  */
263 static int
264 setup(struct sockaddr_storage *to, uint16_t idx)
265 {
266 	int client_s, server_s, pid, argv_idx;
267 	char execname[] = "/usr/libexec/tftpd";
268 	char s_flag_str[] = "-s";
269 	char w_flag_str[] = "-w";
270 	char pwd[MAXPATHLEN];
271 	char *argv[10];
272 	struct sockaddr_in addr4;
273 	struct sockaddr_in6 addr6;
274 	struct sockaddr *server_addr;
275 	struct pidfh *pfh;
276 	uint16_t port = BASEPORT + idx;
277 	socklen_t len;
278 
279 	if (protocol == PF_INET) {
280 		len = sizeof(addr4);
281 		bzero(&addr4, len);
282 		addr4.sin_len = len;
283 		addr4.sin_family = PF_INET;
284 		addr4.sin_port = htons(port);
285 		server_addr = (struct sockaddr*)&addr4;
286 	} else {
287 		len = sizeof(addr6);
288 		bzero(&addr6, len);
289 		addr6.sin6_len = len;
290 		addr6.sin6_family = PF_INET6;
291 		addr6.sin6_port = htons(port);
292 		server_addr = (struct sockaddr*)&addr6;
293 	}
294 
295 	ATF_REQUIRE_EQ(getcwd(pwd, sizeof(pwd)), pwd);
296 
297 	/* Must bind(2) pre-fork so it happens before the client's send(2) */
298 	ATF_REQUIRE((server_s = socket(protocol, SOCK_DGRAM, 0)) > 0);
299 	ATF_REQUIRE_EQ_MSG(bind(server_s, server_addr, len), 0,
300 	    "bind failed with error %s", strerror(errno));
301 
302 	pid = fork();
303 	switch (pid) {
304 	case -1:
305 		atf_tc_fail("fork failed");
306 		break;
307 	case 0:
308 		/* In child */
309 		pfh = pidfile_open(pidfile, 0644, NULL);
310 		ATF_REQUIRE_MSG(pfh != NULL,
311 		    "pidfile_open: %s", strerror(errno));
312 		ATF_REQUIRE_EQ(pidfile_write(pfh), 0);
313 		ATF_REQUIRE_EQ(pidfile_close(pfh), 0);
314 
315 		bzero(argv, sizeof(argv));
316 		argv[0] = execname;
317 		argv_idx = 1;
318 		if (w_flag)
319 			argv[argv_idx++] = w_flag_str;
320 		if (s_flag)
321 			argv[argv_idx++] = s_flag_str;
322 		argv[argv_idx++] = pwd;
323 		ATF_REQUIRE_EQ(dup2(server_s, STDOUT_FILENO), STDOUT_FILENO);
324 		ATF_REQUIRE_EQ(dup2(server_s, STDIN_FILENO), STDIN_FILENO);
325 		ATF_REQUIRE_EQ(dup2(server_s, STDERR_FILENO), STDERR_FILENO);
326 		execv(execname, argv);
327 		atf_tc_fail("exec failed");
328 		break;
329 	default:
330 		/* In parent */
331 		bzero(to, sizeof(*to));
332 		if (protocol == PF_INET) {
333 			struct sockaddr_in *to4 = (struct sockaddr_in*)to;
334 			to4->sin_len = sizeof(*to4);
335 			to4->sin_family = PF_INET;
336 			to4->sin_port = htons(port);
337 			to4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
338 		} else {
339 			struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT;
340 			struct sockaddr_in6 *to6 = (struct sockaddr_in6*)to;
341 			to6->sin6_len = sizeof(*to6);
342 			to6->sin6_family = PF_INET6;
343 			to6->sin6_port = htons(port);
344 			to6->sin6_addr = loopback;
345 		}
346 
347 		close(server_s);
348 		ATF_REQUIRE((client_s = socket(protocol, SOCK_DGRAM, 0)) > 0);
349 		break;
350 	}
351 
352 	/* Clear the client's umask.  Test cases will specify exact modes */
353 	umask(0000);
354 
355 	return (client_s);
356 }
357 
358 /* Like write(2), but never returns less than the requested length */
359 static void
360 write_all(int fd, const void *buf, size_t nbytes)
361 {
362 	ssize_t r;
363 
364 	while (nbytes > 0) {
365 		r = write(fd, buf, nbytes);
366 		ATF_REQUIRE(r > 0);
367 		nbytes -= r;
368 		buf = (const char*)buf + r;
369 	}
370 }
371 
372 
373 /*
374  * Test Cases
375  */
376 
377 /*
378  * Read a file, specified by absolute pathname.
379  */
380 TFTPD_TC_DEFINE(abspath,)
381 {
382 	int fd;
383 	char command[1024];
384 	size_t pathlen;
385 	char suffix[] = {'\0', 'o', 'c', 't', 'e', 't', '\0'};
386 
387 	command[0] = 0;		/* RRQ high byte */
388 	command[1] = 1;		/* RRQ low byte */
389 	ATF_REQUIRE(getcwd(&command[2], sizeof(command) - 2) != NULL);
390 	pathlen = strlcat(&command[2], "/abspath.txt", sizeof(command) - 2);
391 	ATF_REQUIRE(pathlen + sizeof(suffix) < sizeof(command) - 2);
392 	memmove(&command[2 + pathlen], suffix, sizeof(suffix));
393 
394 	fd = open("abspath.txt", O_CREAT | O_RDONLY, 0644);
395 	ATF_REQUIRE(fd >= 0);
396 	close(fd);
397 
398 	send_bytes(command, 2 + pathlen + sizeof(suffix));
399 	recv_data(1, NULL, 0);
400 	send_ack(1);
401 }
402 
403 /*
404  * Attempt to read a file outside of the allowed directory(ies)
405  */
406 TFTPD_TC_DEFINE(dotdot,)
407 {
408 	ATF_REQUIRE_EQ(mkdir("subdir", 0777), 0);
409 	SEND_RRQ("../disallowed.txt", "octet");
410 	RECV_ERROR(2, "Access violation");
411 	s = setup(&addr, __COUNTER__); \
412 	SEND_RRQ("subdir/../../disallowed.txt", "octet");
413 	RECV_ERROR(2, "Access violation");
414 	s = setup(&addr, __COUNTER__); \
415 	SEND_RRQ("/etc/passwd", "octet");
416 	RECV_ERROR(2, "Access violation");
417 }
418 
419 /*
420  * With "-s", tftpd should chroot to the specified directory
421  */
422 TFTPD_TC_DEFINE(s_flag, atf_tc_set_md_var(tc, "require.user", "root");,
423 		s_flag = true)
424 {
425 	int fd;
426 	char contents[] = "small";
427 
428 	fd = open("small.txt", O_RDWR | O_CREAT, 0644);
429 	ATF_REQUIRE(fd >= 0);
430 	write_all(fd, contents, strlen(contents) + 1);
431 	close(fd);
432 
433 	SEND_RRQ("/small.txt", "octet");
434 	recv_data(1, contents, strlen(contents) + 1);
435 	send_ack(1);
436 }
437 
438 /*
439  * Read a file, and simulate a dropped ACK packet
440  */
441 TFTPD_TC_DEFINE(rrq_dropped_ack,)
442 {
443 	int fd;
444 	char contents[] = "small";
445 
446 	fd = open("small.txt", O_RDWR | O_CREAT, 0644);
447 	ATF_REQUIRE(fd >= 0);
448 	write_all(fd, contents, strlen(contents) + 1);
449 	close(fd);
450 
451 	SEND_RRQ("small.txt", "octet");
452 	recv_data(1, contents, strlen(contents) + 1);
453 	/*
454 	 * client "sends" the ack, but network drops it
455 	 * Eventually, tftpd should resend the data packet
456 	 */
457 	recv_data(1, contents, strlen(contents) + 1);
458 	send_ack(1);
459 }
460 
461 /*
462  * Read a file, and simulate a dropped DATA packet
463  */
464 TFTPD_TC_DEFINE(rrq_dropped_data,)
465 {
466 	int fd;
467 	size_t i;
468 	uint32_t contents[192];
469 	char buffer[1024];
470 
471 	for (i = 0; i < nitems(contents); i++)
472 		contents[i] = i;
473 
474 	fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
475 	ATF_REQUIRE(fd >= 0);
476 	write_all(fd, contents, sizeof(contents));
477 	close(fd);
478 
479 	SEND_RRQ("medium.txt", "octet");
480 	recv_data(1, (const char*)&contents[0], 512);
481 	send_ack(1);
482 	(void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL);
483 	/*
484 	 * server "sends" the data, but network drops it
485 	 * Eventually, client should resend the last ACK
486 	 */
487 	send_ack(1);
488 	recv_data(2, (const char*)&contents[128], 256);
489 	send_ack(2);
490 }
491 
492 /*
493  * Read a medium file, and simulate a duplicated ACK packet
494  */
495 TFTPD_TC_DEFINE(rrq_duped_ack,)
496 {
497 	int fd;
498 	size_t i;
499 	uint32_t contents[192];
500 
501 	for (i = 0; i < nitems(contents); i++)
502 		contents[i] = i;
503 
504 	fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
505 	ATF_REQUIRE(fd >= 0);
506 	write_all(fd, contents, sizeof(contents));
507 	close(fd);
508 
509 	SEND_RRQ("medium.txt", "octet");
510 	recv_data(1, (const char*)&contents[0], 512);
511 	send_ack(1);
512 	send_ack(1);	/* Dupe an ACK packet */
513 	recv_data(2, (const char*)&contents[128], 256);
514 	recv_data(2, (const char*)&contents[128], 256);
515 	send_ack(2);
516 }
517 
518 
519 /*
520  * Attempt to read a file without read permissions
521  */
522 TFTPD_TC_DEFINE(rrq_eaccess,)
523 {
524 	int fd;
525 
526 	fd = open("empty.txt", O_CREAT | O_RDONLY, 0000);
527 	ATF_REQUIRE(fd >= 0);
528 	close(fd);
529 
530 	SEND_RRQ("empty.txt", "octet");
531 	RECV_ERROR(2, "Access violation");
532 }
533 
534 /*
535  * Read an empty file
536  */
537 TFTPD_TC_DEFINE(rrq_empty,)
538 {
539 	int fd;
540 
541 	fd = open("empty.txt", O_CREAT | O_RDONLY, 0644);
542 	ATF_REQUIRE(fd >= 0);
543 	close(fd);
544 
545 	SEND_RRQ("empty.txt", "octet");
546 	recv_data(1, NULL, 0);
547 	send_ack(1);
548 }
549 
550 /*
551  * Read a medium file of more than one block
552  */
553 TFTPD_TC_DEFINE(rrq_medium,)
554 {
555 	int fd;
556 	size_t i;
557 	uint32_t contents[192];
558 
559 	for (i = 0; i < nitems(contents); i++)
560 		contents[i] = i;
561 
562 	fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
563 	ATF_REQUIRE(fd >= 0);
564 	write_all(fd, contents, sizeof(contents));
565 	close(fd);
566 
567 	SEND_RRQ("medium.txt", "octet");
568 	recv_data(1, (const char*)&contents[0], 512);
569 	send_ack(1);
570 	recv_data(2, (const char*)&contents[128], 256);
571 	send_ack(2);
572 }
573 
574 /*
575  * Read a file in netascii format
576  */
577 TFTPD_TC_DEFINE(rrq_netascii,)
578 {
579 	int fd;
580 	char contents[] = "foo\nbar\rbaz\n";
581 	/*
582 	 * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed
583 	 * is not intended
584 	 */
585 	char expected[] = "foo\r\nbar\r\0baz\r\n";
586 
587 	fd = open("unix.txt", O_RDWR | O_CREAT, 0644);
588 	ATF_REQUIRE(fd >= 0);
589 	write_all(fd, contents, strlen(contents) + 1);
590 	close(fd);
591 
592 	SEND_RRQ("unix.txt", "netascii");
593 	recv_data(1, expected, sizeof(expected));
594 	send_ack(1);
595 }
596 
597 /*
598  * Read a file that doesn't exist
599  */
600 TFTPD_TC_DEFINE(rrq_nonexistent,)
601 {
602 	SEND_RRQ("nonexistent.txt", "octet");
603 	RECV_ERROR(1, "File not found");
604 }
605 
606 /*
607  * Attempt to read a file whose name exceeds PATH_MAX
608  */
609 TFTPD_TC_DEFINE(rrq_path_max,)
610 {
611 #define AReallyBigFileName \
612 	    "AReallyBigFileNameXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
613 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
614 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
615 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
616 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
617 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
618 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
619 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
620 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
621 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
622 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
623 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
624 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
625 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
626 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
627 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
628 	    ".txt"
629 	ATF_REQUIRE_MSG(strlen(AReallyBigFileName) > PATH_MAX,
630 	    "Somebody increased PATH_MAX.  Update the test");
631 	SEND_RRQ(AReallyBigFileName, "octet");
632 	RECV_ERROR(4, "Illegal TFTP operation");
633 }
634 
635 /*
636  * Read a small file of less than one block
637  */
638 TFTPD_TC_DEFINE(rrq_small,)
639 {
640 	int fd;
641 	char contents[] = "small";
642 
643 	fd = open("small.txt", O_RDWR | O_CREAT, 0644);
644 	ATF_REQUIRE(fd >= 0);
645 	write_all(fd, contents, strlen(contents) + 1);
646 	close(fd);
647 
648 	SEND_RRQ("small.txt", "octet");
649 	recv_data(1, contents, strlen(contents) + 1);
650 	send_ack(1);
651 }
652 
653 /*
654  * Try to transfer a file with an unknown mode.
655  */
656 TFTPD_TC_DEFINE(unknown_modes,)
657 {
658 	SEND_RRQ("foo.txt", "ascii");	/* Misspelling of "ascii" */
659 	RECV_ERROR(4, "Illegal TFTP operation");
660 	s = setup(&addr, __COUNTER__); \
661 	SEND_RRQ("foo.txt", "binary");	/* Obsolete.  Use "octet" instead */
662 	RECV_ERROR(4, "Illegal TFTP operation");
663 	s = setup(&addr, __COUNTER__); \
664 	SEND_RRQ("foo.txt", "en_US.UTF-8");
665 	RECV_ERROR(4, "Illegal TFTP operation");
666 	s = setup(&addr, __COUNTER__); \
667 	SEND_RRQ("foo.txt", "mail");	/* Obsolete in RFC-1350 */
668 	RECV_ERROR(4, "Illegal TFTP operation");
669 }
670 
671 /*
672  * Send an unknown opcode.  tftpd should respond with the appropriate error
673  */
674 TFTPD_TC_DEFINE(unknown_opcode,)
675 {
676 	/* Looks like an RRQ or WRQ request, but with a bad opcode */
677 	SEND_STR("\0\007foo.txt\0octet\0");
678 	RECV_ERROR(4, "Illegal TFTP operation");
679 }
680 
681 /*
682  * Invoke tftpd with "-w" and write to a nonexistent file.
683  */
684 TFTPD_TC_DEFINE(w_flag,, w_flag = 1;)
685 {
686 	int fd;
687 	ssize_t r;
688 	char contents[] = "small";
689 	char buffer[1024];
690 	size_t contents_len;
691 
692 	contents_len = strlen(contents) + 1;
693 	SEND_WRQ("small.txt", "octet");
694 	recv_ack(0);
695 	send_data(1, contents, contents_len);
696 	recv_ack(1);
697 
698 	fd = open("small.txt", O_RDONLY);
699 	ATF_REQUIRE(fd >= 0);
700 	r = read(fd, buffer, sizeof(buffer));
701 	close(fd);
702 	require_bufeq(contents, contents_len, buffer, r);
703 }
704 
705 /*
706  * Write a medium file, and simulate a dropped ACK packet
707  */
708 TFTPD_TC_DEFINE(wrq_dropped_ack,)
709 {
710 	int fd;
711 	size_t i;
712 	ssize_t r;
713 	uint32_t contents[192];
714 	char buffer[1024];
715 
716 	for (i = 0; i < nitems(contents); i++)
717 		contents[i] = i;
718 
719 	fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
720 	ATF_REQUIRE(fd >= 0);
721 	close(fd);
722 
723 	SEND_WRQ("medium.txt", "octet");
724 	recv_ack(0);
725 	send_data(1, (const char*)&contents[0], 512);
726 	/*
727 	 * Servers "sends" an ACK packet, but network drops it.
728 	 * Eventually, server should resend the last ACK
729 	 */
730 	(void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL);
731 	recv_ack(1);
732 	send_data(2, (const char*)&contents[128], 256);
733 	recv_ack(2);
734 
735 	fd = open("medium.txt", O_RDONLY);
736 	ATF_REQUIRE(fd >= 0);
737 	r = read(fd, buffer, sizeof(buffer));
738 	close(fd);
739 	require_bufeq((const char*)contents, 768, buffer, r);
740 }
741 
742 /*
743  * Write a small file, and simulate a dropped DATA packet
744  */
745 TFTPD_TC_DEFINE(wrq_dropped_data,)
746 {
747 	int fd;
748 	ssize_t r;
749 	char contents[] = "small";
750 	size_t contents_len;
751 	char buffer[1024];
752 
753 	fd = open("small.txt", O_RDWR | O_CREAT, 0666);
754 	ATF_REQUIRE(fd >= 0);
755 	close(fd);
756 	contents_len = strlen(contents) + 1;
757 
758 	SEND_WRQ("small.txt", "octet");
759 	recv_ack(0);
760 	/*
761 	 * Client "sends" a DATA packet, but network drops it.
762 	 * Eventually, server should resend the last ACK
763 	 */
764 	recv_ack(0);
765 	send_data(1, contents, contents_len);
766 	recv_ack(1);
767 
768 	fd = open("small.txt", O_RDONLY);
769 	ATF_REQUIRE(fd >= 0);
770 	r = read(fd, buffer, sizeof(buffer));
771 	close(fd);
772 	require_bufeq(contents, contents_len, buffer, r);
773 }
774 
775 /*
776  * Write a medium file, and simulate a duplicated DATA packet
777  */
778 TFTPD_TC_DEFINE(wrq_duped_data,)
779 {
780 	int fd;
781 	size_t i;
782 	ssize_t r;
783 	uint32_t contents[192];
784 	char buffer[1024];
785 
786 	for (i = 0; i < nitems(contents); i++)
787 		contents[i] = i;
788 
789 	fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
790 	ATF_REQUIRE(fd >= 0);
791 	close(fd);
792 
793 	SEND_WRQ("medium.txt", "octet");
794 	recv_ack(0);
795 	send_data(1, (const char*)&contents[0], 512);
796 	send_data(1, (const char*)&contents[0], 512);
797 	recv_ack(1);
798 	recv_ack(1);
799 	send_data(2, (const char*)&contents[128], 256);
800 	recv_ack(2);
801 
802 	fd = open("medium.txt", O_RDONLY);
803 	ATF_REQUIRE(fd >= 0);
804 	r = read(fd, buffer, sizeof(buffer));
805 	close(fd);
806 	require_bufeq((const char*)contents, 768, buffer, r);
807 }
808 
809 /*
810  * Attempt to write a file without write permissions
811  */
812 TFTPD_TC_DEFINE(wrq_eaccess,)
813 {
814 	int fd;
815 
816 	fd = open("empty.txt", O_CREAT | O_RDONLY, 0440);
817 	ATF_REQUIRE(fd >= 0);
818 	close(fd);
819 
820 	SEND_WRQ("empty.txt", "octet");
821 	RECV_ERROR(2, "Access violation");
822 }
823 
824 /*
825  * Attempt to write a file without world write permissions, but with world
826  * read permissions
827  */
828 TFTPD_TC_DEFINE(wrq_eaccess_world_readable,)
829 {
830 	int fd;
831 
832 	fd = open("empty.txt", O_CREAT | O_RDONLY, 0444);
833 	ATF_REQUIRE(fd >= 0);
834 	close(fd);
835 
836 	SEND_WRQ("empty.txt", "octet");
837 	RECV_ERROR(2, "Access violation");
838 }
839 
840 
841 /*
842  * Write a medium file of more than one block
843  */
844 TFTPD_TC_DEFINE(wrq_medium,)
845 {
846 	int fd;
847 	size_t i;
848 	ssize_t r;
849 	uint32_t contents[192];
850 	char buffer[1024];
851 
852 	for (i = 0; i < nitems(contents); i++)
853 		contents[i] = i;
854 
855 	fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
856 	ATF_REQUIRE(fd >= 0);
857 	close(fd);
858 
859 	SEND_WRQ("medium.txt", "octet");
860 	recv_ack(0);
861 	send_data(1, (const char*)&contents[0], 512);
862 	recv_ack(1);
863 	send_data(2, (const char*)&contents[128], 256);
864 	recv_ack(2);
865 
866 	fd = open("medium.txt", O_RDONLY);
867 	ATF_REQUIRE(fd >= 0);
868 	r = read(fd, buffer, sizeof(buffer));
869 	close(fd);
870 	require_bufeq((const char*)contents, 768, buffer, r);
871 }
872 
873 /*
874  * Write a file in netascii format
875  */
876 TFTPD_TC_DEFINE(wrq_netascii,)
877 {
878 	int fd;
879 	ssize_t r;
880 	/*
881 	 * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed
882 	 * is not intended
883 	 */
884 	char contents[] = "foo\r\nbar\r\0baz\r\n";
885 	char expected[] = "foo\nbar\rbaz\n";
886 	size_t contents_len;
887 	char buffer[1024];
888 
889 	fd = open("unix.txt", O_RDWR | O_CREAT, 0666);
890 	ATF_REQUIRE(fd >= 0);
891 	close(fd);
892 	contents_len = sizeof(contents);
893 
894 	SEND_WRQ("unix.txt", "netascii");
895 	recv_ack(0);
896 	send_data(1, contents, contents_len);
897 	recv_ack(1);
898 
899 	fd = open("unix.txt", O_RDONLY);
900 	ATF_REQUIRE(fd >= 0);
901 	r = read(fd, buffer, sizeof(buffer));
902 	close(fd);
903 	require_bufeq(expected, sizeof(expected), buffer, r);
904 }
905 
906 /*
907  * Attempt to write to a nonexistent file.  With the default options, this
908  * isn't allowed.
909  */
910 TFTPD_TC_DEFINE(wrq_nonexistent,)
911 {
912 	SEND_WRQ("nonexistent.txt", "octet");
913 	RECV_ERROR(1, "File not found");
914 }
915 
916 /*
917  * Write a small file of less than one block
918  */
919 TFTPD_TC_DEFINE(wrq_small,)
920 {
921 	int fd;
922 	ssize_t r;
923 	char contents[] = "small";
924 	size_t contents_len;
925 	char buffer[1024];
926 
927 	fd = open("small.txt", O_RDWR | O_CREAT, 0666);
928 	ATF_REQUIRE(fd >= 0);
929 	close(fd);
930 	contents_len = strlen(contents) + 1;
931 
932 	SEND_WRQ("small.txt", "octet");
933 	recv_ack(0);
934 	send_data(1, contents, contents_len);
935 	recv_ack(1);
936 
937 	fd = open("small.txt", O_RDONLY);
938 	ATF_REQUIRE(fd >= 0);
939 	r = read(fd, buffer, sizeof(buffer));
940 	close(fd);
941 	require_bufeq(contents, contents_len, buffer, r);
942 }
943 
944 /*
945  * Write an empty file over a non-empty one
946  */
947 TFTPD_TC_DEFINE(wrq_truncate,)
948 {
949 	int fd;
950 	char contents[] = "small";
951 	struct stat sb;
952 
953 	fd = open("small.txt", O_RDWR | O_CREAT, 0666);
954 	ATF_REQUIRE(fd >= 0);
955 	write_all(fd, contents, strlen(contents) + 1);
956 	close(fd);
957 
958 	SEND_WRQ("small.txt", "octet");
959 	recv_ack(0);
960 	send_data(1, NULL, 0);
961 	recv_ack(1);
962 
963 	ATF_REQUIRE_EQ(stat("small.txt", &sb), 0);
964 	ATF_REQUIRE_EQ(sb.st_size, 0);
965 }
966 
967 
968 /*
969  * Main
970  */
971 
972 ATF_TP_ADD_TCS(tp)
973 {
974 	TFTPD_TC_ADD(tp, abspath);
975 	TFTPD_TC_ADD(tp, dotdot);
976 	TFTPD_TC_ADD(tp, s_flag);
977 	TFTPD_TC_ADD(tp, rrq_dropped_ack);
978 	TFTPD_TC_ADD(tp, rrq_dropped_data);
979 	TFTPD_TC_ADD(tp, rrq_duped_ack);
980 	TFTPD_TC_ADD(tp, rrq_eaccess);
981 	TFTPD_TC_ADD(tp, rrq_empty);
982 	TFTPD_TC_ADD(tp, rrq_medium);
983 	TFTPD_TC_ADD(tp, rrq_netascii);
984 	TFTPD_TC_ADD(tp, rrq_nonexistent);
985 	TFTPD_TC_ADD(tp, rrq_path_max);
986 	TFTPD_TC_ADD(tp, rrq_small);
987 	TFTPD_TC_ADD(tp, unknown_modes);
988 	TFTPD_TC_ADD(tp, unknown_opcode);
989 	TFTPD_TC_ADD(tp, w_flag);
990 	TFTPD_TC_ADD(tp, wrq_dropped_ack);
991 	TFTPD_TC_ADD(tp, wrq_dropped_data);
992 	TFTPD_TC_ADD(tp, wrq_duped_data);
993 	TFTPD_TC_ADD(tp, wrq_eaccess);
994 	TFTPD_TC_ADD(tp, wrq_eaccess_world_readable);
995 	TFTPD_TC_ADD(tp, wrq_medium);
996 	TFTPD_TC_ADD(tp, wrq_netascii);
997 	TFTPD_TC_ADD(tp, wrq_nonexistent);
998 	TFTPD_TC_ADD(tp, wrq_small);
999 	TFTPD_TC_ADD(tp, wrq_truncate);
1000 
1001 	return (atf_no_error());
1002 }
1003