functional.c (2aaf9152a852aba9eb2036b95f4948ee77988826) functional.c (fdf929ff91d432d17c4d7d0aa0f9995fffe6fa8e)
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2018 Alan Somers.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:

--- 24 unchanged lines hidden (view full) ---

33#include <sys/stat.h>
34#include <sys/wait.h>
35
36#include <netinet/in.h>
37
38#include <errno.h>
39#include <fcntl.h>
40#include <signal.h>
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2018 Alan Somers.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:

--- 24 unchanged lines hidden (view full) ---

33#include <sys/stat.h>
34#include <sys/wait.h>
35
36#include <netinet/in.h>
37
38#include <errno.h>
39#include <fcntl.h>
40#include <signal.h>
41#include <stdalign.h>
41#include <stdio.h>
42#include <unistd.h>
43
44#include <atf-c.h>
45#include <libutil.h>
46
47static const uint16_t BASEPORT = 6969;
48static const char pidfile[] = "tftpd.pid";

--- 35 unchanged lines hidden (view full) ---

84
85static void
86recv_ack(uint16_t blocknum)
87{
88 char hdr[] = {0, 4, blocknum >> 8, blocknum & 0xFF};
89 RECV(hdr, NULL, 0);
90}
91
42#include <stdio.h>
43#include <unistd.h>
44
45#include <atf-c.h>
46#include <libutil.h>
47
48static const uint16_t BASEPORT = 6969;
49static const char pidfile[] = "tftpd.pid";

--- 35 unchanged lines hidden (view full) ---

85
86static void
87recv_ack(uint16_t blocknum)
88{
89 char hdr[] = {0, 4, blocknum >> 8, blocknum & 0xFF};
90 RECV(hdr, NULL, 0);
91}
92
93static void
94recv_oack(const char *options, size_t options_len)
95{
96 char hdr[] = {0, 6};
97 RECV(hdr, options, options_len);
98}
99
92/*
93 * Receive a data packet from tftpd
94 * @param blocknum Expected block number to be received
95 * @param contents Pointer to expected contents
96 * @param contents_len Length of contents expected to receive
97 */
98static void
99recv_data(uint16_t blocknum, const char* contents, size_t contents_len)

--- 54 unchanged lines hidden (view full) ---

154 blocknum >> 8,
155 blocknum & 0xFF
156 };
157
158 send_bytes(packet, sizeof(packet));
159
160}
161
100/*
101 * Receive a data packet from tftpd
102 * @param blocknum Expected block number to be received
103 * @param contents Pointer to expected contents
104 * @param contents_len Length of contents expected to receive
105 */
106static void
107recv_data(uint16_t blocknum, const char* contents, size_t contents_len)

--- 54 unchanged lines hidden (view full) ---

