]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - libexec/tftpd/tests/functional.c
tftpd: Flush files as soon as they are fully received
[FreeBSD/FreeBSD.git] / libexec / tftpd / tests / functional.c
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         int fd = -1;
228         char buffer[80] = {0};
229         pid_t pid;
230
231         fd = open(pidfile, O_RDONLY);
232         if (fd < 0)
233                 return;
234         if (read(fd, buffer, sizeof(buffer)) > 0) {
235                 sscanf(buffer, "%d", &pid);
236                 kill(pid, SIGTERM);
237                 waitpid(pid, NULL, 0);
238         }
239         close(fd);
240         unlink(pidfile);
241 }
242
243 /* Assert that two binary buffers are identical */
244 static void
245 require_bufeq(const char *expected, ssize_t expected_len, const char *actual,
246     ssize_t len)
247 {
248         ssize_t i;
249
250         ATF_REQUIRE_EQ_MSG(expected_len, len,
251             "Expected %ld bytes but got %ld", expected_len, len);
252         for (i = 0; i < len; i++) {
253                 ATF_REQUIRE_EQ_MSG(actual[i], expected[i],
254                     "Expected %#hhx at position %ld; got %hhx instead",
255                     expected[i], i, actual[i]);
256         }
257 }
258
259 /*
260  * Start tftpd and return its communicating socket
261  * @param       to      Will be filled in for use with sendto
262  * @param       idx     Unique identifier of the test case
263  * @return              Socket ready to use
264  */
265 static int
266 setup(struct sockaddr_storage *to, uint16_t idx)
267 {
268         int client_s, server_s, pid, argv_idx;
269         char execname[] = "/usr/libexec/tftpd";
270         char s_flag_str[] = "-s";
271         char w_flag_str[] = "-w";
272         char pwd[MAXPATHLEN];
273         char *argv[10];
274         struct sockaddr_in addr4;
275         struct sockaddr_in6 addr6;
276         struct sockaddr *server_addr;
277         struct pidfh *pfh;
278         uint16_t port = BASEPORT + idx;
279         socklen_t len;
280
281         if (protocol == PF_INET) {
282                 len = sizeof(addr4);
283                 bzero(&addr4, len);
284                 addr4.sin_len = len;
285                 addr4.sin_family = PF_INET;
286                 addr4.sin_port = htons(port);
287                 server_addr = (struct sockaddr*)&addr4;
288         } else {
289                 len = sizeof(addr6);
290                 bzero(&addr6, len);
291                 addr6.sin6_len = len;
292                 addr6.sin6_family = PF_INET6;
293                 addr6.sin6_port = htons(port);
294                 server_addr = (struct sockaddr*)&addr6;
295         }
296
297         ATF_REQUIRE_EQ(getcwd(pwd, sizeof(pwd)), pwd);
298         
299         /* Must bind(2) pre-fork so it happens before the client's send(2) */
300         ATF_REQUIRE((server_s = socket(protocol, SOCK_DGRAM, 0)) > 0);
301         ATF_REQUIRE_EQ_MSG(bind(server_s, server_addr, len), 0,
302             "bind failed with error %s", strerror(errno));
303
304         pid = fork();
305         switch (pid) {
306         case -1:
307                 atf_tc_fail("fork failed");
308                 break;
309         case 0:
310                 /* In child */
311                 pfh = pidfile_open(pidfile, 0644, NULL);
312                 ATF_REQUIRE_MSG(pfh != NULL,
313                     "pidfile_open: %s", strerror(errno));
314                 ATF_REQUIRE_EQ(pidfile_write(pfh), 0);
315                 ATF_REQUIRE_EQ(pidfile_close(pfh), 0);
316
317                 bzero(argv, sizeof(argv));
318                 argv[0] = execname;
319                 argv_idx = 1;
320                 if (w_flag)
321                         argv[argv_idx++] = w_flag_str;
322                 if (s_flag)
323                         argv[argv_idx++] = s_flag_str;
324                 argv[argv_idx++] = pwd;
325                 ATF_REQUIRE_EQ(dup2(server_s, STDOUT_FILENO), STDOUT_FILENO);
326                 ATF_REQUIRE_EQ(dup2(server_s, STDIN_FILENO), STDIN_FILENO);
327                 ATF_REQUIRE_EQ(dup2(server_s, STDERR_FILENO), STDERR_FILENO);
328                 execv(execname, argv);
329                 atf_tc_fail("exec failed");
330                 break;
331         default:
332                 /* In parent */
333                 bzero(to, sizeof(*to));
334                 if (protocol == PF_INET) {
335                         struct sockaddr_in *to4 = (struct sockaddr_in*)to;
336                         to4->sin_len = sizeof(*to4);
337                         to4->sin_family = PF_INET;
338                         to4->sin_port = htons(port);
339                         to4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
340                 } else {
341                         struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT;
342                         struct sockaddr_in6 *to6 = (struct sockaddr_in6*)to;
343                         to6->sin6_len = sizeof(*to6);
344                         to6->sin6_family = PF_INET6;
345                         to6->sin6_port = htons(port);
346                         to6->sin6_addr = loopback;
347                 }
348
349                 close(server_s);
350                 ATF_REQUIRE((client_s = socket(protocol, SOCK_DGRAM, 0)) > 0);
351                 break;
352         }
353         return (client_s);
354 }
355
356 /* Like write(2), but never returns less than the requested length */
357 static void
358 write_all(int fd, const void *buf, size_t nbytes)
359 {
360         ssize_t r;
361
362         while (nbytes > 0) {
363                 r = write(fd, buf, nbytes);
364                 ATF_REQUIRE(r > 0);
365                 nbytes -= r;
366                 buf = (const char*)buf + r;
367         }
368 }
369
370
371 /*
372  * Test Cases
373  */
374
375 /*
376  * Read a file, specified by absolute pathname.
377  */
378 TFTPD_TC_DEFINE(abspath,)
379 {
380         int fd;
381         char command[1024];
382         size_t pathlen;
383         char suffix[] = {'\0', 'o', 'c', 't', 'e', 't', '\0'};
384
385         command[0] = 0;         /* RRQ high byte */
386         command[1] = 1;         /* RRQ low byte */
387         ATF_REQUIRE(getcwd(&command[2], sizeof(command) - 2) != NULL);
388         pathlen = strlcat(&command[2], "/abspath.txt", sizeof(command) - 2);
389         ATF_REQUIRE(pathlen + sizeof(suffix) < sizeof(command) - 2);
390         memmove(&command[2 + pathlen], suffix, sizeof(suffix));
391
392         fd = open("abspath.txt", O_CREAT | O_RDONLY, 0644);
393         ATF_REQUIRE(fd >= 0);
394         close(fd);
395
396         send_bytes(command, 2 + pathlen + sizeof(suffix));
397         recv_data(1, NULL, 0);
398         send_ack(1);
399 }
400
401 /*
402  * Attempt to read a file outside of the allowed directory(ies)
403  */
404 TFTPD_TC_DEFINE(dotdot,)
405 {
406         ATF_REQUIRE_EQ(mkdir("subdir", 0777), 0);
407         SEND_RRQ("../disallowed.txt", "octet");
408         RECV_ERROR(2, "Access violation");
409         s = setup(&addr, __COUNTER__); \
410         SEND_RRQ("subdir/../../disallowed.txt", "octet");
411         RECV_ERROR(2, "Access violation");
412         s = setup(&addr, __COUNTER__); \
413         SEND_RRQ("/etc/passwd", "octet");
414         RECV_ERROR(2, "Access violation");
415 }
416
417 /*
418  * With "-s", tftpd should chroot to the specified directory
419  */
420 TFTPD_TC_DEFINE(s_flag, atf_tc_set_md_var(tc, "require.user", "root");,
421                 s_flag = true)
422 {
423         int fd;
424         char contents[] = "small";
425
426         fd = open("small.txt", O_RDWR | O_CREAT, 0644);
427         ATF_REQUIRE(fd >= 0);
428         write_all(fd, contents, strlen(contents) + 1);
429         close(fd);
430
431         SEND_RRQ("/small.txt", "octet");
432         recv_data(1, contents, strlen(contents) + 1);
433         send_ack(1);
434 }
435
436 /*
437  * Read a file, and simulate a dropped ACK packet
438  */
439 TFTPD_TC_DEFINE(rrq_dropped_ack,)
440 {
441         int fd;
442         char contents[] = "small";
443
444         fd = open("small.txt", O_RDWR | O_CREAT, 0644);
445         ATF_REQUIRE(fd >= 0);
446         write_all(fd, contents, strlen(contents) + 1);
447         close(fd);
448
449         SEND_RRQ("small.txt", "octet");
450         recv_data(1, contents, strlen(contents) + 1);
451         /*
452          * client "sends" the ack, but network drops it
453          * Eventually, tftpd should resend the data packet
454          */
455         recv_data(1, contents, strlen(contents) + 1);
456         send_ack(1);
457 }
458
459 /*
460  * Read a file, and simulate a dropped DATA packet
461  */
462 TFTPD_TC_DEFINE(rrq_dropped_data,)
463 {
464         int fd;
465         size_t i;
466         uint32_t contents[192];
467         char buffer[1024];
468
469         for (i = 0; i < nitems(contents); i++)
470                 contents[i] = i;
471
472         fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
473         ATF_REQUIRE(fd >= 0);
474         write_all(fd, contents, sizeof(contents));
475         close(fd);
476
477         SEND_RRQ("medium.txt", "octet");
478         recv_data(1, (const char*)&contents[0], 512);
479         send_ack(1);
480         (void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL);
481         /*
482          * server "sends" the data, but network drops it
483          * Eventually, client should resend the last ACK
484          */
485         send_ack(1);
486         recv_data(2, (const char*)&contents[128], 256);
487         send_ack(2);
488 }
489
490 /*
491  * Read a medium file, and simulate a duplicated ACK packet
492  */
493 TFTPD_TC_DEFINE(rrq_duped_ack,)
494 {
495         int fd;
496         size_t i;
497         uint32_t contents[192];
498
499         for (i = 0; i < nitems(contents); i++)
500                 contents[i] = i;
501
502         fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
503         ATF_REQUIRE(fd >= 0);
504         write_all(fd, contents, sizeof(contents));
505         close(fd);
506
507         SEND_RRQ("medium.txt", "octet");
508         recv_data(1, (const char*)&contents[0], 512);
509         send_ack(1);
510         send_ack(1);    /* Dupe an ACK packet */
511         recv_data(2, (const char*)&contents[128], 256);
512         recv_data(2, (const char*)&contents[128], 256);
513         send_ack(2);
514 }
515
516
517 /*
518  * Attempt to read a file without read permissions
519  */
520 TFTPD_TC_DEFINE(rrq_eaccess,)
521 {
522         int fd;
523
524         fd = open("empty.txt", O_CREAT | O_RDONLY, 0000);
525         ATF_REQUIRE(fd >= 0);
526         close(fd);
527
528         SEND_RRQ("empty.txt", "octet");
529         RECV_ERROR(2, "Access violation");
530 }
531
532 /*
533  * Read an empty file
534  */
535 TFTPD_TC_DEFINE(rrq_empty,)
536 {
537         int fd;
538
539         fd = open("empty.txt", O_CREAT | O_RDONLY, 0644);
540         ATF_REQUIRE(fd >= 0);
541         close(fd);
542
543         SEND_RRQ("empty.txt", "octet");
544         recv_data(1, NULL, 0);
545         send_ack(1);
546 }
547
548 /*
549  * Read a medium file of more than one block
550  */
551 TFTPD_TC_DEFINE(rrq_medium,)
552 {
553         int fd;
554         size_t i;
555         uint32_t contents[192];
556
557         for (i = 0; i < nitems(contents); i++)
558                 contents[i] = i;
559
560         fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
561         ATF_REQUIRE(fd >= 0);
562         write_all(fd, contents, sizeof(contents));
563         close(fd);
564
565         SEND_RRQ("medium.txt", "octet");
566         recv_data(1, (const char*)&contents[0], 512);
567         send_ack(1);
568         recv_data(2, (const char*)&contents[128], 256);
569         send_ack(2);
570 }
571
572 /*
573  * Read a file in netascii format
574  */
575 TFTPD_TC_DEFINE(rrq_netascii,)
576 {
577         int fd;
578         char contents[] = "foo\nbar\rbaz\n";
579         /* 
580          * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed
581          * is not intended
582          */
583         char expected[] = "foo\r\nbar\r\0baz\r\n";
584
585         fd = open("unix.txt", O_RDWR | O_CREAT, 0644);
586         ATF_REQUIRE(fd >= 0);
587         write_all(fd, contents, strlen(contents) + 1);
588         close(fd);
589
590         SEND_RRQ("unix.txt", "netascii");
591         recv_data(1, expected, sizeof(expected));
592         send_ack(1);
593 }
594
595 /*
596  * Read a file that doesn't exist
597  */
598 TFTPD_TC_DEFINE(rrq_nonexistent,)
599 {
600         SEND_RRQ("nonexistent.txt", "octet");
601         RECV_ERROR(1, "File not found");
602 }
603
604 /*
605  * Attempt to read a file whose name exceeds PATH_MAX
606  */
607 TFTPD_TC_DEFINE(rrq_path_max,)
608 {
609 #define AReallyBigFileName \
610             "AReallyBigFileNameXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
611             "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
612             "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
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             ".txt"
627         ATF_REQUIRE_MSG(strlen(AReallyBigFileName) > PATH_MAX,
628             "Somebody increased PATH_MAX.  Update the test");
629         SEND_RRQ(AReallyBigFileName, "octet");
630         RECV_ERROR(4, "Illegal TFTP operation");
631 }
632
633 /*
634  * Read a small file of less than one block
635  */
636 TFTPD_TC_DEFINE(rrq_small,)
637 {
638         int fd;
639         char contents[] = "small";
640
641         fd = open("small.txt", O_RDWR | O_CREAT, 0644);
642         ATF_REQUIRE(fd >= 0);
643         write_all(fd, contents, strlen(contents) + 1);
644         close(fd);
645
646         SEND_RRQ("small.txt", "octet");
647         recv_data(1, contents, strlen(contents) + 1);
648         send_ack(1);
649 }
650
651 /*
652  * Try to transfer a file with an unknown mode.
653  */
654 TFTPD_TC_DEFINE(unknown_modes,)
655 {
656         SEND_RRQ("foo.txt", "ascii");   /* Misspelling of "ascii" */
657         RECV_ERROR(4, "Illegal TFTP operation");
658         s = setup(&addr, __COUNTER__); \
659         SEND_RRQ("foo.txt", "binary");  /* Obsolete.  Use "octet" instead */
660         RECV_ERROR(4, "Illegal TFTP operation");
661         s = setup(&addr, __COUNTER__); \
662         SEND_RRQ("foo.txt", "en_US.UTF-8");
663         RECV_ERROR(4, "Illegal TFTP operation");
664         s = setup(&addr, __COUNTER__); \
665         SEND_RRQ("foo.txt", "mail");    /* Obsolete in RFC-1350 */
666         RECV_ERROR(4, "Illegal TFTP operation");
667 }
668
669 /*
670  * Send an unknown opcode.  tftpd should respond with the appropriate error
671  */
672 TFTPD_TC_DEFINE(unknown_opcode,)
673 {
674         /* Looks like an RRQ or WRQ request, but with a bad opcode */
675         SEND_STR("\0\007foo.txt\0octet\0");
676         atf_tc_expect_timeout("PR 226005 tftpd ignores bad opcodes but doesn't reject them");
677         RECV_ERROR(4, "Illegal TFTP operation");
678 }
679
680 /*
681  * Invoke tftpd with "-w" and write to a nonexistent file.
682  */
683 TFTPD_TC_DEFINE(w_flag,, w_flag = 1;)
684 {
685         int fd;
686         ssize_t r;
687         char contents[] = "small";
688         char buffer[1024];
689         size_t contents_len;
690
691         contents_len = strlen(contents) + 1;
692         SEND_WRQ("small.txt", "octet");
693         recv_ack(0);
694         send_data(1, contents, contents_len);
695         recv_ack(1);
696
697         fd = open("small.txt", O_RDONLY);
698         r = read(fd, buffer, sizeof(buffer));
699         close(fd);
700         require_bufeq(contents, contents_len, buffer, r);
701 }
702
703 /*
704  * Write a medium file, and simulate a dropped ACK packet
705  */
706 TFTPD_TC_DEFINE(wrq_dropped_ack,)
707 {
708         int fd;
709         size_t i;
710         ssize_t r;
711         uint32_t contents[192];
712         char buffer[1024];
713
714         for (i = 0; i < nitems(contents); i++)
715                 contents[i] = i;
716
717         fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
718         ATF_REQUIRE(fd >= 0);
719         close(fd);
720
721         SEND_WRQ("medium.txt", "octet");
722         recv_ack(0);
723         send_data(1, (const char*)&contents[0], 512);
724         /* 
725          * Servers "sends" an ACK packet, but network drops it.
726          * Eventually, server should resend the last ACK
727          */
728         (void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL);
729         recv_ack(1);
730         send_data(2, (const char*)&contents[128], 256);
731         recv_ack(2);
732
733         fd = open("medium.txt", O_RDONLY);
734         r = read(fd, buffer, sizeof(buffer));
735         close(fd);
736         require_bufeq((const char*)contents, 768, buffer, r);
737 }
738
739 /*
740  * Write a small file, and simulate a dropped DATA packet
741  */
742 TFTPD_TC_DEFINE(wrq_dropped_data,)
743 {
744         int fd;
745         ssize_t r;
746         char contents[] = "small";
747         size_t contents_len;
748         char buffer[1024];
749
750         fd = open("small.txt", O_RDWR | O_CREAT, 0644);
751         ATF_REQUIRE(fd >= 0);
752         close(fd);
753         contents_len = strlen(contents) + 1;
754
755         SEND_WRQ("small.txt", "octet");
756         recv_ack(0);
757         /* 
758          * Client "sends" a DATA packet, but network drops it.
759          * Eventually, server should resend the last ACK
760          */
761         recv_ack(0);
762         send_data(1, contents, contents_len);
763         recv_ack(1);
764
765         fd = open("small.txt", O_RDONLY);
766         r = read(fd, buffer, sizeof(buffer));
767         close(fd);
768         require_bufeq(contents, contents_len, buffer, r);
769 }
770
771 /*
772  * Write a medium file, and simulate a duplicated DATA packet
773  */
774 TFTPD_TC_DEFINE(wrq_duped_data,)
775 {
776         int fd;
777         size_t i;
778         ssize_t r;
779         uint32_t contents[192];
780         char buffer[1024];
781
782         for (i = 0; i < nitems(contents); i++)
783                 contents[i] = i;
784
785         fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
786         ATF_REQUIRE(fd >= 0);
787         close(fd);
788
789         SEND_WRQ("medium.txt", "octet");
790         recv_ack(0);
791         send_data(1, (const char*)&contents[0], 512);
792         send_data(1, (const char*)&contents[0], 512);
793         recv_ack(1);
794         recv_ack(1);
795         send_data(2, (const char*)&contents[128], 256);
796         recv_ack(2);
797
798         fd = open("medium.txt", O_RDONLY);
799         r = read(fd, buffer, sizeof(buffer));
800         close(fd);
801         require_bufeq((const char*)contents, 768, buffer, r);
802 }
803
804 /*
805  * Attempt to write a file without write permissions
806  */
807 TFTPD_TC_DEFINE(wrq_eaccess,)
808 {
809         int fd;
810
811         fd = open("empty.txt", O_CREAT | O_RDONLY, 0440);
812         ATF_REQUIRE(fd >= 0);
813         close(fd);
814
815         SEND_WRQ("empty.txt", "octet");
816         atf_tc_expect_fail("PR 225996 tftpd doesn't abort on a WRQ access "
817             "violation");
818         RECV_ERROR(2, "Access violation");
819 }
820
821 /*
822  * Attempt to write a file without world write permissions, but with world
823  * read permissions
824  */
825 TFTPD_TC_DEFINE(wrq_eaccess_world_readable,)
826 {
827         int fd;
828
829         fd = open("empty.txt", O_CREAT | O_RDONLY, 0444);
830         ATF_REQUIRE(fd >= 0);
831         close(fd);
832
833         SEND_WRQ("empty.txt", "octet");
834         atf_tc_expect_fail("PR 226004 with relative pathnames, tftpd doesn't validate world writability");
835         RECV_ERROR(2, "Access violation");
836 }
837
838
839 /*
840  * Write a medium file of more than one block
841  */
842 TFTPD_TC_DEFINE(wrq_medium,)
843 {
844         int fd;
845         size_t i;
846         ssize_t r;
847         uint32_t contents[192];
848         char buffer[1024];
849
850         for (i = 0; i < nitems(contents); i++)
851                 contents[i] = i;
852
853         fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
854         ATF_REQUIRE(fd >= 0);
855         close(fd);
856
857         SEND_WRQ("medium.txt", "octet");
858         recv_ack(0);
859         send_data(1, (const char*)&contents[0], 512);
860         recv_ack(1);
861         send_data(2, (const char*)&contents[128], 256);
862         recv_ack(2);
863
864         fd = open("medium.txt", O_RDONLY);
865         r = read(fd, buffer, sizeof(buffer));
866         close(fd);
867         require_bufeq((const char*)contents, 768, buffer, r);
868 }
869
870 /*
871  * Write a file in netascii format
872  */
873 TFTPD_TC_DEFINE(wrq_netascii,)
874 {
875         int fd;
876         ssize_t r;
877         /* 
878          * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed
879          * is not intended
880          */
881         char contents[] = "foo\r\nbar\r\0baz\r\n";
882         char expected[] = "foo\nbar\rbaz\n";
883         size_t contents_len;
884         char buffer[1024];
885
886         fd = open("unix.txt", O_RDWR | O_CREAT, 0666);
887         ATF_REQUIRE(fd >= 0);
888         close(fd);
889         contents_len = sizeof(contents);
890
891         SEND_WRQ("unix.txt", "netascii");
892         recv_ack(0);
893         send_data(1, contents, contents_len);
894         recv_ack(1);
895
896         fd = open("unix.txt", O_RDONLY);
897         r = read(fd, buffer, sizeof(buffer));
898         close(fd);
899         require_bufeq(expected, sizeof(expected), buffer, r);
900 }
901
902 /*
903  * Attempt to write to a nonexistent file.  With the default options, this
904  * isn't allowed.
905  */
906 TFTPD_TC_DEFINE(wrq_nonexistent,)
907 {
908         SEND_WRQ("nonexistent.txt", "octet");
909         atf_tc_expect_fail("PR 225996 tftpd doesn't abort on a WRQ access "
910             "violation");
911         RECV_ERROR(1, "File not found");
912 }
913
914 /*
915  * Write a small file of less than one block
916  */
917 TFTPD_TC_DEFINE(wrq_small,)
918 {
919         int fd;
920         ssize_t r;
921         char contents[] = "small";
922         size_t contents_len;
923         char buffer[1024];
924
925         fd = open("small.txt", O_RDWR | O_CREAT, 0666);
926         ATF_REQUIRE(fd >= 0);
927         close(fd);
928         contents_len = strlen(contents) + 1;
929
930         SEND_WRQ("small.txt", "octet");
931         recv_ack(0);
932         send_data(1, contents, contents_len);
933         recv_ack(1);
934
935         fd = open("small.txt", O_RDONLY);
936         r = read(fd, buffer, sizeof(buffer));
937         close(fd);
938         require_bufeq(contents, contents_len, buffer, r);
939 }
940
941 /*
942  * Write an empty file over a non-empty one
943  */
944 TFTPD_TC_DEFINE(wrq_truncate,)
945 {
946         int fd;
947         char contents[] = "small";
948         struct stat sb;
949
950         fd = open("small.txt", O_RDWR | O_CREAT, 0666);
951         ATF_REQUIRE(fd >= 0);
952         write_all(fd, contents, strlen(contents) + 1);
953         close(fd);
954
955         SEND_WRQ("small.txt", "octet");
956         recv_ack(0);
957         send_data(1, NULL, 0);
958         recv_ack(1);
959
960         ATF_REQUIRE_EQ(stat("small.txt", &sb), 0);
961         ATF_REQUIRE_EQ(sb.st_size, 0);
962 }
963
964
965 /*
966  * Main
967  */
968
969 ATF_TP_ADD_TCS(tp)
970 {
971         TFTPD_TC_ADD(tp, abspath);
972         TFTPD_TC_ADD(tp, dotdot);
973         TFTPD_TC_ADD(tp, s_flag);
974         TFTPD_TC_ADD(tp, rrq_dropped_ack);
975         TFTPD_TC_ADD(tp, rrq_dropped_data);
976         TFTPD_TC_ADD(tp, rrq_duped_ack);
977         TFTPD_TC_ADD(tp, rrq_eaccess);
978         TFTPD_TC_ADD(tp, rrq_empty);
979         TFTPD_TC_ADD(tp, rrq_medium);
980         TFTPD_TC_ADD(tp, rrq_netascii);
981         TFTPD_TC_ADD(tp, rrq_nonexistent);
982         TFTPD_TC_ADD(tp, rrq_path_max);
983         TFTPD_TC_ADD(tp, rrq_small);
984         TFTPD_TC_ADD(tp, unknown_modes);
985         TFTPD_TC_ADD(tp, unknown_opcode);
986         TFTPD_TC_ADD(tp, w_flag);
987         TFTPD_TC_ADD(tp, wrq_dropped_ack);
988         TFTPD_TC_ADD(tp, wrq_dropped_data);
989         TFTPD_TC_ADD(tp, wrq_duped_data);
990         TFTPD_TC_ADD(tp, wrq_eaccess);
991         TFTPD_TC_ADD(tp, wrq_eaccess_world_readable);
992         TFTPD_TC_ADD(tp, wrq_medium);
993         TFTPD_TC_ADD(tp, wrq_netascii);
994         TFTPD_TC_ADD(tp, wrq_nonexistent);
995         TFTPD_TC_ADD(tp, wrq_small);
996         TFTPD_TC_ADD(tp, wrq_truncate);
997
998         return (atf_no_error());
999 }