Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/libexec/tftpd/tests/functional.c
35084 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
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:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
#include <sys/param.h>
29
#include <sys/socket.h>
30
#include <sys/stat.h>
31
#include <sys/time.h>
32
#include <sys/wait.h>
33
34
#include <netinet/in.h>
35
36
#include <errno.h>
37
#include <fcntl.h>
38
#include <signal.h>
39
#include <stdalign.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, size_t expected_len,
56
const char *actual, size_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((size_t)r, 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
static void
92
recv_oack(const char *options, size_t options_len)
93
{
94
char hdr[] = {0, 6};
95
RECV(hdr, options, options_len);
96
}
97
98
/*
99
* Receive a data packet from tftpd
100
* @param blocknum Expected block number to be received
101
* @param contents Pointer to expected contents
102
* @param contents_len Length of contents expected to receive
103
*/
104
static void
105
recv_data(uint16_t blocknum, const char *contents, size_t contents_len)
106
{
107
char hdr[] = {0, 3, blocknum >> 8, blocknum & 0xFF};
108
RECV(hdr, contents, contents_len);
109
}
110
111
#define RECV_ERROR(code, msg) do { \
112
char hdr[] = {0, 5, code >> 8, code & 0xFF}; \
113
RECV(hdr, msg, sizeof(msg)); \
114
} while (0)
115
116
/*
117
* send a command to tftpd.
118
* @param cmd Command to send, as a char array
119
*/
120
static void
121
send_bytes(const void *cmd, size_t len)
122
{
123
ssize_t r;
124
125
r = sendto(s, cmd, len, 0, (struct sockaddr *)(&addr), addr.ss_len);
126
ATF_REQUIRE(r >= 0);
127
ATF_REQUIRE_EQ(len, (size_t)r);
128
}
129
130
static void
131
send_data(uint16_t blocknum, const char *contents, size_t contents_len)
132
{
133
char buffer[1024];
134
135
buffer[0] = 0; /* DATA opcode high byte */
136
buffer[1] = 3; /* DATA opcode low byte */
137
buffer[2] = blocknum >> 8;
138
buffer[3] = blocknum & 0xFF;
139
memmove(&buffer[4], contents, contents_len);
140
send_bytes(buffer, 4 + contents_len);
141
}
142
143
/*
144
* send a command to tftpd.
145
* @param cmd Command to send, as a const string
146
* (terminating NUL will be ignored)
147
*/
148
#define SEND_STR(cmd) \
149
ATF_REQUIRE_EQ(sizeof(cmd) - 1, \
150
sendto(s, (cmd), sizeof(cmd) - 1, 0, \
151
(struct sockaddr *)(&addr), addr.ss_len))
152
153
/*
154
* Acknowledge block blocknum
155
*/
156
static void
157
send_ack(uint16_t blocknum)
158
{
159
char packet[] = {
160
0, 4, /* ACK opcode in BE */
161
blocknum >> 8,
162
blocknum & 0xFF
163
};
164
165
send_bytes(packet, sizeof(packet));
166
}
167
168
/*
169
* build an option string
170
*/
171
#define OPTION_STR(name, value) name "\000" value "\000"
172
173
/*
174
* send a read request to tftpd.
175
* @param filename filename as a string, absolute or relative
176
* @param mode either "octet" or "netascii"
177
*/
178
#define SEND_RRQ(filename, mode) \
179
SEND_STR("\0\001" filename "\0" mode "\0")
180
181
/*
182
* send a read request with options
183
*/
184
#define SEND_RRQ_OPT(filename, mode, options) \
185
SEND_STR("\0\001" filename "\0" mode "\000" options)
186
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) \
193
SEND_STR("\0\002" filename "\0" mode "\0")
194
195
/*
196
* send a write request with options
197
*/
198
#define SEND_WRQ_OPT(filename, mode, options) \
199
SEND_STR("\0\002" filename "\0" mode "\000" options)
200
201
/* Define a test case, for both IPv4 and IPv6 */
202
#define TFTPD_TC_DEFINE(name, head, ...) \
203
static void \
204
name ## _body(void); \
205
ATF_TC_WITH_CLEANUP(name ## _v4); \
206
ATF_TC_HEAD(name ## _v4, tc) \
207
{ \
208
head \
209
} \
210
ATF_TC_BODY(name ## _v4, tc) \
211
{ \
212
int exitcode = 0; \
213
__VA_ARGS__; \
214
protocol = AF_INET; \
215
s = setup(&addr, __COUNTER__); \
216
name ## _body(); \
217
close(s); \
218
if (exitcode >= 0) \
219
check_server(exitcode); \
220
} \
221
ATF_TC_CLEANUP(name ## _v4, tc) \
222
{ \
223
cleanup(); \
224
} \
225
ATF_TC_WITH_CLEANUP(name ## _v6); \
226
ATF_TC_HEAD(name ## _v6, tc) \
227
{ \
228
head \
229
} \
230
ATF_TC_BODY(name ## _v6, tc) \
231
{ \
232
int exitcode = 0; \
233
__VA_ARGS__; \
234
protocol = AF_INET6; \
235
s = setup(&addr, __COUNTER__); \
236
name ## _body(); \
237
close(s); \
238
if (exitcode >= 0) \
239
check_server(exitcode); \
240
} \
241
ATF_TC_CLEANUP(name ## _v6, tc) \
242
{ \
243
cleanup(); \
244
} \
245
static void \
246
name ## _body(void)
247
248
/* Add the IPv4 and IPv6 versions of a test case */
249
#define TFTPD_TC_ADD(tp, name) do { \
250
ATF_TP_ADD_TC(tp, name ## _v4); \
251
ATF_TP_ADD_TC(tp, name ## _v6); \
252
} while (0)
253
254
static void
255
sigalrm(int signo __unused)
256
{
257
}
258
259
/* Check that server exits with specific exit code */
260
static void
261
check_server(int exitcode)
262
{
263
struct sigaction sa = { .sa_handler = sigalrm };
264
struct itimerval it = { .it_value = { .tv_sec = 30 } };
265
FILE *f;
266
pid_t pid;
267
int wstatus;
268
269
f = fopen(pidfile, "r");
270
ATF_REQUIRE(f != NULL);
271
ATF_REQUIRE_INTEQ(1, fscanf(f, "%d", &pid));
272
ATF_CHECK_INTEQ(0, fclose(f));
273
ATF_REQUIRE_INTEQ(0, sigaction(SIGALRM, &sa, NULL));
274
ATF_REQUIRE_EQ(0, setitimer(ITIMER_REAL, &it, NULL));
275
ATF_REQUIRE_EQ(pid, waitpid(pid, &wstatus, 0));
276
ATF_CHECK(WIFEXITED(wstatus));
277
ATF_CHECK_INTEQ(exitcode, WEXITSTATUS(wstatus));
278
unlink(pidfile);
279
}
280
281
/* Standard cleanup used by all testcases */
282
static void
283
cleanup(void)
284
{
285
FILE *f;
286
pid_t pid;
287
288
f = fopen(pidfile, "r");
289
if (f == NULL)
290
return;
291
unlink(pidfile);
292
if (fscanf(f, "%d", &pid) == 1) {
293
kill(pid, SIGTERM);
294
waitpid(pid, NULL, 0);
295
}
296
fclose(f);
297
}
298
299
/* Assert that two binary buffers are identical */
300
static void
301
require_bufeq(const char *expected, size_t expected_len,
302
const char *actual, size_t len)
303
{
304
size_t i;
305
306
ATF_REQUIRE_EQ_MSG(expected_len, len,
307
"Expected %zu bytes but got %zu", expected_len, len);
308
for (i = 0; i < len; i++) {
309
ATF_REQUIRE_EQ_MSG(expected[i], actual[i],
310
"Expected %#hhx at position %zu; got %hhx instead",
311
expected[i], i, actual[i]);
312
}
313
}
314
315
/*
316
* Start tftpd and return its communicating socket
317
* @param to Will be filled in for use with sendto
318
* @param idx Unique identifier of the test case
319
* @return Socket ready to use
320
*/
321
static int
322
setup(struct sockaddr_storage *to, uint16_t idx)
323
{
324
int client_s, server_s, pid, argv_idx;
325
char execname[] = "/usr/libexec/tftpd";
326
char b_flag_str[] = "-b";
327
char s_flag_str[] = "-s";
328
char w_flag_str[] = "-w";
329
char pwd[MAXPATHLEN];
330
char *argv[10];
331
struct sockaddr_in addr4;
332
struct sockaddr_in6 addr6;
333
struct sockaddr *server_addr;
334
struct pidfh *pfh;
335
uint16_t port = BASEPORT + idx;
336
socklen_t len;
337
int pd[2];
338
339
ATF_REQUIRE_EQ(0, pipe2(pd, O_CLOEXEC));
340
341
if (protocol == PF_INET) {
342
len = sizeof(addr4);
343
bzero(&addr4, len);
344
addr4.sin_len = len;
345
addr4.sin_family = PF_INET;
346
addr4.sin_port = htons(port);
347
server_addr = (struct sockaddr *)&addr4;
348
} else {
349
len = sizeof(addr6);
350
bzero(&addr6, len);
351
addr6.sin6_len = len;
352
addr6.sin6_family = PF_INET6;
353
addr6.sin6_port = htons(port);
354
server_addr = (struct sockaddr *)&addr6;
355
}
356
357
ATF_REQUIRE_EQ(pwd, getcwd(pwd, sizeof(pwd)));
358
359
/* Must bind(2) pre-fork so it happens before the client's send(2) */
360
server_s = socket(protocol, SOCK_DGRAM, 0);
361
if (server_s < 0 && errno == EAFNOSUPPORT) {
362
atf_tc_skip("This test requires IPv%d support",
363
protocol == PF_INET ? 4 : 6);
364
}
365
ATF_REQUIRE_MSG(server_s >= 0,
366
"socket failed with error %s", strerror(errno));
367
ATF_REQUIRE_EQ_MSG(0, bind(server_s, server_addr, len),
368
"bind failed with error %s", strerror(errno));
369
370
pid = fork();
371
switch (pid) {
372
case -1:
373
atf_tc_fail("fork failed");
374
break;
375
case 0:
376
/* In child */
377
pfh = pidfile_open(pidfile, 0644, NULL);
378
ATF_REQUIRE_MSG(pfh != NULL,
379
"pidfile_open: %s", strerror(errno));
380
ATF_REQUIRE_EQ(0, pidfile_write(pfh));
381
ATF_REQUIRE_EQ(0, pidfile_close(pfh));
382
383
bzero(argv, sizeof(argv));
384
argv[0] = execname;
385
argv_idx = 1;
386
argv[argv_idx++] = b_flag_str;
387
if (w_flag)
388
argv[argv_idx++] = w_flag_str;
389
if (s_flag)
390
argv[argv_idx++] = s_flag_str;
391
argv[argv_idx++] = pwd;
392
ATF_REQUIRE_EQ(STDOUT_FILENO, dup2(server_s, STDOUT_FILENO));
393
ATF_REQUIRE_EQ(STDIN_FILENO, dup2(server_s, STDIN_FILENO));
394
ATF_REQUIRE_EQ(STDERR_FILENO, dup2(server_s, STDERR_FILENO));
395
execv(execname, argv);
396
atf_tc_fail("exec failed");
397
break;
398
default:
399
/* In parent */
400
ATF_REQUIRE_INTEQ(0, close(pd[1]));
401
/* block until other end is closed on exec() or exit() */
402
ATF_REQUIRE_INTEQ(0, read(pd[0], &pd[1], sizeof(pd[1])));
403
ATF_REQUIRE_INTEQ(0, close(pd[0]));
404
bzero(to, sizeof(*to));
405
if (protocol == PF_INET) {
406
struct sockaddr_in *to4 = (struct sockaddr_in *)to;
407
to4->sin_len = sizeof(*to4);
408
to4->sin_family = PF_INET;
409
to4->sin_port = htons(port);
410
to4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
411
} else {
412
struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT;
413
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)to;
414
to6->sin6_len = sizeof(*to6);
415
to6->sin6_family = PF_INET6;
416
to6->sin6_port = htons(port);
417
to6->sin6_addr = loopback;
418
}
419
420
ATF_REQUIRE_INTEQ(0, close(server_s));
421
ATF_REQUIRE((client_s = socket(protocol, SOCK_DGRAM, 0)) > 0);
422
break;
423
}
424
425
/* Clear the client's umask. Test cases will specify exact modes */
426
umask(0000);
427
428
return (client_s);
429
}
430
431
/* Like write(2), but never returns less than the requested length */
432
static void
433
write_all(int fd, const void *buf, size_t nbytes)
434
{
435
ssize_t r;
436
437
while (nbytes > 0) {
438
r = write(fd, buf, nbytes);
439
ATF_REQUIRE(r > 0);
440
nbytes -= (size_t)r;
441
buf = (const char *)buf + (size_t)r;
442
}
443
}
444
445
446
/*
447
* Test Cases
448
*/
449
450
/*
451
* Read a file, specified by absolute pathname.
452
*/
453
TFTPD_TC_DEFINE(abspath,)
454
{
455
int fd;
456
char command[1024];
457
size_t pathlen;
458
char suffix[] = {'\0', 'o', 'c', 't', 'e', 't', '\0'};
459
460
command[0] = 0; /* RRQ high byte */
461
command[1] = 1; /* RRQ low byte */
462
ATF_REQUIRE(getcwd(&command[2], sizeof(command) - 2) != NULL);
463
pathlen = strlcat(&command[2], "/abspath.txt", sizeof(command) - 2);
464
ATF_REQUIRE(pathlen + sizeof(suffix) < sizeof(command) - 2);
465
memmove(&command[2 + pathlen], suffix, sizeof(suffix));
466
467
fd = open("abspath.txt", O_CREAT | O_RDONLY, 0644);
468
ATF_REQUIRE(fd >= 0);
469
close(fd);
470
471
send_bytes(command, 2 + pathlen + sizeof(suffix));
472
recv_data(1, NULL, 0);
473
send_ack(1);
474
}
475
476
/*
477
* Attempt to read a file outside of the allowed directory(ies)
478
*/
479
TFTPD_TC_DEFINE(dotdot,)
480
{
481
ATF_REQUIRE_EQ(0, mkdir("subdir", 0777));
482
SEND_RRQ("../disallowed.txt", "octet");
483
RECV_ERROR(2, "Access violation");
484
s = setup(&addr, __COUNTER__);
485
SEND_RRQ("subdir/../../disallowed.txt", "octet");
486
RECV_ERROR(2, "Access violation");
487
s = setup(&addr, __COUNTER__);
488
SEND_RRQ("/etc/passwd", "octet");
489
RECV_ERROR(2, "Access violation");
490
}
491
492
/*
493
* With "-s", tftpd should chroot to the specified directory
494
*/
495
TFTPD_TC_DEFINE(s_flag,
496
atf_tc_set_md_var(tc, "require.user", "root");,
497
s_flag = true)
498
{
499
int fd;
500
char contents[] = "small";
501
502
fd = open("small.txt", O_RDWR | O_CREAT, 0644);
503
ATF_REQUIRE(fd >= 0);
504
write_all(fd, contents, strlen(contents) + 1);
505
close(fd);
506
507
SEND_RRQ("/small.txt", "octet");
508
recv_data(1, contents, strlen(contents) + 1);
509
send_ack(1);
510
}
511
512
/*
513
* Read a file, and simulate a dropped ACK packet
514
*/
515
TFTPD_TC_DEFINE(rrq_dropped_ack,)
516
{
517
int fd;
518
char contents[] = "small";
519
520
fd = open("small.txt", O_RDWR | O_CREAT, 0644);
521
ATF_REQUIRE(fd >= 0);
522
write_all(fd, contents, strlen(contents) + 1);
523
close(fd);
524
525
SEND_RRQ("small.txt", "octet");
526
recv_data(1, contents, strlen(contents) + 1);
527
/*
528
* client "sends" the ack, but network drops it
529
* Eventually, tftpd should resend the data packet
530
*/
531
recv_data(1, contents, strlen(contents) + 1);
532
send_ack(1);
533
}
534
535
/*
536
* Read a file, and simulate a dropped DATA packet
537
*/
538
TFTPD_TC_DEFINE(rrq_dropped_data,)
539
{
540
int fd;
541
size_t i;
542
uint32_t contents[192];
543
char buffer[1024];
544
545
for (i = 0; i < nitems(contents); i++)
546
contents[i] = i;
547
548
fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
549
ATF_REQUIRE(fd >= 0);
550
write_all(fd, contents, sizeof(contents));
551
close(fd);
552
553
SEND_RRQ("medium.txt", "octet");
554
recv_data(1, (const char *)&contents[0], 512);
555
send_ack(1);
556
(void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL);
557
/*
558
* server "sends" the data, but network drops it
559
* Eventually, client should resend the last ACK
560
*/
561
send_ack(1);
562
recv_data(2, (const char *)&contents[128], 256);
563
send_ack(2);
564
}
565
566
/*
567
* Read a medium file, and simulate a duplicated ACK packet
568
*/
569
TFTPD_TC_DEFINE(rrq_duped_ack,)
570
{
571
int fd;
572
size_t i;
573
uint32_t contents[192];
574
575
for (i = 0; i < nitems(contents); i++)
576
contents[i] = i;
577
578
fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
579
ATF_REQUIRE(fd >= 0);
580
write_all(fd, contents, sizeof(contents));
581
close(fd);
582
583
SEND_RRQ("medium.txt", "octet");
584
recv_data(1, (const char *)&contents[0], 512);
585
send_ack(1);
586
send_ack(1); /* Dupe an ACK packet */
587
recv_data(2, (const char *)&contents[128], 256);
588
recv_data(2, (const char *)&contents[128], 256);
589
send_ack(2);
590
}
591
592
593
/*
594
* Attempt to read a file without read permissions
595
*/
596
TFTPD_TC_DEFINE(rrq_eaccess,)
597
{
598
int fd;
599
600
fd = open("empty.txt", O_CREAT | O_RDONLY, 0000);
601
ATF_REQUIRE(fd >= 0);
602
close(fd);
603
604
SEND_RRQ("empty.txt", "octet");
605
RECV_ERROR(2, "Access violation");
606
}
607
608
/*
609
* Read an empty file
610
*/
611
TFTPD_TC_DEFINE(rrq_empty,)
612
{
613
int fd;
614
615
fd = open("empty.txt", O_CREAT | O_RDONLY, 0644);
616
ATF_REQUIRE(fd >= 0);
617
close(fd);
618
619
SEND_RRQ("empty.txt", "octet");
620
recv_data(1, NULL, 0);
621
send_ack(1);
622
}
623
624
/*
625
* Read a medium file of more than one block
626
*/
627
TFTPD_TC_DEFINE(rrq_medium,)
628
{
629
int fd;
630
size_t i;
631
uint32_t contents[192];
632
633
for (i = 0; i < nitems(contents); i++)
634
contents[i] = i;
635
636
fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
637
ATF_REQUIRE(fd >= 0);
638
write_all(fd, contents, sizeof(contents));
639
close(fd);
640
641
SEND_RRQ("medium.txt", "octet");
642
recv_data(1, (const char *)&contents[0], 512);
643
send_ack(1);
644
recv_data(2, (const char *)&contents[128], 256);
645
send_ack(2);
646
}
647
648
/*
649
* Read a medium file with a window size of 2.
650
*/
651
TFTPD_TC_DEFINE(rrq_medium_window,)
652
{
653
int fd;
654
size_t i;
655
uint32_t contents[192];
656
char options[] = OPTION_STR("windowsize", "2");
657
658
for (i = 0; i < nitems(contents); i++)
659
contents[i] = i;
660
661
fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
662
ATF_REQUIRE(fd >= 0);
663
write_all(fd, contents, sizeof(contents));
664
close(fd);
665
666
SEND_RRQ_OPT("medium.txt", "octet", OPTION_STR("windowsize", "2"));
667
recv_oack(options, sizeof(options) - 1);
668
send_ack(0);
669
recv_data(1, (const char *)&contents[0], 512);
670
recv_data(2, (const char *)&contents[128], 256);
671
send_ack(2);
672
}
673
674
/*
675
* Read a file in netascii format
676
*/
677
TFTPD_TC_DEFINE(rrq_netascii,)
678
{
679
int fd;
680
char contents[] = "foo\nbar\rbaz\n";
681
/*
682
* Weirdly, RFC-764 says that CR must be followed by NUL if a line feed
683
* is not intended
684
*/
685
char expected[] = "foo\r\nbar\r\0baz\r\n";
686
687
fd = open("unix.txt", O_RDWR | O_CREAT, 0644);
688
ATF_REQUIRE(fd >= 0);
689
write_all(fd, contents, strlen(contents) + 1);
690
close(fd);
691
692
SEND_RRQ("unix.txt", "netascii");
693
recv_data(1, expected, sizeof(expected));
694
send_ack(1);
695
}
696
697
/*
698
* Read a file that doesn't exist
699
*/
700
TFTPD_TC_DEFINE(rrq_nonexistent,)
701
{
702
SEND_RRQ("nonexistent.txt", "octet");
703
RECV_ERROR(1, "File not found");
704
}
705
706
/*
707
* Attempt to read a file whose name exceeds PATH_MAX
708
*/
709
TFTPD_TC_DEFINE(rrq_path_max,)
710
{
711
#define AReallyBigFileName \
712
"AReallyBigFileNameXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
713
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
714
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
715
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
716
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
717
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
718
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
719
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
720
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
721
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
722
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
723
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
724
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
725
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
726
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
727
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
728
".txt"
729
ATF_REQUIRE_MSG(strlen(AReallyBigFileName) > PATH_MAX,
730
"Somebody increased PATH_MAX. Update the test");
731
SEND_RRQ(AReallyBigFileName, "octet");
732
RECV_ERROR(4, "Illegal TFTP operation");
733
}
734
735
/*
736
* Read a small file of less than one block
737
*/
738
TFTPD_TC_DEFINE(rrq_small,)
739
{
740
int fd;
741
char contents[] = "small";
742
743
fd = open("small.txt", O_RDWR | O_CREAT, 0644);
744
ATF_REQUIRE(fd >= 0);
745
write_all(fd, contents, strlen(contents) + 1);
746
close(fd);
747
748
SEND_RRQ("small.txt", "octet");
749
recv_data(1, contents, strlen(contents) + 1);
750
send_ack(1);
751
}
752
753
/*
754
* Read a file following the example in RFC 7440.
755
*/
756
TFTPD_TC_DEFINE(rrq_window_rfc7440,)
757
{
758
int fd;
759
size_t i;
760
char options[] = OPTION_STR("windowsize", "4");
761
alignas(uint32_t) char contents[13 * 512 - 4];
762
uint32_t *u32p;
763
764
u32p = (uint32_t *)contents;
765
for (i = 0; i < sizeof(contents) / sizeof(uint32_t); i++)
766
u32p[i] = i;
767
768
fd = open("rfc7440.txt", O_RDWR | O_CREAT, 0644);
769
ATF_REQUIRE(fd >= 0);
770
write_all(fd, contents, sizeof(contents));
771
close(fd);
772
773
SEND_RRQ_OPT("rfc7440.txt", "octet", OPTION_STR("windowsize", "4"));
774
recv_oack(options, sizeof(options) - 1);
775
send_ack(0);
776
recv_data(1, &contents[0 * 512], 512);
777
recv_data(2, &contents[1 * 512], 512);
778
recv_data(3, &contents[2 * 512], 512);
779
recv_data(4, &contents[3 * 512], 512);
780
send_ack(4);
781
recv_data(5, &contents[4 * 512], 512);
782
recv_data(6, &contents[5 * 512], 512);
783
recv_data(7, &contents[6 * 512], 512);
784
recv_data(8, &contents[7 * 512], 512);
785
786
/* ACK 5 as if 6-8 were dropped. */
787
send_ack(5);
788
recv_data(6, &contents[5 * 512], 512);
789
recv_data(7, &contents[6 * 512], 512);
790
recv_data(8, &contents[7 * 512], 512);
791
recv_data(9, &contents[8 * 512], 512);
792
send_ack(9);
793
recv_data(10, &contents[9 * 512], 512);
794
recv_data(11, &contents[10 * 512], 512);
795
recv_data(12, &contents[11 * 512], 512);
796
recv_data(13, &contents[12 * 512], 508);
797
798
/* Drop ACK and after timeout receive 10-13. */
799
recv_data(10, &contents[9 * 512], 512);
800
recv_data(11, &contents[10 * 512], 512);
801
recv_data(12, &contents[11 * 512], 512);
802
recv_data(13, &contents[12 * 512], 508);
803
send_ack(13);
804
}
805
806
/*
807
* Try to transfer a file with an unknown mode.
808
*/
809
TFTPD_TC_DEFINE(unknown_modes,)
810
{
811
SEND_RRQ("foo.txt", "ascii"); /* Misspelling of "ascii" */
812
RECV_ERROR(4, "Illegal TFTP operation");
813
s = setup(&addr, __COUNTER__);
814
SEND_RRQ("foo.txt", "binary"); /* Obsolete. Use "octet" instead */
815
RECV_ERROR(4, "Illegal TFTP operation");
816
s = setup(&addr, __COUNTER__);
817
SEND_RRQ("foo.txt", "en_US.UTF-8");
818
RECV_ERROR(4, "Illegal TFTP operation");
819
s = setup(&addr, __COUNTER__);
820
SEND_RRQ("foo.txt", "mail"); /* Obsolete in RFC-1350 */
821
RECV_ERROR(4, "Illegal TFTP operation");
822
}
823
824
/*
825
* Send an unknown opcode. tftpd should respond with the appropriate error
826
*/
827
TFTPD_TC_DEFINE(unknown_opcode,)
828
{
829
/* Looks like an RRQ or WRQ request, but with a bad opcode */
830
SEND_STR("\0\007foo.txt\0octet\0");
831
RECV_ERROR(4, "Illegal TFTP operation");
832
}
833
834
/*
835
* Invoke tftpd with "-w" and write to a nonexistent file.
836
*/
837
TFTPD_TC_DEFINE(w_flag,, w_flag = 1;)
838
{
839
int fd;
840
ssize_t r;
841
char contents[] = "small";
842
char buffer[1024];
843
size_t contents_len;
844
845
contents_len = strlen(contents) + 1;
846
SEND_WRQ("small.txt", "octet");
847
recv_ack(0);
848
send_data(1, contents, contents_len);
849
recv_ack(1);
850
851
fd = open("small.txt", O_RDONLY);
852
ATF_REQUIRE(fd >= 0);
853
r = read(fd, buffer, sizeof(buffer));
854
ATF_REQUIRE(r > 0);
855
close(fd);
856
require_bufeq(contents, contents_len, buffer, (size_t)r);
857
}
858
859
/*
860
* Write a medium file, and simulate a dropped ACK packet
861
*/
862
TFTPD_TC_DEFINE(wrq_dropped_ack,)
863
{
864
int fd;
865
size_t i;
866
ssize_t r;
867
uint32_t contents[192];
868
char buffer[1024];
869
870
for (i = 0; i < nitems(contents); i++)
871
contents[i] = i;
872
873
fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
874
ATF_REQUIRE(fd >= 0);
875
close(fd);
876
877
SEND_WRQ("medium.txt", "octet");
878
recv_ack(0);
879
send_data(1, (const char *)&contents[0], 512);
880
/*
881
* Servers "sends" an ACK packet, but network drops it.
882
* Eventually, server should resend the last ACK
883
*/
884
(void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL);
885
recv_ack(1);
886
send_data(2, (const char *)&contents[128], 256);
887
recv_ack(2);
888
889
fd = open("medium.txt", O_RDONLY);
890
ATF_REQUIRE(fd >= 0);
891
r = read(fd, buffer, sizeof(buffer));
892
ATF_REQUIRE(r > 0);
893
close(fd);
894
require_bufeq((const char *)contents, 768, buffer, (size_t)r);
895
}
896
897
/*
898
* Write a small file, and simulate a dropped DATA packet
899
*/
900
TFTPD_TC_DEFINE(wrq_dropped_data,)
901
{
902
int fd;
903
ssize_t r;
904
char contents[] = "small";
905
size_t contents_len;
906
char buffer[1024];
907
908
fd = open("small.txt", O_RDWR | O_CREAT, 0666);
909
ATF_REQUIRE(fd >= 0);
910
close(fd);
911
contents_len = strlen(contents) + 1;
912
913
SEND_WRQ("small.txt", "octet");
914
recv_ack(0);
915
/*
916
* Client "sends" a DATA packet, but network drops it.
917
* Eventually, server should resend the last ACK
918
*/
919
recv_ack(0);
920
send_data(1, contents, contents_len);
921
recv_ack(1);
922
923
fd = open("small.txt", O_RDONLY);
924
ATF_REQUIRE(fd >= 0);
925
r = read(fd, buffer, sizeof(buffer));
926
ATF_REQUIRE(r > 0);
927
close(fd);
928
require_bufeq(contents, contents_len, buffer, (size_t)r);
929
}
930
931
/*
932
* Write a medium file, and simulate a duplicated DATA packet
933
*/
934
TFTPD_TC_DEFINE(wrq_duped_data,)
935
{
936
int fd;
937
size_t i;
938
ssize_t r;
939
uint32_t contents[192];
940
char buffer[1024];
941
942
for (i = 0; i < nitems(contents); i++)
943
contents[i] = i;
944
945
fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
946
ATF_REQUIRE(fd >= 0);
947
close(fd);
948
949
SEND_WRQ("medium.txt", "octet");
950
recv_ack(0);
951
send_data(1, (const char *)&contents[0], 512);
952
send_data(1, (const char *)&contents[0], 512);
953
recv_ack(1);
954
recv_ack(1);
955
send_data(2, (const char *)&contents[128], 256);
956
recv_ack(2);
957
958
fd = open("medium.txt", O_RDONLY);
959
ATF_REQUIRE(fd >= 0);
960
r = read(fd, buffer, sizeof(buffer));
961
ATF_REQUIRE(r > 0);
962
close(fd);
963
require_bufeq((const char *)contents, 768, buffer, (size_t)r);
964
}
965
966
/*
967
* Attempt to write a file without write permissions
968
*/
969
TFTPD_TC_DEFINE(wrq_eaccess,)
970
{
971
int fd;
972
973
fd = open("empty.txt", O_CREAT | O_RDONLY, 0440);
974
ATF_REQUIRE(fd >= 0);
975
close(fd);
976
977
SEND_WRQ("empty.txt", "octet");
978
RECV_ERROR(2, "Access violation");
979
}
980
981
/*
982
* Attempt to write a file without world write permissions, but with world
983
* read permissions
984
*/
985
TFTPD_TC_DEFINE(wrq_eaccess_world_readable,)
986
{
987
int fd;
988
989
fd = open("empty.txt", O_CREAT | O_RDONLY, 0444);
990
ATF_REQUIRE(fd >= 0);
991
close(fd);
992
993
SEND_WRQ("empty.txt", "octet");
994
RECV_ERROR(2, "Access violation");
995
}
996
997
998
/*
999
* Write a medium file of more than one block
1000
*/
1001
TFTPD_TC_DEFINE(wrq_medium,)
1002
{
1003
int fd;
1004
size_t i;
1005
ssize_t r;
1006
uint32_t contents[192];
1007
char buffer[1024];
1008
1009
for (i = 0; i < nitems(contents); i++)
1010
contents[i] = i;
1011
1012
fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
1013
ATF_REQUIRE(fd >= 0);
1014
close(fd);
1015
1016
SEND_WRQ("medium.txt", "octet");
1017
recv_ack(0);
1018
send_data(1, (const char *)&contents[0], 512);
1019
recv_ack(1);
1020
send_data(2, (const char *)&contents[128], 256);
1021
recv_ack(2);
1022
1023
fd = open("medium.txt", O_RDONLY);
1024
ATF_REQUIRE(fd >= 0);
1025
r = read(fd, buffer, sizeof(buffer));
1026
ATF_REQUIRE(r > 0);
1027
close(fd);
1028
require_bufeq((const char *)contents, 768, buffer, (size_t)r);
1029
}
1030
1031
/*
1032
* Write a medium file with a window size of 2.
1033
*/
1034
TFTPD_TC_DEFINE(wrq_medium_window,)
1035
{
1036
int fd;
1037
size_t i;
1038
ssize_t r;
1039
uint32_t contents[192];
1040
char buffer[1024];
1041
char options[] = OPTION_STR("windowsize", "2");
1042
1043
for (i = 0; i < nitems(contents); i++)
1044
contents[i] = i;
1045
1046
fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
1047
ATF_REQUIRE(fd >= 0);
1048
close(fd);
1049
1050
SEND_WRQ_OPT("medium.txt", "octet", OPTION_STR("windowsize", "2"));
1051
recv_oack(options, sizeof(options) - 1);
1052
send_data(1, (const char *)&contents[0], 512);
1053
send_data(2, (const char *)&contents[128], 256);
1054
recv_ack(2);
1055
1056
fd = open("medium.txt", O_RDONLY);
1057
ATF_REQUIRE(fd >= 0);
1058
r = read(fd, buffer, sizeof(buffer));
1059
ATF_REQUIRE(r > 0);
1060
close(fd);
1061
require_bufeq((const char *)contents, 768, buffer, (size_t)r);
1062
}
1063
1064
/*
1065
* Write a file in netascii format
1066
*/
1067
TFTPD_TC_DEFINE(wrq_netascii,)
1068
{
1069
int fd;
1070
ssize_t r;
1071
/*
1072
* Weirdly, RFC-764 says that CR must be followed by NUL if a line feed
1073
* is not intended
1074
*/
1075
char contents[] = "foo\r\nbar\r\0baz\r\n";
1076
char expected[] = "foo\nbar\rbaz\n";
1077
size_t contents_len;
1078
char buffer[1024];
1079
1080
fd = open("unix.txt", O_RDWR | O_CREAT, 0666);
1081
ATF_REQUIRE(fd >= 0);
1082
close(fd);
1083
contents_len = sizeof(contents);
1084
1085
SEND_WRQ("unix.txt", "netascii");
1086
recv_ack(0);
1087
send_data(1, contents, contents_len);
1088
recv_ack(1);
1089
1090
fd = open("unix.txt", O_RDONLY);
1091
ATF_REQUIRE(fd >= 0);
1092
r = read(fd, buffer, sizeof(buffer));
1093
ATF_REQUIRE(r > 0);
1094
close(fd);
1095
require_bufeq(expected, sizeof(expected), buffer, (size_t)r);
1096
}
1097
1098
/*
1099
* Attempt to write to a nonexistent file. With the default options, this
1100
* isn't allowed.
1101
*/
1102
TFTPD_TC_DEFINE(wrq_nonexistent,)
1103
{
1104
SEND_WRQ("nonexistent.txt", "octet");
1105
RECV_ERROR(1, "File not found");
1106
}
1107
1108
/*
1109
* Write a small file of less than one block
1110
*/
1111
TFTPD_TC_DEFINE(wrq_small,)
1112
{
1113
int fd;
1114
ssize_t r;
1115
char contents[] = "small";
1116
size_t contents_len;
1117
char buffer[1024];
1118
1119
fd = open("small.txt", O_RDWR | O_CREAT, 0666);
1120
ATF_REQUIRE(fd >= 0);
1121
close(fd);
1122
contents_len = strlen(contents) + 1;
1123
1124
SEND_WRQ("small.txt", "octet");
1125
recv_ack(0);
1126
send_data(1, contents, contents_len);
1127
recv_ack(1);
1128
1129
fd = open("small.txt", O_RDONLY);
1130
ATF_REQUIRE(fd >= 0);
1131
r = read(fd, buffer, sizeof(buffer));
1132
ATF_REQUIRE(r > 0);
1133
close(fd);
1134
require_bufeq(contents, contents_len, buffer, (size_t)r);
1135
}
1136
1137
/*
1138
* Write an empty file over a non-empty one
1139
*/
1140
TFTPD_TC_DEFINE(wrq_truncate,)
1141
{
1142
int fd;
1143
char contents[] = "small";
1144
struct stat sb;
1145
1146
fd = open("small.txt", O_RDWR | O_CREAT, 0666);
1147
ATF_REQUIRE(fd >= 0);
1148
write_all(fd, contents, strlen(contents) + 1);
1149
close(fd);
1150
1151
SEND_WRQ("small.txt", "octet");
1152
recv_ack(0);
1153
send_data(1, NULL, 0);
1154
recv_ack(1);
1155
1156
ATF_REQUIRE_EQ(0, stat("small.txt", &sb));
1157
ATF_REQUIRE_EQ(0, sb.st_size);
1158
}
1159
1160
/*
1161
* Write a file following the example in RFC 7440.
1162
*/
1163
TFTPD_TC_DEFINE(wrq_window_rfc7440,)
1164
{
1165
int fd;
1166
size_t i;
1167
ssize_t r;
1168
char options[] = OPTION_STR("windowsize", "4");
1169
alignas(uint32_t) char contents[13 * 512 - 4];
1170
char buffer[sizeof(contents)];
1171
uint32_t *u32p;
1172
1173
u32p = (uint32_t *)contents;
1174
for (i = 0; i < sizeof(contents) / sizeof(uint32_t); i++)
1175
u32p[i] = i;
1176
1177
fd = open("rfc7440.txt", O_RDWR | O_CREAT, 0666);
1178
ATF_REQUIRE(fd >= 0);
1179
close(fd);
1180
1181
SEND_WRQ_OPT("rfc7440.txt", "octet", OPTION_STR("windowsize", "4"));
1182
recv_oack(options, sizeof(options) - 1);
1183
send_data(1, &contents[0 * 512], 512);
1184
send_data(2, &contents[1 * 512], 512);
1185
send_data(3, &contents[2 * 512], 512);
1186
send_data(4, &contents[3 * 512], 512);
1187
recv_ack(4);
1188
send_data(5, &contents[4 * 512], 512);
1189
1190
/* Drop 6-8. */
1191
recv_ack(5);
1192
send_data(6, &contents[5 * 512], 512);
1193
send_data(7, &contents[6 * 512], 512);
1194
send_data(8, &contents[7 * 512], 512);
1195
send_data(9, &contents[8 * 512], 512);
1196
recv_ack(9);
1197
1198
/* Drop 11. */
1199
send_data(10, &contents[9 * 512], 512);
1200
send_data(12, &contents[11 * 512], 512);
1201
1202
/*
1203
* We can't send 13 here as tftpd has probably already seen 12
1204
* and sent the ACK of 10 if running locally. While it would
1205
* recover by sending another ACK of 10, our state machine
1206
* would be out of sync.
1207
*/
1208
1209
/* Ignore ACK for 10 and resend 10-13. */
1210
recv_ack(10);
1211
send_data(10, &contents[9 * 512], 512);
1212
send_data(11, &contents[10 * 512], 512);
1213
send_data(12, &contents[11 * 512], 512);
1214
send_data(13, &contents[12 * 512], 508);
1215
recv_ack(13);
1216
1217
fd = open("rfc7440.txt", O_RDONLY);
1218
ATF_REQUIRE(fd >= 0);
1219
r = read(fd, buffer, sizeof(buffer));
1220
ATF_REQUIRE(r > 0);
1221
close(fd);
1222
require_bufeq(contents, sizeof(contents), buffer, (size_t)r);
1223
}
1224
1225
/*
1226
* Send less than four bytes
1227
*/
1228
TFTPD_TC_DEFINE(short_packet1, /* no head */, exitcode = 1)
1229
{
1230
SEND_STR("\1");
1231
}
1232
TFTPD_TC_DEFINE(short_packet2, /* no head */, exitcode = 1)
1233
{
1234
SEND_STR("\1\2");
1235
}
1236
TFTPD_TC_DEFINE(short_packet3, /* no head */, exitcode = 1)
1237
{
1238
SEND_STR("\1\2\3");
1239
}
1240
1241
1242
/*
1243
* Main
1244
*/
1245
1246
ATF_TP_ADD_TCS(tp)
1247
{
1248
TFTPD_TC_ADD(tp, abspath);
1249
TFTPD_TC_ADD(tp, dotdot);
1250
TFTPD_TC_ADD(tp, s_flag);
1251
TFTPD_TC_ADD(tp, rrq_dropped_ack);
1252
TFTPD_TC_ADD(tp, rrq_dropped_data);
1253
TFTPD_TC_ADD(tp, rrq_duped_ack);
1254
TFTPD_TC_ADD(tp, rrq_eaccess);
1255
TFTPD_TC_ADD(tp, rrq_empty);
1256
TFTPD_TC_ADD(tp, rrq_medium);
1257
TFTPD_TC_ADD(tp, rrq_medium_window);
1258
TFTPD_TC_ADD(tp, rrq_netascii);
1259
TFTPD_TC_ADD(tp, rrq_nonexistent);
1260
TFTPD_TC_ADD(tp, rrq_path_max);
1261
TFTPD_TC_ADD(tp, rrq_small);
1262
TFTPD_TC_ADD(tp, rrq_window_rfc7440);
1263
TFTPD_TC_ADD(tp, unknown_modes);
1264
TFTPD_TC_ADD(tp, unknown_opcode);
1265
TFTPD_TC_ADD(tp, w_flag);
1266
TFTPD_TC_ADD(tp, wrq_dropped_ack);
1267
TFTPD_TC_ADD(tp, wrq_dropped_data);
1268
TFTPD_TC_ADD(tp, wrq_duped_data);
1269
TFTPD_TC_ADD(tp, wrq_eaccess);
1270
TFTPD_TC_ADD(tp, wrq_eaccess_world_readable);
1271
TFTPD_TC_ADD(tp, wrq_medium);
1272
TFTPD_TC_ADD(tp, wrq_medium_window);
1273
TFTPD_TC_ADD(tp, wrq_netascii);
1274
TFTPD_TC_ADD(tp, wrq_nonexistent);
1275
TFTPD_TC_ADD(tp, wrq_small);
1276
TFTPD_TC_ADD(tp, wrq_truncate);
1277
TFTPD_TC_ADD(tp, wrq_window_rfc7440);
1278
TFTPD_TC_ADD(tp, short_packet1);
1279
TFTPD_TC_ADD(tp, short_packet2);
1280
TFTPD_TC_ADD(tp, short_packet3);
1281
1282
return (atf_no_error());
1283
}
1284
1285