162 blocknum >> 8,
163 blocknum & 0xFF
164 };
165
166 send_bytes(packet, sizeof(packet));
167
168}
169
170/*
171 * build an option string
172 */
173#define OPTION_STR(name, value) name "\000" value "\000"
174
162/*
163 * send a read request to tftpd.
164 * @param filename filename as a string, absolute or relative
165 * @param mode either "octet" or "netascii"
166 */
167#define SEND_RRQ(filename, mode) SEND_STR("\0\001" filename "\0" mode "\0")
168
175/*
176 * send a read request to tftpd.
177 * @param filename filename as a string, absolute or relative
178 * @param mode either "octet" or "netascii"
179 */
180#define SEND_RRQ(filename, mode) SEND_STR("\0\001" filename "\0" mode "\0")
181
182/*
183 * send a read request with options
184 */
185#define SEND_RRQ_OPT(filename, mode, options) SEND_STR("\0\001" filename "\0" mode "\000" options)
186
169/*
170 * send a write request to tftpd.
171 * @param filename filename as a string, absolute or relative
172 * @param mode either "octet" or "netascii"
173 */
174#define SEND_WRQ(filename, mode) SEND_STR("\0\002" filename "\0" mode "\0")
175
187/*
188 * send a write request to tftpd.
189 * @param filename filename as a string, absolute or relative
190 * @param mode either "octet" or "netascii"
191 */
192#define SEND_WRQ(filename, mode) SEND_STR("\0\002" filename "\0" mode "\0")
193
194/*
195 * send a write request with options
196 */
197#define SEND_WRQ_OPT(filename, mode, options) SEND_STR("\0\002" filename "\0" mode "\000" options)
198
176/* Define a test case, for both IPv4 and IPv6 */
177#define TFTPD_TC_DEFINE(name, head, ...) \
178static void \
179name ## _body(void); \
180ATF_TC_WITH_CLEANUP(name ## _v4); \
181ATF_TC_HEAD(name ## _v4, tc) \
182{ \
183 head \

--- 384 unchanged lines hidden (view full) ---

568 SEND_RRQ("medium.txt", "octet");
569 recv_data(1, (const char*)&contents[0], 512);
570 send_ack(1);
571 recv_data(2, (const char*)&contents[128], 256);
572 send_ack(2);
573}
574
575/*
199/* Define a test case, for both IPv4 and IPv6 */
200#define TFTPD_TC_DEFINE(name, head, ...) \
201static void \
202name ## _body(void); \
203ATF_TC_WITH_CLEANUP(name ## _v4); \
204ATF_TC_HEAD(name ## _v4, tc) \
205{ \
206 head \

--- 384 unchanged lines hidden (view full) ---

591 SEND_RRQ("medium.txt", "octet");
592 recv_data(1, (const char*)&contents[0], 512);
593 send_ack(1);
594 recv_data(2, (const char*)&contents[128], 256);
595 send_ack(2);
596}
597
598/*
599 * Read a medium file with a window size of 2.
600 */
601TFTPD_TC_DEFINE(rrq_medium_window,)
602{
603 int fd;
604 size_t i;
605 uint32_t contents[192];
606 char options[] = OPTION_STR("windowsize", "2");
607
608 for (i = 0; i < nitems(contents); i++)
609 contents[i] = i;
610
611 fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
612 ATF_REQUIRE(fd >= 0);
613 write_all(fd, contents, sizeof(contents));
614 close(fd);
615
616 SEND_RRQ_OPT("medium.txt", "octet", OPTION_STR("windowsize", "2"));
617 recv_oack(options, sizeof(options) - 1);
618 send_ack(0);
619 recv_data(1, (const char*)&contents[0], 512);
620 recv_data(2, (const char*)&contents[128], 256);
621 send_ack(2);
622}
623
624/*
576 * Read a file in netascii format
577 */
578TFTPD_TC_DEFINE(rrq_netascii,)
579{
580 int fd;
581 char contents[] = "foo\nbar\rbaz\n";
582 /*
583 * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed

--- 63 unchanged lines hidden (view full) ---

647 close(fd);
648
649 SEND_RRQ("small.txt", "octet");
650 recv_data(1, contents, strlen(contents) + 1);
651 send_ack(1);
652}
653
654/*
625 * Read a file in netascii format
626 */
627TFTPD_TC_DEFINE(rrq_netascii,)
628{
629 int fd;
630 char contents[] = "foo\nbar\rbaz\n";
631 /*
632 * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed

--- 63 unchanged lines hidden (view full) ---

696 close(fd);
697
698 SEND_RRQ("small.txt", "octet");
699 recv_data(1, contents, strlen(contents) + 1);
700 send_ack(1);
701}
702
703/*
704 * Read a file following the example in RFC 7440.
705 */
706TFTPD_TC_DEFINE(rrq_window_rfc7440,)
707{
708 int fd;
709 size_t i;
710 char options[] = OPTION_STR("windowsize", "4");
711 alignas(uint32_t) char contents[13 * 512 - 4];
712 uint32_t *u32p;
713
714 u32p = (uint32_t *)contents;
715 for (i = 0; i < sizeof(contents) / sizeof(uint32_t); i++)
716 u32p[i] = i;
717
718 fd = open("rfc7440.txt", O_RDWR | O_CREAT, 0644);
719 ATF_REQUIRE(fd >= 0);
720 write_all(fd, contents, sizeof(contents));
721 close(fd);
722
723 SEND_RRQ_OPT("rfc7440.txt", "octet", OPTION_STR("windowsize", "4"));
724 recv_oack(options, sizeof(options) - 1);
725 send_ack(0);
726 recv_data(1, &contents[0 * 512], 512);
727 recv_data(2, &contents[1 * 512], 512);
728 recv_data(3, &contents[2 * 512], 512);
729 recv_data(4, &contents[3 * 512], 512);
730 send_ack(4);
731 recv_data(5, &contents[4 * 512], 512);
732 recv_data(6, &contents[5 * 512], 512);
733 recv_data(7, &contents[6 * 512], 512);
734 recv_data(8, &contents[7 * 512], 512);
735
736 /* ACK 5 as if 6-8 were dropped. */
737 send_ack(5);
738 recv_data(6, &contents[5 * 512], 512);
739 recv_data(7, &contents[6 * 512], 512);
740 recv_data(8, &contents[7 * 512], 512);
741 recv_data(9, &contents[8 * 512], 512);
742 send_ack(9);
743 recv_data(10, &contents[9 * 512], 512);
744 recv_data(11, &contents[10 * 512], 512);
745 recv_data(12, &contents[11 * 512], 512);
746 recv_data(13, &contents[12 * 512], 508);
747
748 /* Drop ACK and after timeout receive 10-13. */
749 recv_data(10, &contents[9 * 512], 512);
750 recv_data(11, &contents[10 * 512], 512);
751 recv_data(12, &contents[11 * 512], 512);
752 recv_data(13, &contents[12 * 512], 508);
753 send_ack(13);
754}
755
756/*
655 * Try to transfer a file with an unknown mode.
656 */
657TFTPD_TC_DEFINE(unknown_modes,)
658{
659 SEND_RRQ("foo.txt", "ascii"); /* Misspelling of "ascii" */
660 RECV_ERROR(4, "Illegal TFTP operation");
661 s = setup(&addr, __COUNTER__); \
662 SEND_RRQ("foo.txt", "binary"); /* Obsolete. Use "octet" instead */

--- 204 unchanged lines hidden (view full) ---

867 fd = open("medium.txt", O_RDONLY);
868 ATF_REQUIRE(fd >= 0);
869 r = read(fd, buffer, sizeof(buffer));
870 close(fd);
871 require_bufeq((const char*)contents, 768, buffer, r);
872}
873
874/*
757 * Try to transfer a file with an unknown mode.
758 */
759TFTPD_TC_DEFINE(unknown_modes,)
760{
761 SEND_RRQ("foo.txt", "ascii"); /* Misspelling of "ascii" */
762 RECV_ERROR(4, "Illegal TFTP operation");
763 s = setup(&addr, __COUNTER__); \
764 SEND_RRQ("foo.txt", "binary"); /* Obsolete. Use "octet" instead */

--- 204 unchanged lines hidden (view full) ---

969 fd = open("medium.txt", O_RDONLY);
970 ATF_REQUIRE(fd >= 0);
971 r = read(fd, buffer, sizeof(buffer));
972 close(fd);
973 require_bufeq((const char*)contents, 768, buffer, r);
974}
975
976/*
977 * Write a medium file with a window size of 2.
978 */
979TFTPD_TC_DEFINE(wrq_medium_window,)
980{
981 int fd;
982 size_t i;
983 ssize_t r;
984 uint32_t contents[192];
985 char buffer[1024];
986 char options[] = OPTION_STR("windowsize", "2");
987
988 for (i = 0; i < nitems(contents); i++)
989 contents[i] = i;
990
991 fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
992 ATF_REQUIRE(fd >= 0);
993 close(fd);
994
995 SEND_WRQ_OPT("medium.txt", "octet", OPTION_STR("windowsize", "2"));
996 recv_oack(options, sizeof(options) - 1);
997 send_data(1, (const char*)&contents[0], 512);
998 send_data(2, (const char*)&contents[128], 256);
999 recv_ack(2);
1000
1001 fd = open("medium.txt", O_RDONLY);
1002 ATF_REQUIRE(fd >= 0);
1003 r = read(fd, buffer, sizeof(buffer));
1004 close(fd);
1005 require_bufeq((const char*)contents, 768, buffer, r);
1006}
1007
1008/*
875 * Write a file in netascii format
876 */
877TFTPD_TC_DEFINE(wrq_netascii,)
878{
879 int fd;
880 ssize_t r;
881 /*
882 * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed

--- 77 unchanged lines hidden (view full) ---

960 recv_ack(0);
961 send_data(1, NULL, 0);
962 recv_ack(1);
963
964 ATF_REQUIRE_EQ(stat("small.txt", &sb), 0);
965 ATF_REQUIRE_EQ(sb.st_size, 0);
966}
967
1009 * Write a file in netascii format
1010 */
1011TFTPD_TC_DEFINE(wrq_netascii,)
1012{
1013 int fd;
1014 ssize_t r;
1015 /*
1016 * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed

--- 77 unchanged lines hidden (view full) ---

1094 recv_ack(0);
1095 send_data(1, NULL, 0);
1096 recv_ack(1);
1097
1098 ATF_REQUIRE_EQ(stat("small.txt", &sb), 0);
1099 ATF_REQUIRE_EQ(sb.st_size, 0);
1100}
1101
1102/*
1103 * Write a file following the example in RFC 7440.
1104 */
1105TFTPD_TC_DEFINE(wrq_window_rfc7440,)
1106{
1107 int fd;
1108 size_t i;
1109 ssize_t r;
1110 char options[] = OPTION_STR("windowsize", "4");
1111 alignas(uint32_t) char contents[13 * 512 - 4];
1112 char buffer[sizeof(contents)];
1113 uint32_t *u32p;
968
1114
1115 u32p = (uint32_t *)contents;
1116 for (i = 0; i < sizeof(contents) / sizeof(uint32_t); i++)
1117 u32p[i] = i;
1118
1119 fd = open("rfc7440.txt", O_RDWR | O_CREAT, 0666);
1120 ATF_REQUIRE(fd >= 0);
1121 close(fd);
1122
1123 SEND_WRQ_OPT("rfc7440.txt", "octet", OPTION_STR("windowsize", "4"));
1124 recv_oack(options, sizeof(options) - 1);
1125 send_data(1, &contents[0 * 512], 512);
1126 send_data(2, &contents[1 * 512], 512);
1127 send_data(3, &contents[2 * 512], 512);
1128 send_data(4, &contents[3 * 512], 512);
1129 recv_ack(4);
1130 send_data(5, &contents[4 * 512], 512);
1131
1132 /* Drop 6-8. */
1133 recv_ack(5);
1134 send_data(6, &contents[5 * 512], 512);
1135 send_data(7, &contents[6 * 512], 512);
1136 send_data(8, &contents[7 * 512], 512);
1137 send_data(9, &contents[8 * 512], 512);
1138 recv_ack(9);
1139
1140 /* Drop 11. */
1141 send_data(10, &contents[9 * 512], 512);
1142 send_data(12, &contents[11 * 512], 512);
1143
1144 /*
1145 * We can't send 13 here as tftpd has probably already seen 12
1146 * and sent the ACK of 10 if running locally. While it would
1147 * recover by sending another ACK of 10, our state machine
1148 * would be out of sync.
1149 */
1150
1151 /* Ignore ACK for 10 and resend 10-13. */
1152 recv_ack(10);
1153 send_data(10, &contents[9 * 512], 512);
1154 send_data(11, &contents[10 * 512], 512);
1155 send_data(12, &contents[11 * 512], 512);
1156 send_data(13, &contents[12 * 512], 508);
1157 recv_ack(13);
1158
1159 fd = open("rfc7440.txt", O_RDONLY);
1160 ATF_REQUIRE(fd >= 0);
1161 r = read(fd, buffer, sizeof(buffer));
1162 close(fd);
1163 require_bufeq(contents, sizeof(contents), buffer, r);
1164}
1165
1166
969/*
970 * Main
971 */
972
973ATF_TP_ADD_TCS(tp)
974{
975 TFTPD_TC_ADD(tp, abspath);
976 TFTPD_TC_ADD(tp, dotdot);
977 TFTPD_TC_ADD(tp, s_flag);
978 TFTPD_TC_ADD(tp, rrq_dropped_ack);
979 TFTPD_TC_ADD(tp, rrq_dropped_data);
980 TFTPD_TC_ADD(tp, rrq_duped_ack);
981 TFTPD_TC_ADD(tp, rrq_eaccess);
982 TFTPD_TC_ADD(tp, rrq_empty);
983 TFTPD_TC_ADD(tp, rrq_medium);
1167/*
1168 * Main
1169 */
1170
1171ATF_TP_ADD_TCS(tp)
1172{
1173 TFTPD_TC_ADD(tp, abspath);
1174 TFTPD_TC_ADD(tp, dotdot);
1175 TFTPD_TC_ADD(tp, s_flag);
1176 TFTPD_TC_ADD(tp, rrq_dropped_ack);
1177 TFTPD_TC_ADD(tp, rrq_dropped_data);
1178 TFTPD_TC_ADD(tp, rrq_duped_ack);
1179 TFTPD_TC_ADD(tp, rrq_eaccess);
1180 TFTPD_TC_ADD(tp, rrq_empty);
1181 TFTPD_TC_ADD(tp, rrq_medium);
1182 TFTPD_TC_ADD(tp, rrq_medium_window);
984 TFTPD_TC_ADD(tp, rrq_netascii);
985 TFTPD_TC_ADD(tp, rrq_nonexistent);
986 TFTPD_TC_ADD(tp, rrq_path_max);
987 TFTPD_TC_ADD(tp, rrq_small);
1183 TFTPD_TC_ADD(tp, rrq_netascii);
1184 TFTPD_TC_ADD(tp, rrq_nonexistent);
1185 TFTPD_TC_ADD(tp, rrq_path_max);
1186 TFTPD_TC_ADD(tp, rrq_small);
1187 TFTPD_TC_ADD(tp, rrq_window_rfc7440);
988 TFTPD_TC_ADD(tp, unknown_modes);
989 TFTPD_TC_ADD(tp, unknown_opcode);
990 TFTPD_TC_ADD(tp, w_flag);
991 TFTPD_TC_ADD(tp, wrq_dropped_ack);
992 TFTPD_TC_ADD(tp, wrq_dropped_data);
993 TFTPD_TC_ADD(tp, wrq_duped_data);
994 TFTPD_TC_ADD(tp, wrq_eaccess);
995 TFTPD_TC_ADD(tp, wrq_eaccess_world_readable);
996 TFTPD_TC_ADD(tp, wrq_medium);
1188 TFTPD_TC_ADD(tp, unknown_modes);
1189 TFTPD_TC_ADD(tp, unknown_opcode);
1190 TFTPD_TC_ADD(tp, w_flag);
1191 TFTPD_TC_ADD(tp, wrq_dropped_ack);
1192 TFTPD_TC_ADD(tp, wrq_dropped_data);
1193 TFTPD_TC_ADD(tp, wrq_duped_data);
1194 TFTPD_TC_ADD(tp, wrq_eaccess);
1195 TFTPD_TC_ADD(tp, wrq_eaccess_world_readable);
1196 TFTPD_TC_ADD(tp, wrq_medium);
1197 TFTPD_TC_ADD(tp, wrq_medium_window);
997 TFTPD_TC_ADD(tp, wrq_netascii);
998 TFTPD_TC_ADD(tp, wrq_nonexistent);
999 TFTPD_TC_ADD(tp, wrq_small);
1000 TFTPD_TC_ADD(tp, wrq_truncate);
1198 TFTPD_TC_ADD(tp, wrq_netascii);
1199 TFTPD_TC_ADD(tp, wrq_nonexistent);
1200 TFTPD_TC_ADD(tp, wrq_small);
1201 TFTPD_TC_ADD(tp, wrq_truncate);
1202 TFTPD_TC_ADD(tp, wrq_window_rfc7440);
1001
1002 return (atf_no_error());
1003}
1203
1204 return (atf_no_error());
1205}