1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <tftp/tftp.h> 6 7#include <arpa/inet.h> 8#include <assert.h> 9#include <stdint.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <string.h> 13 14#include <fbl/algorithm.h> 15#include <fbl/unique_ptr.h> 16#include <unittest/unittest.h> 17 18// For inspecting session state 19#include "internal.h" 20 21constexpr char kLocalFilename[] = "local-filename"; 22constexpr char kRemoteFilename[] = "remote-filename"; 23 24struct test_state { 25 // Return value used by ulib/unittest 26 bool reset(size_t ssize, size_t msize, size_t osize) { 27 sess_size = ssize; 28 msg_size = msize; 29 out_size = osize; 30 sess_buf.reset(new uint8_t[sess_size]); 31 msg_data.reset(new uint8_t[msg_size]); 32 out_scratch.reset(new uint8_t[out_size]); 33 auto init_status = tftp_init(&session, sess_buf.get(), sess_size); 34 ASSERT_EQ(TFTP_NO_ERROR, init_status, "could not initialize tftp_session"); 35 data = reinterpret_cast<void*>(msg_data.get()); 36 out = reinterpret_cast<void*>(out_scratch.get()); 37 outlen = out_size; 38 return true; 39 } 40 41 tftp_session* session = nullptr; 42 size_t sess_size = 0; 43 size_t msg_size = 0; 44 size_t out_size = 0; 45 fbl::unique_ptr<uint8_t[]> sess_buf; 46 fbl::unique_ptr<uint8_t[]> msg_data; 47 fbl::unique_ptr<uint8_t[]> out_scratch; 48 void* data = nullptr; 49 void* out = nullptr; 50 size_t outlen = 0; 51 uint32_t timeout = 0; 52}; 53 54static bool test_tftp_init(void) { 55 BEGIN_TEST; 56 57 uint8_t buf[1024]; 58 tftp_session* session; 59 int status = tftp_init(&session, nullptr, 4096); 60 EXPECT_LT(status, 0, "tftp_init should fail for NULL buffer"); 61 status = tftp_init(&session, buf, 4); 62 EXPECT_LT(status, 0, "tftp_init should fail for too small buffer"); 63 status = tftp_init(&session, buf, sizeof(buf)); 64 EXPECT_EQ(status, TFTP_NO_ERROR, "error creating tftp session"); 65 EXPECT_EQ(sizeof(tftp_session), tftp_sizeof_session(), ""); 66 ASSERT_GE(sizeof(buf), tftp_sizeof_session(), 67 "need to update test for larger tftp_session size"); 68 status = tftp_init(&session, buf, tftp_sizeof_session()); 69 EXPECT_EQ(status, TFTP_NO_ERROR, "tftp_init failed on correctly sized buffer"); 70 71 END_TEST; 72} 73 74static bool test_tftp_session_options(void) { 75 BEGIN_TEST; 76 77 test_state ts; 78 ts.reset(1024, 1024, 1500); 79 80 auto open_read_fn = [](const char* filename, void* cookie) -> ssize_t { 81 return 0; 82 }; 83 auto open_write_fn = [](const char* filename, size_t size, void* cookie) -> tftp_status { 84 return 0; 85 }; 86 auto read_fn = [](void* data, size_t* len, off_t offset, void* cookie) -> tftp_status { 87 return 0; 88 }; 89 auto write_fn = [](const void* data, size_t* len, off_t offset, void* cookie) -> tftp_status { 90 return 0; 91 }; 92 auto close_fn = [](void* cookie) { 93 return; 94 }; 95 tftp_file_interface ifc = { open_read_fn, open_write_fn, read_fn, write_fn, close_fn }; 96 auto status = tftp_session_set_file_interface(ts.session, &ifc); 97 EXPECT_EQ(TFTP_NO_ERROR, status, "could not set file callbacks"); 98 EXPECT_EQ((tftp_file_open_read_cb)open_read_fn, ts.session->file_interface.open_read, 99 "bad open (read) function pointer"); 100 EXPECT_EQ((tftp_file_open_write_cb)open_write_fn, ts.session->file_interface.open_write, 101 "bad open (write) function pointer"); 102 EXPECT_EQ((tftp_file_read_cb)read_fn, ts.session->file_interface.read, "bad read function pointer"); 103 EXPECT_EQ((tftp_file_write_cb)write_fn, ts.session->file_interface.write, "bad write function pointer"); 104 EXPECT_EQ((tftp_file_close_cb)close_fn, ts.session->file_interface.close, "bad write function pointer"); 105 106 END_TEST; 107} 108 109bool verify_write_request(const test_state& ts) { 110 auto msg = reinterpret_cast<tftp_msg*>(ts.out); 111 EXPECT_EQ(msg->opcode, htons(OPCODE_WRQ), "opcode should be 2 (WRQ)"); 112 EXPECT_STR_EQ(kRemoteFilename, msg->data, "bad filename"); 113 return true; 114} 115 116bool verify_read_request(const test_state& ts) { 117 auto msg = reinterpret_cast<tftp_msg*>(ts.out); 118 EXPECT_EQ(msg->opcode, htons(OPCODE_RRQ), "opcode should be 1 (RRQ)"); 119 EXPECT_STR_EQ(kRemoteFilename, msg->data, "bad filename"); 120 return true; 121} 122 123// Find a string (which may include NULL characters) inside a memory region. 124static bool find_str_in_mem(const char* str, size_t str_len, const char* mem, size_t mem_len) { 125 while (str_len <= mem_len) { 126 if (!memcmp(str, mem, str_len)) { 127 return true; 128 } 129 mem_len--; 130 mem++; 131 } 132 return false; 133} 134 135static bool test_tftp_generate_request(tftp_file_direction dir, 136 size_t file_size, 137 const uint16_t* block_size, 138 const uint8_t* timeout, 139 const uint16_t* window_size, 140 const uint16_t* block_size_override, 141 const uint8_t* timeout_override, 142 const uint16_t* window_size_override) { 143 BEGIN_TEST; 144 145 tftp_status status; 146 test_state ts; 147 ts.reset(1024, file_size, 1500); 148 149 // Test TFTP state, but not internal session state 150 status = tftp_set_options(ts.session, block_size, timeout, window_size); 151 ASSERT_EQ(TFTP_NO_ERROR, status, "error setting session options"); 152 if (block_size) { 153 EXPECT_EQ(*block_size, ts.session->options.block_size, "bad session options: block size"); 154 } 155 if (timeout) { 156 EXPECT_EQ(*timeout, ts.session->options.timeout, "bad session options: timeout"); 157 } 158 if (window_size) { 159 EXPECT_EQ(*window_size, ts.session->options.window_size, 160 "bad session options: window size"); 161 } 162 163 size_t request_file_size = (dir == SEND_FILE) ? file_size : 0; 164 status = tftp_generate_request(ts.session, dir, kLocalFilename, kRemoteFilename, 165 MODE_OCTET, request_file_size, block_size_override, 166 timeout_override, window_size_override, ts.out, &ts.outlen, 167 &ts.timeout); 168 EXPECT_EQ(TFTP_NO_ERROR, status, "error generating request"); 169 if (dir == SEND_FILE) { 170 EXPECT_TRUE(verify_write_request(ts), "bad write request"); 171 } else { 172 EXPECT_TRUE(verify_read_request(ts), "bad read request"); 173 } 174 175 EXPECT_EQ(REQ_SENT, ts.session->state, "bad session: state"); 176 if (dir == SEND_FILE) { 177 EXPECT_EQ(file_size, ts.session->file_size, "bad session: file size"); 178 } 179 EXPECT_EQ(DEFAULT_MODE, ts.session->mode, "bad session: mode"); 180 EXPECT_EQ(0, ts.session->offset, "bad session: offset"); 181 EXPECT_EQ(0, ts.session->block_number, "bad session: block number"); 182 183 // Verify the options that were set in the request 184 const char* msg = static_cast<const char*>(ts.out); 185 char str[256]; 186 size_t str_sz; 187 188 if (block_size_override) { 189 str_sz = snprintf(str, sizeof(str), "BLKSIZE!%c%d", '\0', *block_size_override) + 1; 190 EXPECT_TRUE(find_str_in_mem(str, str_sz, msg, ts.outlen), "block size not overridden"); 191 } else if (block_size) { 192 str_sz = snprintf(str, sizeof(str), "BLKSIZE%c%d", '\0', *block_size) + 1; 193 EXPECT_TRUE(find_str_in_mem(str, str_sz, msg, ts.outlen), 194 "block size not properly requested"); 195 } else { 196 str_sz = snprintf(str, sizeof(str), "BLKSIZE") + 1; 197 EXPECT_FALSE(find_str_in_mem(str, str_sz, msg, ts.outlen), 198 "block size shouldn't appear in request"); 199 } 200 201 if (timeout_override) { 202 str_sz = snprintf(str, sizeof(str), "TIMEOUT!%c%d", '\0', *timeout_override) + 1; 203 EXPECT_TRUE(find_str_in_mem(str, str_sz, msg, ts.outlen), "timeout not overridden"); 204 } else if (timeout) { 205 str_sz = snprintf(str, sizeof(str), "TIMEOUT%c%d", '\0', *timeout) + 1; 206 EXPECT_TRUE(find_str_in_mem(str, str_sz, msg, ts.outlen), 207 "timeout not properly requested"); 208 } else { 209 str_sz = snprintf(str, sizeof(str), "TIMEOUT") + 1; 210 EXPECT_FALSE(find_str_in_mem(str, str_sz, msg, ts.outlen), 211 "timeout shouldn't appear in request"); 212 } 213 214 if (window_size_override) { 215 str_sz = snprintf(str, sizeof(str), "WINDOWSIZE!%c%d", '\0', *window_size_override) + 1; 216 EXPECT_TRUE(find_str_in_mem(str, str_sz, msg, ts.outlen), "window size not overridden"); 217 } else if (window_size) { 218 str_sz = snprintf(str, sizeof(str), "WINDOWSIZE%c%d", '\0', *window_size) + 1; 219 EXPECT_TRUE(find_str_in_mem(str, str_sz, msg, ts.outlen), 220 "window size not properly requested"); 221 } else { 222 str_sz = snprintf(str, sizeof(str), "WINDOWSIZE") + 1; 223 EXPECT_FALSE(find_str_in_mem(str, str_sz, msg, ts.outlen), 224 "window size shouldn't appear in request"); 225 } 226 227 END_TEST; 228} 229 230static bool test_tftp_generate_wrq_default(void) { 231 return test_tftp_generate_request(SEND_FILE, 1024, nullptr, nullptr, nullptr, 232 nullptr, nullptr, nullptr); 233} 234 235static bool test_tftp_generate_wrq_options(void) { 236 constexpr uint16_t kBlockSize = 555; 237 constexpr uint8_t kTimeout = 3; 238 constexpr uint16_t kWindowSize = 44; 239 return test_tftp_generate_request(SEND_FILE, 1024, &kBlockSize, &kTimeout, &kWindowSize, 240 nullptr, nullptr, nullptr); 241} 242 243static bool test_tftp_generate_wrq_override_blocksize(void) { 244 constexpr uint16_t kBlockSize = 1000; 245 return test_tftp_generate_request(SEND_FILE, 1024, nullptr, nullptr, nullptr, 246 &kBlockSize, nullptr, nullptr); 247} 248 249static bool test_tftp_generate_wrq_override_timeout(void) { 250 uint8_t kTimeout = 60; 251 return test_tftp_generate_request(SEND_FILE, 1024, nullptr, nullptr, nullptr, 252 nullptr, &kTimeout, nullptr); 253} 254 255static bool test_tftp_generate_wrq_override_windowsize(void) { 256 uint16_t kWindowSize = 32; 257 return test_tftp_generate_request(SEND_FILE, 1024, nullptr, nullptr, nullptr, 258 nullptr, nullptr, &kWindowSize); 259} 260 261static bool test_tftp_generate_rrq_default(void) { 262 return test_tftp_generate_request(RECV_FILE, 1024, nullptr, nullptr, nullptr, 263 nullptr, nullptr, nullptr); 264} 265 266static bool test_tftp_generate_rrq_options(void) { 267 constexpr uint16_t kBlockSize = 555; 268 constexpr uint8_t kTimeout = 3; 269 constexpr uint16_t kWindowSize = 44; 270 return test_tftp_generate_request(RECV_FILE, 1024, &kBlockSize, &kTimeout, &kWindowSize, 271 nullptr, nullptr, nullptr); 272} 273 274static bool test_tftp_generate_rrq_override_blocksize(void) { 275 constexpr uint16_t kBlockSize = 1000; 276 return test_tftp_generate_request(RECV_FILE, 1024, nullptr, nullptr, nullptr, 277 &kBlockSize, nullptr, nullptr); 278} 279 280static bool test_tftp_generate_rrq_override_timeout(void) { 281 uint8_t kTimeout = 60; 282 return test_tftp_generate_request(RECV_FILE, 1024, nullptr, nullptr, nullptr, 283 nullptr, &kTimeout, nullptr); 284} 285 286static bool test_tftp_generate_rrq_override_windowsize(void) { 287 uint16_t kWindowSize = 32; 288 return test_tftp_generate_request(RECV_FILE, 1024, nullptr, nullptr, nullptr, 289 nullptr, nullptr, &kWindowSize); 290} 291 292bool verify_response_opcode(const test_state& ts, uint16_t opcode) { 293 ASSERT_GT(ts.outlen, 0, "outlen must not be zero"); 294 auto msg = reinterpret_cast<tftp_msg*>(ts.out); 295 // The upper byte of the opcode is ignored 296 EXPECT_EQ(ntohs(msg->opcode) & 0xff, opcode, "bad opcode"); 297 return true; 298} 299 300static bool test_tftp_receive_request_unexpected(tftp_file_direction dir) { 301 BEGIN_TEST; 302 303 test_state ts; 304 ts.reset(1024, 1024, 1500); 305 306 size_t req_file_size = (dir == SEND_FILE) ? ts.msg_size : 0; 307 auto status = tftp_generate_request(ts.session, dir, kLocalFilename, kRemoteFilename, 308 MODE_OCTET, req_file_size, NULL, NULL, NULL, ts.out, &ts.outlen, &ts.timeout); 309 ASSERT_EQ(TFTP_NO_ERROR, status, "could not generate request"); 310 if (dir == SEND_FILE) { 311 ASSERT_TRUE(verify_write_request(ts), "bad write request"); 312 } else { 313 ASSERT_TRUE(verify_read_request(ts), "bad read request"); 314 } 315 316 ASSERT_LE(ts.outlen, 1500, "outlen too large"); 317 uint8_t buf[1500]; 318 memcpy(buf, ts.out, ts.outlen); 319 320 // We are unprepared to service a request after we have sent one out 321 status = tftp_process_msg(ts.session, buf, sizeof(buf), ts.out, &ts.outlen, &ts.timeout, 322 nullptr); 323 EXPECT_EQ(TFTP_ERR_BAD_STATE, status, "receive should fail"); 324 EXPECT_TRUE(verify_response_opcode(ts, OPCODE_ERROR), "bad error response"); 325 326 END_TEST; 327} 328 329static bool test_tftp_receive_wrq_unexpected(void) { 330 return test_tftp_receive_request_unexpected(SEND_FILE); 331} 332 333static bool test_tftp_receive_rrq_unexpected(void) { 334 return test_tftp_receive_request_unexpected(RECV_FILE); 335} 336 337static bool test_tftp_receive_request_too_large(tftp_file_direction dir) { 338 BEGIN_TEST; 339 340 test_state ts; 341 ts.reset(1024, 1024, 1500); 342 343 uint8_t buf[1024] = { 0 }; 344 buf[1] = (dir == SEND_FILE) ? OPCODE_WRQ : OPCODE_RRQ; 345 346 auto status = tftp_process_msg(ts.session, buf, sizeof(buf), ts.out, &ts.outlen, &ts.timeout, 347 nullptr); 348 EXPECT_LT(status, 0, "receive should fail"); 349 EXPECT_TRUE(verify_response_opcode(ts, OPCODE_ERROR), "bad error response"); 350 351 END_TEST; 352} 353 354static bool test_tftp_receive_wrq_too_large(void) { 355 return test_tftp_receive_request_too_large(SEND_FILE); 356} 357 358static bool test_tftp_receive_rrq_too_large(void) { 359 return test_tftp_receive_request_too_large(RECV_FILE); 360} 361 362static bool test_tftp_receive_request_no_tsize(tftp_file_direction dir) { 363 BEGIN_TEST; 364 365 test_state ts; 366 ts.reset(1024, 1024, 1500); 367 368 char buf[256]; 369 buf[0] = 0x00; 370 buf[1] = (dir == SEND_FILE) ? OPCODE_WRQ : OPCODE_RRQ; 371 size_t buf_sz = 2 + snprintf(&buf[2], sizeof(buf) - 2, 372 "%s%cOCTET", 373 kRemoteFilename, '\0') 374 + 1; 375 376 ASSERT_LT(buf_sz, (int)sizeof(buf), "insufficient space for message"); 377 auto status = tftp_process_msg(ts.session, buf, buf_sz, ts.out, &ts.outlen, &ts.timeout, 378 nullptr); 379 EXPECT_EQ(TFTP_ERR_BAD_STATE, status, "tftp session should fail"); 380 EXPECT_EQ(ERROR, ts.session->state, "tftp session in wrong state"); 381 EXPECT_EQ(0, ts.session->file_size, "tftp session bad file size"); 382 EXPECT_TRUE(verify_response_opcode(ts, OPCODE_ERROR), "bad error response"); 383 384 END_TEST; 385} 386 387static bool test_tftp_receive_wrq_no_tsize(void) { 388 return test_tftp_receive_request_no_tsize(SEND_FILE); 389} 390 391static bool test_tftp_receive_rrq_no_tsize(void) { 392 return test_tftp_receive_request_no_tsize(RECV_FILE); 393} 394 395static bool test_tftp_receive_request_send_oack(tftp_file_direction dir) { 396 BEGIN_TEST; 397 398 test_state ts; 399 ts.reset(1024, 1024, 1500); 400 tftp_file_open_write_cb open_write_cb = 401 [](const char* filename, size_t size, void* cookie) -> tftp_status { 402 EXPECT_STR_EQ(filename, kRemoteFilename, 403 "bad remote filename in open_write callback"); 404 EXPECT_EQ(size, 1024, "bad file size"); 405 return 0; 406 }; 407 tftp_file_open_read_cb open_read_cb = 408 [](const char* filename, void* cookie) -> ssize_t { 409 EXPECT_STR_EQ(filename, kRemoteFilename, 410 "bad remote filename in open_read callback"); 411 return 0; 412 }; 413 tftp_file_interface ifc = {open_read_cb, open_write_cb, NULL, NULL, NULL}; 414 tftp_session_set_file_interface(ts.session, &ifc); 415 uint16_t default_block_size = 13; 416 uint8_t default_timeout = 2; 417 uint16_t default_window_size = 42; 418 tftp_set_options(ts.session, &default_block_size, &default_timeout, &default_window_size); 419 420 size_t req_file_size = (dir == SEND_FILE) ? 1024 : 0; 421 char buf[256]; 422 buf[0] = 0x00; 423 buf[1] = (dir == SEND_FILE) ? OPCODE_WRQ : OPCODE_RRQ; 424 size_t buf_sz = 2 + snprintf(&buf[2], sizeof(buf) - 2, 425 "%s%cOCTET%cTSIZE%c%zu", 426 kRemoteFilename, '\0', '\0', '\0', req_file_size) 427 + 1; 428 429 ASSERT_LT(buf_sz, (int)sizeof(buf), "insufficient space for WRQ message"); 430 auto status = tftp_process_msg(ts.session, buf, buf_sz, ts.out, &ts.outlen, &ts.timeout, 431 nullptr); 432 EXPECT_EQ(TFTP_NO_ERROR, status, "receive request failed"); 433 EXPECT_EQ(REQ_RECEIVED, ts.session->state, "tftp session in wrong state"); 434 EXPECT_EQ(req_file_size, ts.session->file_size, "tftp session bad file size"); 435 EXPECT_EQ(DEFAULT_BLOCKSIZE, ts.session->block_size, "bad session: block size"); 436 EXPECT_EQ(DEFAULT_TIMEOUT, ts.session->timeout, "bad session: timeout"); 437 EXPECT_EQ(DEFAULT_WINDOWSIZE, ts.session->window_size, "bad session: window size"); 438 EXPECT_TRUE(verify_response_opcode(ts, OPCODE_OACK), "bad response"); 439 440 // The request was made from the client's perspective, so our state is the inverse 441 tftp_file_direction our_direction = (dir == SEND_FILE) ? RECV_FILE : SEND_FILE; 442 EXPECT_EQ(our_direction, ts.session->direction, "tftp session has bad direction"); 443 444 const char* msg = static_cast<const char*>(ts.out); 445 const char win_sz_str[] = "WINDOWSIZE"; 446 EXPECT_FALSE(find_str_in_mem(win_sz_str, sizeof(win_sz_str), msg, ts.outlen), 447 "window size in oack, but not in request"); 448 const char timeout_str[] = "TIMEOUT"; 449 EXPECT_FALSE(find_str_in_mem(timeout_str, sizeof(timeout_str), msg, ts.outlen), 450 "timeout in oack, but not in request"); 451 const char block_sz_str[] = "BLKSIZE"; 452 EXPECT_FALSE(find_str_in_mem(block_sz_str, sizeof(block_sz_str), msg, ts.outlen), 453 "block size in oack, but not in request"); 454 455 END_TEST; 456} 457 458static bool test_tftp_receive_wrq_send_oack(void) { 459 return test_tftp_receive_request_send_oack(SEND_FILE); 460} 461 462static bool test_tftp_receive_rrq_send_oack(void) { 463 return test_tftp_receive_request_send_oack(RECV_FILE); 464} 465 466static ssize_t dummy_open_read(const char* filename, void* cookie) { 467 return 1024; 468} 469 470static tftp_status dummy_open_write(const char* filename, size_t size, void* cookie) { 471 return 0; 472} 473 474static bool test_tftp_receive_request_options(tftp_file_direction dir, 475 const uint16_t* server_block_size, 476 const uint8_t* server_timeout, 477 const uint16_t* server_window_size, 478 const uint16_t* client_block_size, 479 const uint8_t* client_timeout, 480 const uint16_t* client_window_size, 481 bool force_block_size, 482 bool force_timeout, 483 bool force_window_size) { 484 BEGIN_TEST; 485 486 test_state ts; 487 ts.reset(1024, 1024, 1500); 488 tftp_file_interface ifc = {dummy_open_read, dummy_open_write, NULL, NULL, NULL}; 489 tftp_session_set_file_interface(ts.session, &ifc); 490 tftp_status status = tftp_set_options(ts.session, server_block_size, server_timeout, 491 server_window_size); 492 ASSERT_EQ(TFTP_NO_ERROR, status, "failed to set server options"); 493 494 size_t req_file_size = (dir == SEND_FILE) ? 1024 : 0; 495 char buf[256]; 496 buf[0] = 0x00; 497 buf[1] = (dir == SEND_FILE) ? OPCODE_WRQ : OPCODE_RRQ; 498 size_t buf_sz = 2; 499 buf_sz += snprintf(&buf[buf_sz], sizeof(buf) - buf_sz, 500 "%s%cOCTET%cTSIZE%c%zu", 501 kRemoteFilename, '\0', '\0', '\0', req_file_size) + 1; 502 if (client_block_size) { 503 buf_sz += snprintf(&buf[buf_sz], sizeof(buf) - buf_sz, 504 "BLKSIZE%s%c%d", 505 force_block_size ? "!" : "", '\0', *client_block_size) + 1; 506 } 507 if (client_timeout) { 508 buf_sz += snprintf(&buf[buf_sz], sizeof(buf) - buf_sz, 509 "TIMEOUT%s%c%d", 510 force_timeout ? "!" : "", '\0', *client_timeout) + 1; 511 } 512 if (client_window_size) { 513 buf_sz += snprintf(&buf[buf_sz], sizeof(buf) - buf_sz, 514 "WINDOWSIZE%s%c%d", 515 force_window_size ? "!" : "", '\0', *client_window_size) + 1; 516 } 517 518 ASSERT_LT(buf_sz, (int)sizeof(buf), "insufficient space for request"); 519 status = tftp_process_msg(ts.session, buf, buf_sz, ts.out, &ts.outlen, &ts.timeout, nullptr); 520 EXPECT_EQ(TFTP_NO_ERROR, status, "receive write request failed"); 521 EXPECT_TRUE(verify_response_opcode(ts, OPCODE_OACK), "bad response"); 522 523 const char* msg = static_cast<const char*>(ts.out); 524 char opt_str[256]; 525 size_t opt_str_sz; 526 527 if (client_block_size) { 528 uint16_t negotiated_block_size = (server_block_size && !force_block_size) 529 ? *server_block_size 530 : *client_block_size; 531 opt_str_sz = snprintf(opt_str, sizeof(opt_str), 532 "BLKSIZE%c%d", 533 '\0', negotiated_block_size) + 1; 534 EXPECT_TRUE(find_str_in_mem(opt_str, opt_str_sz, msg, ts.outlen), 535 "block size not correct in oack"); 536 EXPECT_EQ(negotiated_block_size, ts.session->block_size, "bad session: block size"); 537 } else { 538 opt_str_sz = snprintf(opt_str, sizeof(opt_str), "BLKSIZE") + 1; 539 EXPECT_FALSE(find_str_in_mem(opt_str, opt_str_sz, msg, ts.outlen), 540 "block size appears in oack, but not in request"); 541 EXPECT_EQ(DEFAULT_BLOCKSIZE, ts.session->block_size, "bad session: block size"); 542 } 543 544 if (client_timeout) { 545 uint8_t negotiated_timeout = (server_timeout && !force_timeout) 546 ? *server_timeout 547 : *client_timeout; 548 opt_str_sz = snprintf(opt_str, sizeof(opt_str), 549 "TIMEOUT%c%d", 550 '\0', negotiated_timeout) + 1; 551 EXPECT_TRUE(find_str_in_mem(opt_str, opt_str_sz, msg, ts.outlen), 552 "timeout not correct in oack"); 553 EXPECT_EQ(negotiated_timeout, ts.session->timeout, "bad session: timeout"); 554 } else { 555 opt_str_sz = snprintf(opt_str, sizeof(opt_str), "TIMEOUT") + 1; 556 EXPECT_FALSE(find_str_in_mem(opt_str, opt_str_sz, msg, ts.outlen), 557 "timeout appears in oack, but not in request"); 558 EXPECT_EQ(DEFAULT_TIMEOUT, ts.session->timeout, "bad session: timeout"); 559 } 560 561 if (client_window_size) { 562 uint16_t negotiated_window_size = (server_window_size && !force_window_size) 563 ? *server_window_size 564 : *client_window_size; 565 opt_str_sz = snprintf(opt_str, sizeof(opt_str), 566 "WINDOWSIZE%c%d", 567 '\0', negotiated_window_size) + 1; 568 EXPECT_TRUE(find_str_in_mem(opt_str, opt_str_sz, msg, ts.outlen), 569 "window size not correct in oack"); 570 EXPECT_EQ(negotiated_window_size, ts.session->window_size, "bad session: window size"); 571 } else { 572 opt_str_sz = snprintf(opt_str, sizeof(opt_str), "WINDOWSIZE") + 1; 573 EXPECT_FALSE(find_str_in_mem(opt_str, opt_str_sz, msg, ts.outlen), 574 "window size appears in oack, but not in request"); 575 EXPECT_EQ(DEFAULT_WINDOWSIZE, ts.session->window_size, "bad session: window size"); 576 } 577 578 END_TEST; 579} 580 581static bool test_tftp_receive_wrq_blocksize(void) { 582 constexpr uint8_t kDefaultTimeout = 4; 583 constexpr uint16_t kBlocksize = 1024; 584 return test_tftp_receive_request_options(SEND_FILE, nullptr, &kDefaultTimeout, nullptr, 585 &kBlocksize, nullptr, nullptr, 586 false, false, false); 587} 588 589static bool test_tftp_receive_wrq_timeout(void) { 590 constexpr uint16_t kDefaultBlocksize = 115; 591 constexpr uint8_t kTimeout = 3; 592 return test_tftp_receive_request_options(SEND_FILE, &kDefaultBlocksize, nullptr, nullptr, 593 nullptr, &kTimeout, nullptr, 594 false, false, false); 595} 596 597static bool test_tftp_receive_wrq_windowsize(void) { 598 constexpr uint16_t kWindowsize = 77; 599 return test_tftp_receive_request_options(SEND_FILE, nullptr, nullptr, nullptr, 600 nullptr, nullptr, &kWindowsize, 601 false, false, false); 602} 603 604// Verify that if override values are set, they supercede the values in a normal request 605static bool test_tftp_receive_wrq_have_overrides(void) { 606 constexpr uint16_t kServerBlockSize = 302; 607 constexpr uint8_t kServerTimeout = 7; 608 constexpr uint16_t kServerWindowSize = 16; 609 constexpr uint16_t kClientBlockSize = 32; 610 constexpr uint8_t kClientTimeout = 9; 611 constexpr uint16_t kClientWindowSize = 143; 612 return test_tftp_receive_request_options( 613 SEND_FILE, &kServerBlockSize, &kServerTimeout, &kServerWindowSize, 614 &kClientBlockSize, &kClientTimeout, &kClientWindowSize, 615 false, false, false); 616} 617 618// Verify that if a WRQ has a '!' following an option it is honored, even if overrides are set 619static bool test_tftp_receive_force_wrq_no_overrides(void) { 620 constexpr uint16_t kServerBlockSize = 302; 621 constexpr uint8_t kServerTimeout = 7; 622 constexpr uint16_t kServerWindowSize = 16; 623 constexpr uint16_t kClientBlockSize = 32; 624 constexpr uint8_t kClientTimeout = 9; 625 constexpr uint16_t kClientWindowSize = 143; 626 return test_tftp_receive_request_options( 627 SEND_FILE, &kServerBlockSize, &kServerTimeout, &kServerWindowSize, 628 &kClientBlockSize, &kClientTimeout, &kClientWindowSize, 629 true, true, true); 630} 631 632static bool test_tftp_receive_force_wrq_have_overrides(void) { 633 constexpr uint16_t kClientBlockSize = 32; 634 constexpr uint8_t kClientTimeout = 9; 635 constexpr uint16_t kClientWindowSize = 143; 636 return test_tftp_receive_request_options( 637 SEND_FILE, nullptr, nullptr, nullptr, 638 &kClientBlockSize, &kClientTimeout, &kClientWindowSize, 639 true, true, true); 640} 641 642static bool test_tftp_receive_rrq_blocksize(void) { 643 constexpr uint8_t kDefaultTimeout = 4; 644 constexpr uint16_t kBlocksize = 1024; 645 return test_tftp_receive_request_options(RECV_FILE, nullptr, &kDefaultTimeout, nullptr, 646 &kBlocksize, nullptr, nullptr, 647 false, false, false); 648} 649 650static bool test_tftp_receive_rrq_timeout(void) { 651 constexpr uint16_t kDefaultBlocksize = 115; 652 constexpr uint8_t kTimeout = 3; 653 return test_tftp_receive_request_options(RECV_FILE, &kDefaultBlocksize, nullptr, nullptr, 654 nullptr, &kTimeout, nullptr, 655 false, false, false); 656} 657 658static bool test_tftp_receive_rrq_windowsize(void) { 659 constexpr uint16_t kWindowsize = 77; 660 return test_tftp_receive_request_options(RECV_FILE, nullptr, nullptr, nullptr, 661 nullptr, nullptr, &kWindowsize, 662 false, false, false); 663} 664 665// Verify that if override values are set, they supercede the values in a normal request 666static bool test_tftp_receive_rrq_have_overrides(void) { 667 constexpr uint16_t kServerBlockSize = 302; 668 constexpr uint8_t kServerTimeout = 7; 669 constexpr uint16_t kServerWindowSize = 16; 670 constexpr uint16_t kClientBlockSize = 32; 671 constexpr uint8_t kClientTimeout = 9; 672 constexpr uint16_t kClientWindowSize = 143; 673 return test_tftp_receive_request_options( 674 RECV_FILE, &kServerBlockSize, &kServerTimeout, &kServerWindowSize, 675 &kClientBlockSize, &kClientTimeout, &kClientWindowSize, 676 false, false, false); 677} 678 679// Verify that if a WRQ has a '!' following an option it is honored, even if overrides are set 680static bool test_tftp_receive_force_rrq_no_overrides(void) { 681 constexpr uint16_t kServerBlockSize = 302; 682 constexpr uint8_t kServerTimeout = 7; 683 constexpr uint16_t kServerWindowSize = 16; 684 constexpr uint16_t kClientBlockSize = 32; 685 constexpr uint8_t kClientTimeout = 9; 686 constexpr uint16_t kClientWindowSize = 143; 687 return test_tftp_receive_request_options( 688 RECV_FILE, &kServerBlockSize, &kServerTimeout, &kServerWindowSize, 689 &kClientBlockSize, &kClientTimeout, &kClientWindowSize, 690 true, true, true); 691} 692 693static bool test_tftp_receive_force_rrq_have_overrides(void) { 694 constexpr uint16_t kClientBlockSize = 32; 695 constexpr uint8_t kClientTimeout = 9; 696 constexpr uint16_t kClientWindowSize = 143; 697 return test_tftp_receive_request_options( 698 RECV_FILE, nullptr, nullptr, nullptr, 699 &kClientBlockSize, &kClientTimeout, &kClientWindowSize, 700 true, true, true); 701} 702 703struct tx_test_data { 704 struct { 705 uint16_t block; 706 off_t offset; 707 size_t len; 708 uint8_t data[2048]; // for reads 709 } expected; 710 711 struct { 712 // block is in the outgoing message buffer 713 off_t offset; 714 size_t len; 715 uint8_t data[2048]; // for writes 716 } actual; 717 718 tx_test_data() { 719 expected.block = 1; 720 expected.offset = 0; 721 expected.len = DEFAULT_BLOCKSIZE; 722 expected.data[0] = 'F'; 723 expected.data[DEFAULT_BLOCKSIZE - 1] = 'X'; 724 actual.offset = -1; 725 actual.len = -1; 726 memset(actual.data, 0, sizeof(actual.data)); 727 } 728}; 729 730bool verify_read_data(const test_state& ts, const tx_test_data& td) { 731 BEGIN_HELPER; 732 EXPECT_EQ(td.expected.offset, td.actual.offset, "read offset mismatch"); 733 EXPECT_EQ(td.expected.len, td.actual.len, "read length mismatch"); 734 auto msg = static_cast<tftp_data_msg*>(ts.out); 735 // The upper byte of the opcode is ignored 736 EXPECT_EQ(OPCODE_DATA, ntohs(msg->opcode) & 0xff, "bad opcode"); 737 // Don't continue if we have a bad block number or we risk OOB reads 738 ASSERT_EQ(td.expected.block, ntohs(msg->block), "bad block number"); 739 EXPECT_BYTES_EQ(td.expected.data, msg->data, td.actual.len, "read data mismatch"); 740 END_HELPER; 741} 742 743tftp_status mock_open_write(const char* filename, size_t size, void* file_cookie) { 744 return TFTP_NO_ERROR; 745} 746 747tftp_status mock_read(void* data, size_t* len, off_t offset, void* cookie) { 748 tx_test_data* td = static_cast<tx_test_data*>(cookie); 749 td->actual.len = *len; 750 td->actual.offset = offset; 751 memcpy(data, td->expected.data, *len); 752 return static_cast<tftp_status>(*len); 753} 754 755static bool test_tftp_receive_wrq_oack(const size_t file_size, 756 const uint16_t* block_size_ptr, 757 const uint8_t* timeout_ptr, 758 const uint16_t* window_size_ptr) { 759 BEGIN_TEST; 760 761 uint16_t block_size = block_size_ptr ? *block_size_ptr : DEFAULT_BLOCKSIZE; 762 uint8_t timeout = timeout_ptr ? *timeout_ptr : DEFAULT_TIMEOUT; 763 uint16_t window_size = window_size_ptr ? *window_size_ptr : DEFAULT_WINDOWSIZE; 764 765 test_state ts; 766 ts.reset(1024, file_size, 1500); 767 768 auto status = tftp_generate_request(ts.session, SEND_FILE, kLocalFilename, kRemoteFilename, 769 MODE_OCTET, ts.msg_size, block_size_ptr, timeout_ptr, 770 window_size_ptr, ts.out, &ts.outlen, &ts.timeout); 771 ASSERT_EQ(TFTP_NO_ERROR, status, "error generating write request"); 772 ASSERT_TRUE(verify_write_request(ts), "bad write request"); 773 774 char buf[256]; 775 buf[0] = 0x00; 776 buf[1] = 0x06; // Opcode (OACK) 777 size_t buf_sz = 2; 778 buf_sz += snprintf(&buf[buf_sz], sizeof(buf) - buf_sz, 779 "%s%cOCTET%cTSIZE%c%zu", 780 kRemoteFilename, '\0', '\0', '\0', file_size) + 1; 781 if (block_size_ptr) { 782 buf_sz += snprintf(&buf[buf_sz], sizeof(buf) - buf_sz, 783 "BLKSIZE%c%d", 784 '\0', block_size) + 1; 785 } 786 if (timeout_ptr) { 787 buf_sz += snprintf(&buf[buf_sz], sizeof(buf) - buf_sz, 788 "TIMEOUT%c%d", 789 '\0', timeout) + 1; 790 } 791 if (window_size_ptr) { 792 buf_sz += snprintf(&buf[buf_sz], sizeof(buf) - buf_sz, 793 "WINDOWSIZE%c%d", 794 '\0', window_size) + 1; 795 } 796 797 tftp_file_interface ifc = {NULL, NULL, mock_read, NULL, NULL}; 798 tftp_session_set_file_interface(ts.session, &ifc); 799 800 tx_test_data td; 801 td.expected.len = block_size; 802 td.expected.data[block_size - 1] = 'X'; 803 status = tftp_process_msg(ts.session, buf, buf_sz, ts.out, &ts.outlen, &ts.timeout, &td); 804 EXPECT_EQ(TFTP_NO_ERROR, status, "tftp_process_msg failed"); 805 bool has_pending = (block_size < file_size) && (window_size > 1); 806 EXPECT_EQ(has_pending, tftp_session_has_pending(ts.session), 807 "Unexpected tftp_session_has_pending()"); 808 EXPECT_EQ(FIRST_DATA, ts.session->state, "session should be in state FIRST_DATA"); 809 EXPECT_EQ(file_size, ts.session->file_size, "tftp session bad file size"); 810 EXPECT_EQ(block_size, ts.session->block_size, "bad session: block size"); 811 EXPECT_EQ(timeout, ts.session->timeout, "bad session: timeout"); 812 EXPECT_EQ(window_size, ts.session->window_size, "bad session: window size"); 813 814 EXPECT_EQ(ts.outlen, sizeof(tftp_data_msg) + block_size, "bad outlen"); 815 EXPECT_EQ(ts.timeout, timeout * 1000U, "timeout should be set"); 816 EXPECT_TRUE(verify_read_data(ts, td), "bad test data"); 817 818 if (has_pending) { 819 // Since pending is true, call for a second data packet to transmit 820 // Updated the read offset and change a few bytes 821 td.expected.block = 2; 822 size_t second_block_size = fbl::min<size_t>(file_size - block_size, block_size); 823 td.expected.offset = block_size; 824 td.expected.data[0] = 'X'; 825 if (second_block_size > 0) { 826 td.expected.data[second_block_size - 1] = 'F'; 827 } 828 829 status = tftp_prepare_data(ts.session, ts.out, &ts.outlen, &ts.timeout, &td); 830 EXPECT_EQ(ts.outlen, sizeof(tftp_data_msg) + block_size, "bad outlen"); 831 EXPECT_TRUE(verify_read_data(ts, td), "bad test data"); 832 has_pending = (block_size + second_block_size < file_size) && (window_size > 2); 833 EXPECT_EQ(has_pending, tftp_session_has_pending(ts.session), 834 "Unexpected tftp_session_has_pending()"); 835 } 836 END_TEST; 837} 838 839static bool test_tftp_receive_wrq_oack(void) { 840 return test_tftp_receive_wrq_oack(1024, NULL, NULL, NULL); 841} 842 843static bool test_tftp_receive_wrq_oack_blocksize(void) { 844 constexpr uint16_t kBlockSize = 1024; 845 return test_tftp_receive_wrq_oack(2048, &kBlockSize, nullptr, nullptr); 846} 847 848static bool test_tftp_receive_wrq_oack_timeout(void) { 849 constexpr uint8_t kTimeout = 5; 850 return test_tftp_receive_wrq_oack(1024, nullptr, &kTimeout, nullptr); 851} 852 853static bool test_tftp_receive_wrq_oack_windowsize(void) { 854 constexpr uint16_t kWindowSize = 2; 855 return test_tftp_receive_wrq_oack(4096, nullptr, nullptr, &kWindowSize); 856} 857 858static bool test_tftp_receive_rrq_oack(size_t file_size, 859 const uint16_t* block_size_ptr, 860 const uint8_t* timeout_ptr, 861 const uint16_t* window_size_ptr) { 862 BEGIN_TEST; 863 864 uint16_t block_size = block_size_ptr ? *block_size_ptr : DEFAULT_BLOCKSIZE; 865 uint8_t timeout = timeout_ptr ? *timeout_ptr : DEFAULT_TIMEOUT; 866 uint16_t window_size = window_size_ptr ? *window_size_ptr : DEFAULT_WINDOWSIZE; 867 868 test_state ts; 869 ts.reset(1024, file_size, 1500); 870 871 auto status = tftp_generate_request(ts.session, RECV_FILE, kLocalFilename, kRemoteFilename, 872 MODE_OCTET, 0, block_size_ptr, timeout_ptr, 873 window_size_ptr, ts.out, &ts.outlen, &ts.timeout); 874 ASSERT_EQ(TFTP_NO_ERROR, status, "error generating read request"); 875 ASSERT_TRUE(verify_read_request(ts), "bad read request"); 876 877 char buf[256]; 878 buf[0] = 0x00; 879 buf[1] = 0x06; // Opcode (OACK) 880 size_t buf_sz = 2; 881 buf_sz += snprintf(&buf[buf_sz], sizeof(buf) - buf_sz, 882 "TSIZE%c%zu", 883 '\0', file_size) + 1; 884 if (block_size_ptr) { 885 buf_sz += snprintf(&buf[buf_sz], sizeof(buf) - buf_sz, 886 "BLKSIZE%c%d", 887 '\0', block_size) + 1; 888 } 889 if (timeout_ptr) { 890 buf_sz += snprintf(&buf[buf_sz], sizeof(buf) - buf_sz, 891 "TIMEOUT%c%d", 892 '\0', timeout) + 1; 893 } 894 if (window_size_ptr) { 895 buf_sz += snprintf(&buf[buf_sz], sizeof(buf) - buf_sz, 896 "WINDOWSIZE%c%d", 897 '\0', window_size) + 1; 898 } 899 tftp_file_interface ifc = {NULL, mock_open_write, NULL, NULL, NULL}; 900 tftp_session_set_file_interface(ts.session, &ifc); 901 902 status = tftp_process_msg(ts.session, buf, buf_sz, ts.out, &ts.outlen, &ts.timeout, NULL); 903 EXPECT_FALSE(tftp_session_has_pending(ts.session), "session should not have pending data"); 904 EXPECT_EQ(FIRST_DATA, ts.session->state, "session should be in state FIRST_DATA"); 905 EXPECT_EQ(file_size, ts.session->file_size, "tftp session bad file size"); 906 EXPECT_EQ(block_size, ts.session->block_size, "bad session: block size"); 907 EXPECT_EQ(timeout, ts.session->timeout, "bad session: timeout"); 908 EXPECT_EQ(window_size, ts.session->window_size, "bad session: window size"); 909 910 uint8_t expected_ack[] = { 911 0x00, 0x04, // Opcode (ACK) 912 0x00, 0x00 // Block 913 }; 914 EXPECT_EQ(sizeof(expected_ack), ts.outlen, "response size mismatch"); 915 EXPECT_EQ(0, memcmp(expected_ack, ts.out, sizeof(expected_ack)), "bad response"); 916 END_TEST; 917} 918 919static bool test_tftp_receive_rrq_oack(void) { 920 return test_tftp_receive_rrq_oack(1024, nullptr, nullptr, nullptr); 921} 922 923static bool test_tftp_receive_rrq_oack_blocksize(void) { 924 constexpr uint16_t kBlockSize = 1024; 925 return test_tftp_receive_rrq_oack(2048, &kBlockSize, nullptr, nullptr); 926} 927 928static bool test_tftp_receive_rrq_oack_timeout(void) { 929 constexpr uint8_t kTimeout = 5; 930 return test_tftp_receive_rrq_oack(1024, nullptr, &kTimeout, nullptr); 931} 932 933static bool test_tftp_receive_rrq_oack_windowsize(void) { 934 constexpr uint16_t kWindowSize = 412; 935 return test_tftp_receive_rrq_oack(1024, nullptr, nullptr, &kWindowSize); 936} 937 938// Verify that if the server overrides our settings we use the oack'd settings it provides 939static bool test_tftp_receive_oack_overrides(void) { 940 BEGIN_TEST; 941 942 test_state ts; 943 ts.reset(1024, 4096, 1500); 944 945 uint16_t kBlockSize = 14; 946 uint8_t kTimeout = 12; 947 uint16_t kWindowSize = 6; 948 949 auto status = tftp_generate_request(ts.session, SEND_FILE, kLocalFilename, kRemoteFilename, 950 MODE_OCTET, ts.msg_size, &kBlockSize, &kTimeout, &kWindowSize, ts.out, &ts.outlen, 951 &ts.timeout); 952 ASSERT_EQ(TFTP_NO_ERROR, status, "error generating write request"); 953 954 uint8_t buf[] = { 955 0x00, 0x06, // Opcode (OACK) 956 'T', 'S', 'I', 'Z', 'E', 0x00, // Option 957 '4', '0', '9', '6', 0x00, // TSIZE value 958 'B', 'L', 'K', 'S', 'I', 'Z', 'E', 0x00, // Option 959 '5', '5', 0x00, // BLKSIZE value 960 'T', 'I', 'M', 'E', 'O', 'U', 'T', 0x00, // Option 961 '3', 0x00, // TIMEOUT value 962 'W', 'I', 'N', 'D', 'O', 'W', 'S', 'I', 'Z', 'E', 0x00, // Option 963 '2', '1', '4', 0x00, // WINDOWSIZE value 964 }; 965 966 tftp_file_interface ifc = {NULL, NULL, mock_read, NULL, NULL}; 967 tftp_session_set_file_interface(ts.session, &ifc); 968 969 tx_test_data td; 970 status = tftp_process_msg(ts.session, buf, sizeof(buf), ts.out, &ts.outlen, &ts.timeout, &td); 971 EXPECT_EQ(FIRST_DATA, ts.session->state, "session should be in state FIRST_DATA"); 972 EXPECT_EQ(4096, ts.session->file_size, "tftp session bad file size"); 973 EXPECT_EQ(55, ts.session->block_size, "bad session: block size"); 974 EXPECT_EQ(3, ts.session->timeout, "bad session: timeout"); 975 EXPECT_EQ(214, ts.session->window_size, "bad session: window size"); 976 EXPECT_EQ(ts.outlen, sizeof(tftp_data_msg) + ts.session->block_size, "bad outlen"); 977 978 END_TEST; 979} 980 981tftp_status mock_write(const void* data, size_t* len, off_t offset, void* cookie) { 982 tx_test_data* td = static_cast<tx_test_data*>(cookie); 983 td->actual.len = *len; 984 td->actual.offset = offset; 985 memcpy(td->actual.data + offset, data, *len); 986 return static_cast<tftp_status>(*len); 987} 988 989bool verify_write_data(const uint8_t* expected, const tx_test_data& td) { 990 BEGIN_HELPER; 991 ASSERT_EQ(td.expected.offset, td.actual.offset, "write offset mismatch"); 992 ASSERT_EQ(td.expected.len, td.actual.len, "write length mismatch"); 993 EXPECT_BYTES_EQ(expected, td.actual.data + td.actual.offset, td.actual.len, "write data mismatch"); 994 END_HELPER; 995} 996 997static bool test_tftp_receive_data(void) { 998 BEGIN_TEST; 999 1000 test_state ts; 1001 ts.reset(1024, 1024, 1500); 1002 tftp_file_interface ifc = {NULL, 1003 [](const char* filename, size_t size, void* cookie) -> tftp_status { 1004 EXPECT_STR_EQ(filename, kRemoteFilename, "bad filename"); 1005 EXPECT_EQ(size, 1024, "bad file size"); 1006 return 0; 1007 }, NULL, NULL, NULL}; 1008 tftp_session_set_file_interface(ts.session, &ifc); 1009 1010 char req_buf[256]; 1011 req_buf[0] = 0x00; 1012 req_buf[1] = OPCODE_WRQ; 1013 size_t req_buf_sz = 2 + snprintf(&req_buf[2], sizeof(req_buf) - 2, 1014 "%s%cOCTET%cTSIZE%c%d", 1015 kRemoteFilename, '\0', '\0', '\0', 1024) 1016 + 1; 1017 1018 ASSERT_LT(req_buf_sz, (int)sizeof(req_buf), "insufficient space for WRQ message"); 1019 auto status = tftp_process_msg(ts.session, req_buf, req_buf_sz, ts.out, &ts.outlen, 1020 &ts.timeout, nullptr); 1021 ASSERT_EQ(TFTP_NO_ERROR, status, "receive write request failed"); 1022 ASSERT_EQ(REQ_RECEIVED, ts.session->state, "tftp session in wrong state"); 1023 ASSERT_TRUE(verify_response_opcode(ts, OPCODE_OACK), "bad response"); 1024 1025 uint8_t data_buf[516] = { 1026 0x00, 0x03, // Opcode (DATA) 1027 0x00, 0x01, // Block 1028 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // Data; compiler will fill out the rest with zeros 1029 }; 1030 data_buf[515] = 0x79; // set the last byte to make sure it all gets copied 1031 1032 ifc.write = mock_write; 1033 tftp_session_set_file_interface(ts.session, &ifc); 1034 1035 tx_test_data td; 1036 status = tftp_process_msg(ts.session, data_buf, sizeof(data_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1037 EXPECT_EQ(TFTP_NO_ERROR, status, "receive data failed"); 1038 EXPECT_TRUE(verify_response_opcode(ts, OPCODE_ACK), "bad response"); 1039 EXPECT_TRUE(verify_write_data(data_buf + 4, td), "bad write data"); 1040 EXPECT_EQ(1, ts.session->block_number, "tftp session block number mismatch"); 1041 EXPECT_EQ(0, ts.session->window_index, "tftp session window index mismatch"); 1042 1043 END_TEST; 1044} 1045 1046static bool test_tftp_receive_data_final_block(void) { 1047 BEGIN_TEST; 1048 1049 test_state ts; 1050 ts.reset(1024, 1024, 1500); 1051 tftp_file_interface ifc = {NULL, 1052 [](const char* filename, size_t size, void* cookie) -> tftp_status { 1053 EXPECT_STR_EQ(filename, kRemoteFilename, "bad filename"); 1054 EXPECT_EQ(size, 1024, "bad file size"); 1055 return 0; 1056 }, NULL, NULL, NULL}; 1057 tftp_session_set_file_interface(ts.session, &ifc); 1058 1059 char req_buf[256]; 1060 req_buf[0] = 0x00; 1061 req_buf[1] = OPCODE_WRQ; 1062 size_t req_buf_sz = 2 + snprintf(&req_buf[2], sizeof(req_buf) - 2, 1063 "%s%cOCTET%cTSIZE%c%d", 1064 kRemoteFilename, '\0', '\0', '\0', 1024) 1065 + 1; 1066 1067 ASSERT_LT(req_buf_sz, (int)sizeof(req_buf), "insufficient space for WRQ message"); 1068 auto status = tftp_process_msg(ts.session, req_buf, req_buf_sz, ts.out, &ts.outlen, &ts.timeout, nullptr); 1069 ASSERT_EQ(TFTP_NO_ERROR, status, "receive write request failed"); 1070 ASSERT_EQ(REQ_RECEIVED, ts.session->state, "tftp session in wrong state"); 1071 ASSERT_TRUE(verify_response_opcode(ts, OPCODE_OACK), "bad response"); 1072 1073 uint8_t data_buf[516] = { 1074 0x00, 0x03, // Opcode (DATA) 1075 0x00, 0x01, // Block 1076 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // Data; compiler will fill out the rest with zeros 1077 }; 1078 data_buf[515] = 0x79; // set the last byte to make sure it all gets copied 1079 1080 ifc.write = mock_write; 1081 tftp_session_set_file_interface(ts.session, &ifc); 1082 1083 tx_test_data td; 1084 status = tftp_process_msg(ts.session, data_buf, sizeof(data_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1085 ASSERT_EQ(TFTP_NO_ERROR, status, "receive data failed"); 1086 ASSERT_TRUE(verify_response_opcode(ts, OPCODE_ACK), "bad response"); 1087 ASSERT_TRUE(verify_write_data(data_buf + 4, td), "bad write data"); 1088 1089 // Update block number and first/last bytes of the data packet 1090 data_buf[3]++; 1091 data_buf[4]++; 1092 data_buf[515]++; 1093 td.expected.block++; 1094 td.expected.offset = DEFAULT_BLOCKSIZE; 1095 1096 status = tftp_process_msg(ts.session, data_buf, sizeof(data_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1097 EXPECT_EQ(TFTP_NO_ERROR, status, "receive data failed"); 1098 ASSERT_TRUE(verify_response_opcode(ts, OPCODE_ACK), "bad response"); 1099 EXPECT_TRUE(verify_write_data(data_buf + 4, td), "bad write data"); 1100 1101 // Last data packet. Empty, indicating end of data. 1102 data_buf[3]++; 1103 status = tftp_process_msg(ts.session, data_buf, 4, ts.out, &ts.outlen, &ts.timeout, nullptr); 1104 EXPECT_EQ(TFTP_TRANSFER_COMPLETED, status, "receive data failed"); 1105 ASSERT_TRUE(verify_response_opcode(ts, OPCODE_ACK), "bad response"); 1106 END_TEST; 1107} 1108 1109static bool test_tftp_receive_data_blocksize(void) { 1110 BEGIN_TEST; 1111 1112 test_state ts; 1113 ts.reset(1024, 2048, 1500); 1114 tftp_file_interface ifc = {NULL, 1115 [](const char* filename, size_t size, void* cookie) -> tftp_status { 1116 EXPECT_STR_EQ(filename, kRemoteFilename, "bad filename"); 1117 EXPECT_EQ(size, 2048, "bad file size"); 1118 return 0; 1119 }, NULL, NULL, NULL}; 1120 tftp_session_set_file_interface(ts.session, &ifc); 1121 1122 char req_buf[256]; 1123 req_buf[0] = 0x00; 1124 req_buf[1] = OPCODE_WRQ; 1125 size_t req_buf_sz = 2 + snprintf(&req_buf[2], sizeof(req_buf) - 2, 1126 "%s%cOCTET%cTSIZE%c%d%cBLKSIZE%c%d", 1127 kRemoteFilename, '\0', '\0', '\0', 2048, '\0', '\0', 1024) 1128 + 1; 1129 1130 ASSERT_LT(req_buf_sz, (int)sizeof(req_buf), "insufficient space for WRQ message"); 1131 auto status = tftp_process_msg(ts.session, req_buf, req_buf_sz, ts.out, &ts.outlen, 1132 &ts.timeout, nullptr); 1133 ASSERT_EQ(TFTP_NO_ERROR, status, "receive write request failed"); 1134 ASSERT_EQ(REQ_RECEIVED, ts.session->state, "tftp session in wrong state"); 1135 ASSERT_TRUE(verify_response_opcode(ts, OPCODE_OACK), "bad response"); 1136 1137 uint8_t data_buf[1028] = { 1138 0x00, 0x03, // Opcode (DATA) 1139 0x00, 0x01, // Block 1140 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // Data; compiler will fill out the rest with zeros 1141 }; 1142 data_buf[1027] = 0x79; // set the last byte to make sure it all gets copied 1143 1144 ifc.write = mock_write; 1145 tftp_session_set_file_interface(ts.session, &ifc); 1146 1147 tx_test_data td; 1148 td.expected.len = 1024; 1149 status = tftp_process_msg(ts.session, data_buf, sizeof(data_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1150 EXPECT_EQ(TFTP_NO_ERROR, status, "receive data failed"); 1151 EXPECT_TRUE(verify_response_opcode(ts, OPCODE_ACK), "bad response"); 1152 EXPECT_TRUE(verify_write_data(data_buf + 4, td), "bad write data"); 1153 EXPECT_EQ(1, ts.session->block_number, "tftp session block number mismatch"); 1154 EXPECT_EQ(0, ts.session->window_index, "tftp session window index mismatch"); 1155 1156 END_TEST; 1157} 1158 1159static bool test_tftp_receive_data_windowsize(void) { 1160 BEGIN_TEST; 1161 1162 test_state ts; 1163 ts.reset(1024, 1025, 1500); 1164 tftp_file_interface ifc = {NULL, 1165 [](const char* filename, size_t size, void* cookie) -> tftp_status { 1166 EXPECT_STR_EQ(filename, kRemoteFilename, "bad filename"); 1167 EXPECT_EQ(size, 1025, "bad file size"); 1168 return 0; 1169 }, NULL, NULL, NULL}; 1170 tftp_session_set_file_interface(ts.session, &ifc); 1171 1172 char req_buf[256]; 1173 req_buf[0] = 0x00; 1174 req_buf[1] = OPCODE_WRQ; 1175 size_t req_buf_sz = 2 + snprintf(&req_buf[2], sizeof(req_buf) - 2, 1176 "%s%cOCTET%cTSIZE%c%d%cWINDOWSIZE%c%d", 1177 kRemoteFilename, '\0', '\0', '\0', 1025, '\0', '\0', 2) 1178 + 1; 1179 1180 ASSERT_LT(req_buf_sz, (int)sizeof(req_buf), "insufficient space for WRQ message"); 1181 auto status = tftp_process_msg(ts.session, req_buf, req_buf_sz, ts.out, &ts.outlen, 1182 &ts.timeout, nullptr); 1183 ASSERT_EQ(TFTP_NO_ERROR, status, "receive write request failed"); 1184 ASSERT_EQ(REQ_RECEIVED, ts.session->state, "tftp session in wrong state"); 1185 ASSERT_TRUE(verify_response_opcode(ts, OPCODE_OACK), "bad response"); 1186 1187 uint8_t data_buf[516] = { 1188 0x00, 0x03, // Opcode (DATA) 1189 0x00, 0x01, // Block 1190 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // Data; compiler will fill out the rest with zeros 1191 }; 1192 data_buf[515] = 0x79; // set the last byte to make sure it all gets copied 1193 1194 ifc.write = mock_write; 1195 tftp_session_set_file_interface(ts.session, &ifc); 1196 1197 tx_test_data td; 1198 status = tftp_process_msg(ts.session, data_buf, sizeof(data_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1199 EXPECT_EQ(TFTP_NO_ERROR, status, "receive data failed"); 1200 EXPECT_EQ(0, ts.outlen, "no response expected"); 1201 EXPECT_TRUE(verify_write_data(data_buf + 4, td), "bad write data"); 1202 EXPECT_EQ(1, ts.session->block_number, "tftp session block number mismatch"); 1203 EXPECT_EQ(1, ts.session->window_index, "tftp session window index mismatch"); 1204 1205 // Update block number and first/last bytes of the data packet 1206 data_buf[3]++; 1207 data_buf[4]++; 1208 data_buf[515]++; 1209 td.expected.block++; 1210 td.expected.offset += DEFAULT_BLOCKSIZE; 1211 1212 status = tftp_process_msg(ts.session, data_buf, sizeof(data_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1213 EXPECT_EQ(TFTP_NO_ERROR, status, "receive data failed"); 1214 EXPECT_TRUE(verify_response_opcode(ts, OPCODE_ACK), "bad response"); 1215 EXPECT_TRUE(verify_write_data(data_buf + 4, td), "bad write data"); 1216 EXPECT_EQ(2, ts.session->block_number, "tftp session block number mismatch"); 1217 EXPECT_EQ(0, ts.session->window_index, "tftp session window index mismatch"); 1218 1219 END_TEST; 1220} 1221 1222static bool test_tftp_receive_data_skipped_block(void) { 1223 BEGIN_TEST; 1224 1225 test_state ts; 1226 ts.reset(1024, 1024, 1500); 1227 tftp_file_interface ifc = {NULL, 1228 [](const char* filename, size_t size, void* cookie) -> tftp_status { 1229 EXPECT_STR_EQ(filename, kRemoteFilename, "bad filename"); 1230 EXPECT_EQ(size, 1024, "bad file size"); 1231 return 0; 1232 }, NULL, NULL, NULL}; 1233 tftp_session_set_file_interface(ts.session, &ifc); 1234 1235 char req_buf[256]; 1236 req_buf[0] = 0x00; 1237 req_buf[1] = OPCODE_WRQ; 1238 size_t req_buf_sz = 2 + snprintf(&req_buf[2], sizeof(req_buf) - 2, 1239 "%s%cOCTET%cTSIZE%c%d", 1240 kRemoteFilename, '\0', '\0', '\0', 1024) 1241 + 1; 1242 1243 ASSERT_LT(req_buf_sz, (int)sizeof(req_buf), "insufficient space for WRQ message"); 1244 auto status = tftp_process_msg(ts.session, req_buf, req_buf_sz, ts.out, &ts.outlen, 1245 &ts.timeout, nullptr); 1246 ASSERT_EQ(TFTP_NO_ERROR, status, "receive write request failed"); 1247 ASSERT_EQ(REQ_RECEIVED, ts.session->state, "tftp session in wrong state"); 1248 ASSERT_TRUE(verify_response_opcode(ts, OPCODE_OACK), "bad response"); 1249 1250 // This is block 2, meaning we missed block 1 somehow. 1251 uint8_t data_buf[516] = { 1252 0x00, 0x03, // Opcode (DATA) 1253 0x00, 0x02, // Block 1254 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // Data; compiler will fill out the rest with zeros 1255 }; 1256 data_buf[515] = 0x79; // set the last byte to make sure it all gets copied 1257 1258 ifc.write = mock_write; 1259 tftp_session_set_file_interface(ts.session, &ifc); 1260 1261 status = tftp_process_msg(ts.session, data_buf, sizeof(data_buf), ts.out, &ts.outlen, &ts.timeout, nullptr); 1262 EXPECT_EQ(TFTP_NO_ERROR, status, "receive data failed"); 1263 ASSERT_GT(ts.outlen, 0, "outlen must not be zero"); 1264 auto msg = reinterpret_cast<tftp_data_msg*>(ts.out); 1265 EXPECT_EQ(ntohs(msg->opcode) & 0xff, OPCODE_ACK, "bad opcode"); 1266 // The opcode prefix should have been advanced when we saw a dropped block 1267 EXPECT_EQ((ntohs(msg->opcode) & 0xff00) >> 8, 1, "bad opcode prefix"); 1268 EXPECT_EQ(ntohs(msg->block), 0, "bad block number"); 1269 EXPECT_EQ(0, ts.session->block_number, "tftp session block number mismatch"); 1270 EXPECT_EQ(0, ts.session->window_index, "tftp session window index mismatch"); 1271 1272 // Verify with the opcode prefix disabled 1273 tftp_session_set_opcode_prefix_use(ts.session, false); 1274 status = tftp_process_msg(ts.session, data_buf, sizeof(data_buf), ts.out, &ts.outlen, &ts.timeout, nullptr); 1275 EXPECT_EQ(TFTP_NO_ERROR, status, "receive data failed"); 1276 ASSERT_GT(ts.outlen, 0, "outlen must not be zero"); 1277 msg = reinterpret_cast<tftp_data_msg*>(ts.out); 1278 EXPECT_EQ(ntohs(msg->opcode) & 0xff, OPCODE_ACK, "bad opcode"); 1279 EXPECT_EQ((ntohs(msg->opcode) & 0xff00) >> 8, 0, "bad opcode prefix"); 1280 EXPECT_EQ(ntohs(msg->block), 0, "bad block number"); 1281 1282 END_TEST; 1283} 1284 1285static bool test_tftp_receive_data_windowsize_skipped_block(void) { 1286 BEGIN_TEST; 1287 1288 test_state ts; 1289 ts.reset(1024, 2048, 1500); 1290 tftp_file_interface ifc = {NULL, 1291 [](const char* filename, size_t size, void* cookie) -> tftp_status { 1292 EXPECT_STR_EQ(filename, kRemoteFilename, "bad filename"); 1293 EXPECT_EQ(size, 2048, "bad file size"); 1294 return 0; 1295 }, NULL, NULL, NULL}; 1296 tftp_session_set_file_interface(ts.session, &ifc); 1297 1298 char req_buf[256]; 1299 req_buf[0] = 0x00; 1300 req_buf[1] = OPCODE_WRQ; 1301 size_t req_buf_sz = 2 + snprintf(&req_buf[2], sizeof(req_buf) - 2, 1302 "%s%cOCTET%cTSIZE%c%d%cWINDOWSIZE%c%d", 1303 kRemoteFilename, '\0', '\0', '\0', 2048, '\0', '\0', 3) 1304 + 1; 1305 1306 ASSERT_LT(req_buf_sz, (int)sizeof(req_buf), "insufficient space for WRQ message"); 1307 auto status = tftp_process_msg(ts.session, req_buf, req_buf_sz, ts.out, &ts.outlen, 1308 &ts.timeout, nullptr); 1309 ASSERT_EQ(TFTP_NO_ERROR, status, "receive write request failed"); 1310 ASSERT_EQ(REQ_RECEIVED, ts.session->state, "tftp session in wrong state"); 1311 ASSERT_TRUE(verify_response_opcode(ts, OPCODE_OACK), "bad response"); 1312 1313 uint8_t data_buf[516] = { 1314 0x00, 0x03, // Opcode (DATA) 1315 0x00, 0x01, // Block 1316 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // Data; compiler will fill out the rest with zeros 1317 }; 1318 data_buf[515] = 0x79; // set the last byte to make sure it all gets copied 1319 1320 ifc.write = mock_write; 1321 tftp_session_set_file_interface(ts.session, &ifc); 1322 1323 tx_test_data td; 1324 status = tftp_process_msg(ts.session, data_buf, sizeof(data_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1325 EXPECT_EQ(TFTP_NO_ERROR, status, "receive data failed"); 1326 EXPECT_TRUE(verify_write_data(data_buf + 4, td), "bad write data"); 1327 EXPECT_EQ(1, ts.session->block_number, "tftp session block number mismatch"); 1328 EXPECT_EQ(1, ts.session->window_index, "tftp session window index mismatch"); 1329 1330 // Update block number and first/last bytes of the data packet 1331 data_buf[3]++; 1332 data_buf[4]++; 1333 data_buf[515]++; 1334 td.expected.block++; 1335 td.expected.offset += DEFAULT_BLOCKSIZE; 1336 1337 status = tftp_process_msg(ts.session, data_buf, sizeof(data_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1338 EXPECT_EQ(TFTP_NO_ERROR, status, "receive data failed"); 1339 EXPECT_TRUE(verify_write_data(data_buf + 4, td), "bad write data"); 1340 EXPECT_EQ(2, ts.session->block_number, "tftp session block number mismatch"); 1341 EXPECT_EQ(2, ts.session->window_index, "tftp session window index mismatch"); 1342 1343 1344 // Update block number and first/last bytes of the data packet. Block number 1345 // goes up by 2 to indicate a skipped block. 1346 data_buf[3] = 4u; 1347 data_buf[4]++; 1348 data_buf[515]++; 1349 status = tftp_process_msg(ts.session, data_buf, sizeof(data_buf), ts.out, &ts.outlen, &ts.timeout, nullptr); 1350 EXPECT_EQ(TFTP_NO_ERROR, status, "receive data failed"); 1351 ASSERT_GT(ts.outlen, 0, "outlen must not be zero"); 1352 auto msg = reinterpret_cast<tftp_data_msg*>(ts.out); 1353 EXPECT_EQ(ntohs(msg->opcode) & 0xff, OPCODE_ACK, "bad opcode"); 1354 // Opcode prefix should have been incremented when a packet was not received 1355 EXPECT_EQ((ntohs(msg->opcode) & 0xff00) >> 8, 1, "bad opcode prefix"); 1356 EXPECT_EQ(ntohs(msg->block), 2, "bad block number"); 1357 EXPECT_EQ(0, td.actual.data[1024], "block 3 should be empty"); 1358 EXPECT_EQ(2, ts.session->block_number, "tftp session block number mismatch"); 1359 // Reset the window index after sending the ack with the last known block 1360 EXPECT_EQ(0, ts.session->window_index, "tftp session window index mismatch"); 1361 1362 END_TEST; 1363} 1364 1365namespace { 1366 1367constexpr const unsigned long kWrapAt = 0x3ffff; 1368constexpr const int kBlockSize = 8; 1369constexpr const unsigned long kFileSize = (kWrapAt + 2) * kBlockSize; 1370 1371static bool test_tftp_receive_data_block_wrapping(void) { 1372 BEGIN_TEST; 1373 1374 test_state ts; 1375 static bool write_called; 1376 write_called = false; 1377 ts.reset(1024, 2048, 2048); 1378 1379 tftp_file_interface ifc = {NULL, NULL, NULL, NULL, NULL}; 1380 ifc.open_write = [](const char* filename, size_t size, void* cookie) -> tftp_status { 1381 EXPECT_STR_EQ(filename, kRemoteFilename, 1382 "bad filename"); 1383 EXPECT_EQ(size, kFileSize, "bad file size"); 1384 return 0; 1385 }; 1386 ifc.write = [](const void* data, size_t* length, off_t offset, void* file_cookie) 1387 -> tftp_status { 1388 // Remember that the block count starts at zero, which makes the offset 1389 // calculation a bit counter-intuitive (one might expect that we would 1390 // be writing to (kWrapAt + 1) * kBlockSize). 1391 EXPECT_EQ(kWrapAt * kBlockSize, offset, "block count failed to wrap"); 1392 write_called = true; 1393 return TFTP_NO_ERROR; 1394 }; 1395 tftp_session_set_file_interface(ts.session, &ifc); 1396 1397 char req_buf[1024]; 1398 req_buf[0] = 0x00; 1399 req_buf[1] = OPCODE_WRQ; 1400 int req_buf_sz = 2 + snprintf(&req_buf[2], sizeof(req_buf) - 2, 1401 "%s%cOCTET%cTSIZE%c%lu%cBLKSIZE%c%d", 1402 kRemoteFilename, '\0', '\0', '\0', kFileSize, '\0', '\0', 1403 kBlockSize) 1404 + 1; 1405 1406 ASSERT_LT(req_buf_sz, (int)sizeof(req_buf), "insufficient space for WRQ message"); 1407 auto status = tftp_process_msg(ts.session, req_buf, req_buf_sz, ts.out, &ts.outlen, 1408 &ts.timeout, nullptr); 1409 ASSERT_EQ(TFTP_NO_ERROR, status, "receive write request failed"); 1410 ASSERT_EQ(REQ_RECEIVED, ts.session->state, "tftp session in wrong state"); 1411 ASSERT_TRUE(verify_response_opcode(ts, OPCODE_OACK), "bad response"); 1412 1413 // Artificially advance to force block wrapping 1414 ts.session->block_number = kWrapAt; 1415 ts.session->window_index = 0; 1416 1417 uint8_t data_buf[] = { 1418 0x00, 0x03, // Opcode (DATA) 1419 0x00, 0x00, // Block 1420 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 // Data 1421 }; 1422 1423 status = tftp_process_msg(ts.session, data_buf, sizeof(data_buf), ts.out, &ts.outlen, 1424 &ts.timeout, NULL); 1425 EXPECT_EQ(TFTP_NO_ERROR, status, "failed to process data"); 1426 EXPECT_TRUE(write_called, "no attempt to write data"); 1427 EXPECT_EQ(kWrapAt + 1, ts.session->block_number, "failed to advance block number"); 1428 1429 uint8_t expected_ack[] = { 1430 0x00, 0x04, // Opcode (ACK) 1431 0x00, 0x00 // Block 1432 }; 1433 EXPECT_EQ(sizeof(expected_ack), ts.outlen, "response size mismatch"); 1434 EXPECT_EQ(0, memcmp(expected_ack, ts.out, sizeof(expected_ack)), "bad response"); 1435 1436 END_TEST; 1437} 1438 1439} 1440 1441static bool test_tftp_send_data_receive_ack(void) { 1442 BEGIN_TEST; 1443 1444 test_state ts; 1445 ts.reset(1024, 1024, 1500); 1446 1447 auto status = tftp_generate_request(ts.session, SEND_FILE, kLocalFilename, kRemoteFilename, 1448 MODE_OCTET, ts.msg_size, NULL, NULL, NULL, ts.out, &ts.outlen, &ts.timeout); 1449 EXPECT_EQ(TFTP_NO_ERROR, status, "error generating write request"); 1450 EXPECT_TRUE(verify_write_request(ts), "bad write request"); 1451 1452 uint8_t oack_buf[] = { 1453 0x00, 0x06, // Opcode (OACK) 1454 'T', 'S', 'I', 'Z', 'E', 0x00, // Option 1455 '1', '0', '2', '4', 0x00, // TSIZE value 1456 }; 1457 1458 tftp_file_interface ifc = {NULL, NULL, mock_read, NULL, NULL}; 1459 tftp_session_set_file_interface(ts.session, &ifc); 1460 1461 tx_test_data td; 1462 status = tftp_process_msg(ts.session, oack_buf, sizeof(oack_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1463 ASSERT_EQ(TFTP_NO_ERROR, status, "receive error"); 1464 EXPECT_EQ(ts.outlen, sizeof(tftp_data_msg) + DEFAULT_BLOCKSIZE, "bad outlen"); 1465 EXPECT_TRUE(verify_read_data(ts, td), "bad test data"); 1466 1467 uint8_t ack_buf[] = { 1468 0x00, 0x04, // Opcode (ACK) 1469 0x00, 0x01, // Block 1470 }; 1471 1472 td.expected.block = 2; 1473 td.expected.offset += DEFAULT_BLOCKSIZE; 1474 td.expected.data[1] = 'f'; 1475 status = tftp_process_msg(ts.session, ack_buf, sizeof(ack_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1476 EXPECT_EQ(TFTP_NO_ERROR, status, "receive error"); 1477 EXPECT_EQ(SENDING_DATA, ts.session->state, "session should be in state SENDING_DATA"); 1478 // The block number will not advance until we see an ACK for block 2 1479 EXPECT_EQ(1, ts.session->block_number, "tftp session block number mismatch"); 1480 EXPECT_EQ(1, ts.session->window_index, "tftp session window index mismatch"); 1481 EXPECT_EQ(ts.outlen, sizeof(tftp_data_msg) + DEFAULT_BLOCKSIZE, "bad outlen"); 1482 EXPECT_TRUE(verify_read_data(ts, td), "bad test data"); 1483 1484 END_TEST; 1485} 1486 1487static bool test_tftp_send_data_receive_final_ack(void) { 1488 BEGIN_TEST; 1489 1490 test_state ts; 1491 ts.reset(1024, 1024, 1500); 1492 1493 auto status = tftp_generate_request(ts.session, SEND_FILE, kLocalFilename, kRemoteFilename, 1494 MODE_OCTET, ts.msg_size, NULL, NULL, NULL, ts.out, &ts.outlen, &ts.timeout); 1495 EXPECT_EQ(TFTP_NO_ERROR, status, "error generating write request"); 1496 EXPECT_TRUE(verify_write_request(ts), "bad write request"); 1497 1498 uint8_t oack_buf[] = { 1499 0x00, 0x06, // Opcode (OACK) 1500 'T', 'S', 'I', 'Z', 'E', 0x00, // Option 1501 '1', '0', '2', '4', 0x00, // TSIZE value 1502 }; 1503 1504 tftp_file_interface ifc = {NULL, NULL, mock_read, NULL, NULL}; 1505 tftp_session_set_file_interface(ts.session, &ifc); 1506 1507 tx_test_data td; 1508 status = tftp_process_msg(ts.session, oack_buf, sizeof(oack_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1509 ASSERT_EQ(TFTP_NO_ERROR, status, "receive error"); 1510 ASSERT_EQ(ts.outlen, sizeof(tftp_data_msg) + DEFAULT_BLOCKSIZE, "bad outlen"); 1511 ASSERT_TRUE(verify_read_data(ts, td), "bad test data"); 1512 1513 uint8_t ack_buf[] = { 1514 0x00, 0x04, // Opcode (ACK) 1515 0x00, 0x01, // Block 1516 }; 1517 1518 td.expected.block = 2; 1519 td.expected.offset += DEFAULT_BLOCKSIZE; 1520 status = tftp_process_msg(ts.session, ack_buf, sizeof(ack_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1521 ASSERT_EQ(TFTP_NO_ERROR, status, "receive error"); 1522 EXPECT_EQ(ts.outlen, sizeof(tftp_data_msg) + DEFAULT_BLOCKSIZE, "bad outlen"); 1523 EXPECT_TRUE(verify_read_data(ts, td), "bad test data"); 1524 1525 // second block 1526 ack_buf[3]++; 1527 status = tftp_process_msg(ts.session, ack_buf, sizeof(ack_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1528 ASSERT_EQ(TFTP_NO_ERROR, status, "receive block 2 error"); 1529 EXPECT_EQ(ts.outlen, sizeof(tftp_data_msg), "block 3 not empty"); 1530 1531 // Do not expect any more sends. 1532 ack_buf[3]++; 1533 status = tftp_process_msg(ts.session, ack_buf, sizeof(ack_buf), ts.out, &ts.outlen, &ts.timeout, nullptr); 1534 EXPECT_EQ(TFTP_TRANSFER_COMPLETED, status, "tftp transfer should be complete"); 1535 EXPECT_EQ(ts.outlen, 0, "no outgoing message expected"); 1536 1537 END_TEST; 1538} 1539 1540static bool test_tftp_send_data_receive_ack_skipped_block(void) { 1541 BEGIN_TEST; 1542 1543 test_state ts; 1544 ts.reset(1024, 1024, 1500); 1545 1546 auto status = tftp_generate_request(ts.session, SEND_FILE, kLocalFilename, kRemoteFilename, 1547 MODE_OCTET, ts.msg_size, NULL, NULL, NULL, ts.out, &ts.outlen, &ts.timeout); 1548 EXPECT_EQ(TFTP_NO_ERROR, status, "error generating write request"); 1549 EXPECT_TRUE(verify_write_request(ts), "bad write request"); 1550 1551 uint8_t oack_buf[] = { 1552 0x00, 0x06, // Opcode (OACK) 1553 'T', 'S', 'I', 'Z', 'E', 0x00, // Option 1554 '1', '0', '2', '4', 0x00, // TSIZE value 1555 }; 1556 1557 tftp_file_interface ifc = {NULL, NULL, mock_read, NULL, NULL}; 1558 tftp_session_set_file_interface(ts.session, &ifc); 1559 1560 tx_test_data td; 1561 status = tftp_process_msg(ts.session, oack_buf, sizeof(oack_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1562 ASSERT_EQ(TFTP_NO_ERROR, status, "receive error"); 1563 ASSERT_EQ(ts.outlen, sizeof(tftp_data_msg) + DEFAULT_BLOCKSIZE, "bad outlen"); 1564 ASSERT_TRUE(verify_read_data(ts, td), "bad test data"); 1565 1566 uint8_t ack_buf[] = { 1567 0x00, 0x04, // Opcode (ACK) 1568 0x00, 0x00, // Block 1569 }; 1570 1571 tx_test_data td2; 1572 status = tftp_process_msg(ts.session, ack_buf, sizeof(ack_buf), ts.out, &ts.outlen, &ts.timeout, &td2); 1573 EXPECT_EQ(TFTP_NO_ERROR, status, "receive error"); 1574 EXPECT_EQ(SENDING_DATA, ts.session->state, "session should be in state SENDING_DATA"); 1575 EXPECT_EQ(0, ts.session->block_number, "tftp session block number mismatch"); 1576 EXPECT_EQ(1, ts.session->window_index, "tftp window index mismatch"); 1577 EXPECT_EQ(ts.outlen, sizeof(tftp_data_msg) + DEFAULT_BLOCKSIZE, "bad outlen"); 1578 EXPECT_TRUE(verify_read_data(ts, td2), "bad test data"); 1579 END_TEST; 1580} 1581 1582static bool test_tftp_send_data_receive_ack_window_size(void) { 1583 uint16_t kWindowSize = 2; 1584 BEGIN_TEST; 1585 1586 test_state ts; 1587 ts.reset(1024, 2048, 1500); 1588 1589 auto status = tftp_generate_request(ts.session, SEND_FILE, kLocalFilename, kRemoteFilename, 1590 MODE_OCTET, ts.msg_size, NULL, NULL, &kWindowSize, ts.out, &ts.outlen, &ts.timeout); 1591 EXPECT_EQ(TFTP_NO_ERROR, status, "error generating write request"); 1592 EXPECT_TRUE(verify_write_request(ts), "bad write request"); 1593 1594 uint8_t oack_buf[] = { 1595 0x00, 0x06, // Opcode (OACK) 1596 'T', 'S', 'I', 'Z', 'E', 0x00, // Option 1597 '2', '0', '4', '8', 0x00, // TSIZE value 1598 'W', 'I', 'N', 'D', 'O', 'W', 'S', 'I', 'Z', 'E', 0x00, // Option 1599 '2', 0x00, // WINDOWSIZE value 1600 }; 1601 1602 tftp_file_interface ifc = {NULL, NULL, mock_read, NULL, NULL}; 1603 tftp_session_set_file_interface(ts.session, &ifc); 1604 1605 tx_test_data td; 1606 status = tftp_process_msg(ts.session, oack_buf, sizeof(oack_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1607 ASSERT_EQ(TFTP_NO_ERROR, status, "receive error"); 1608 ASSERT_EQ(0, ts.session->block_number, "tftp session block number mismatch"); 1609 ASSERT_EQ(1, ts.session->window_index, "tftp session window index mismatch"); 1610 ASSERT_EQ(ts.outlen, sizeof(tftp_data_msg) + DEFAULT_BLOCKSIZE, "bad outlen"); 1611 ASSERT_TRUE(verify_read_data(ts, td), "bad test data"); 1612 ASSERT_TRUE(tftp_session_has_pending(ts.session), "expected pending data to transmit"); 1613 1614 td.expected.block++; 1615 td.expected.offset += DEFAULT_BLOCKSIZE; 1616 td.expected.data[0]++; 1617 status = tftp_prepare_data(ts.session, ts.out, &ts.outlen, &ts.timeout, &td); 1618 ASSERT_EQ(TFTP_NO_ERROR, status, "receive error"); 1619 // Window index doesn't roll until we receive an ACK 1620 ASSERT_EQ(0, ts.session->block_number, "tftp session block number mismatch"); 1621 ASSERT_EQ(2, ts.session->window_index, "tftp session window index mismatch"); 1622 ASSERT_EQ(ts.outlen, sizeof(tftp_data_msg) + DEFAULT_BLOCKSIZE, "bad outlen"); 1623 ASSERT_TRUE(verify_read_data(ts, td), "bad test data"); 1624 ASSERT_FALSE(tftp_session_has_pending(ts.session), "expected to wait for ack"); 1625 1626 uint8_t ack_buf[] = { 1627 0x00, 0x04, // Opcode (ACK) 1628 0x00, 0x02, // Block 1629 }; 1630 1631 td.expected.block++; 1632 td.expected.offset += DEFAULT_BLOCKSIZE; 1633 td.expected.data[1]++; 1634 status = tftp_process_msg(ts.session, ack_buf, sizeof(ack_buf), ts.out, &ts.outlen, &ts.timeout, &td); 1635 EXPECT_EQ(TFTP_NO_ERROR, status, "receive error"); 1636 EXPECT_EQ(SENDING_DATA, ts.session->state, "session should be in state SENDING_DATA"); 1637 EXPECT_EQ(2, ts.session->block_number, "tftp session block number mismatch"); 1638 EXPECT_EQ(1, ts.session->window_index, "tftp session window index mismatch"); 1639 EXPECT_EQ(ts.outlen, sizeof(tftp_data_msg) + DEFAULT_BLOCKSIZE, "bad outlen"); 1640 EXPECT_TRUE(verify_read_data(ts, td), "bad test data"); 1641 EXPECT_TRUE(tftp_session_has_pending(ts.session), "expected pending data to transmit"); 1642 1643 END_TEST; 1644} 1645 1646static bool test_tftp_send_data_receive_ack_block_wrapping(void) { 1647 BEGIN_TEST; 1648 1649 constexpr unsigned long kWrapAt = 0x3ffff; 1650 constexpr uint16_t kBlockSize = 8; 1651 constexpr unsigned long kFileSize = (kWrapAt + 2) * kBlockSize; 1652 1653 static int reads_performed; 1654 reads_performed = 0; 1655 1656 test_state ts; 1657 ts.reset(1024, 2048, 2048); 1658 1659 auto status = tftp_generate_request(ts.session, SEND_FILE, kLocalFilename, kRemoteFilename, 1660 MODE_OCTET, kFileSize, &kBlockSize, NULL, NULL, ts.out, &ts.outlen, &ts.timeout); 1661 EXPECT_EQ(TFTP_NO_ERROR, status, "error generating write request"); 1662 EXPECT_TRUE(verify_write_request(ts), "bad write request"); 1663 1664 char oack_buf[256]; 1665 oack_buf[0] = 0x00; 1666 oack_buf[1] = 0x06; 1667 size_t oack_buf_sz = 2 + snprintf(&oack_buf[2], sizeof(oack_buf) - 2, 1668 "TSIZE%c%lu%cBLKSIZE%c%d", 1669 '\0', kFileSize, '\0', '\0', kBlockSize) 1670 + 1; 1671 1672 ASSERT_LT(oack_buf_sz, (int)sizeof(oack_buf), "insufficient space for OACK message"); 1673 tftp_file_interface ifc = {NULL, NULL, NULL, NULL, NULL}; 1674 ifc.read = [](void* data, size_t* length, off_t offset, void* cookie) -> tftp_status { 1675 EXPECT_EQ(0, offset, "incorrect initial read"); 1676 reads_performed++; 1677 return TFTP_NO_ERROR; 1678 }; 1679 tftp_session_set_file_interface(ts.session, &ifc); 1680 1681 status = tftp_process_msg(ts.session, oack_buf, oack_buf_sz, ts.out, &ts.outlen, 1682 &ts.timeout, NULL); 1683 ASSERT_EQ(TFTP_NO_ERROR, status, "failure to process OACK"); 1684 EXPECT_EQ(1, reads_performed, "failed to call read function"); 1685 1686 // Artificially advance the session to a point where wrapping will occur 1687 ts.session->block_number = kWrapAt; 1688 ts.session->window_index = 0; 1689 1690 uint8_t data_buf[4 + kBlockSize]; 1691 size_t data_buf_len = sizeof(data_buf); 1692 ifc.read = [](void* data, size_t* length, off_t offset, void* cookie) -> tftp_status { 1693 // Keep in mind that the block index starts at 1, so the offset calculation 1694 // is not necessarily intuitive 1695 EXPECT_EQ(kWrapAt * kBlockSize, offset, "incorrect wrapping read"); 1696 reads_performed++; 1697 return TFTP_NO_ERROR; 1698 }; 1699 tftp_session_set_file_interface(ts.session, &ifc); 1700 status = tftp_prepare_data(ts.session, data_buf, &data_buf_len, &ts.timeout, NULL); 1701 ASSERT_EQ(TFTP_NO_ERROR, status, "failed to generate DATA packet"); 1702 EXPECT_EQ(2, reads_performed, "failed to call read function"); 1703 EXPECT_EQ(sizeof(data_buf), data_buf_len, "improperly formatted DATA packet"); 1704 unsigned int opcode = data_buf[0] << 8 | data_buf[1]; 1705 EXPECT_EQ(0x0003, opcode, "incorrect DATA packet opcode"); 1706 unsigned int block = data_buf[2] << 8 | data_buf[3]; 1707 EXPECT_EQ(0x0000, block, "incorrect DATA packet block"); 1708 1709 END_TEST; 1710} 1711 1712static bool test_tftp_send_data_receive_ack_skip_block_wrap(void) { 1713 BEGIN_TEST; 1714 1715 constexpr unsigned long kLastBlockSent = 0x40003; 1716 constexpr unsigned long kAckBlock = 0x3fffb; 1717 constexpr uint16_t kBlockSize = 8; 1718 constexpr unsigned long kFileSize = 0x50000 * kBlockSize; 1719 1720 static int reads_performed; 1721 reads_performed = 0; 1722 1723 test_state ts; 1724 ts.reset(1024, 2048, 2048); 1725 1726 // Create a write request 1727 auto status = tftp_generate_request(ts.session, SEND_FILE, kLocalFilename, kRemoteFilename, 1728 MODE_OCTET, kFileSize, &kBlockSize, NULL, NULL, ts.out, &ts.outlen, &ts.timeout); 1729 EXPECT_EQ(TFTP_NO_ERROR, status, "error generating write request"); 1730 EXPECT_TRUE(verify_write_request(ts), "bad write request"); 1731 1732 // Simulate a response (OACK) 1733 char oack_buf[256]; 1734 oack_buf[0] = 0x00; 1735 oack_buf[1] = 0x06; 1736 size_t oack_buf_sz = 2 + snprintf(&oack_buf[2], sizeof(oack_buf) - 2, 1737 "TSIZE%c%lu%cBLKSIZE%c%d", 1738 '\0', kFileSize, '\0', '\0', kBlockSize) 1739 + 1; 1740 1741 ASSERT_LT(oack_buf_sz, (int)sizeof(oack_buf), "insufficient space for OACK message"); 1742 tftp_file_interface ifc = {NULL, NULL, NULL, NULL, NULL}; 1743 ifc.read = [](void* data, size_t* length, off_t offset, void* cookie) -> tftp_status { 1744 EXPECT_EQ(0, offset, "incorrect initial read"); 1745 reads_performed++; 1746 return TFTP_NO_ERROR; 1747 }; 1748 tftp_session_set_file_interface(ts.session, &ifc); 1749 1750 // Process OACK and generate write of first block 1751 status = tftp_process_msg(ts.session, oack_buf, oack_buf_sz, ts.out, &ts.outlen, 1752 &ts.timeout, NULL); 1753 ASSERT_EQ(TFTP_NO_ERROR, status, "failure to process OACK"); 1754 EXPECT_EQ(1, reads_performed, "failed to call read function"); 1755 1756 // Artificially advance the session so we can test wrapping 1757 ts.session->block_number = kLastBlockSent; 1758 ts.session->window_index = 0; 1759 1760 // Create a DATA packet for block kLastBlockSent + 1 1761 uint8_t data_buf[4 + kBlockSize] = {0}; 1762 size_t data_buf_len = sizeof(data_buf); 1763 tftp_data_msg* msg = reinterpret_cast<tftp_data_msg*>(&data_buf[0]); 1764 ifc.read = [](void* data, size_t* length, off_t offset, void* cookie) -> tftp_status { 1765 // Keep in mind that the block index starts at 1, so the offset calculation 1766 // is not necessarily intuitive 1767 EXPECT_EQ(kLastBlockSent * kBlockSize, offset, "incorrect read offset"); 1768 reads_performed++; 1769 return TFTP_NO_ERROR; 1770 }; 1771 tftp_session_set_file_interface(ts.session, &ifc); 1772 status = tftp_prepare_data(ts.session, data_buf, &data_buf_len, &ts.timeout, NULL); 1773 ASSERT_EQ(TFTP_NO_ERROR, status, "failed to generate DATA packet"); 1774 EXPECT_EQ(2, reads_performed, "failed to call read function"); 1775 EXPECT_EQ(sizeof(data_buf), data_buf_len, "improperly formatted DATA packet"); 1776 unsigned int opcode = htons(msg->opcode); 1777 EXPECT_EQ(OPCODE_DATA, opcode, "incorrect DATA packet opcode"); 1778 uint16_t offset = ntohs(msg->block); 1779 ASSERT_EQ((kLastBlockSent + 1) & 0xffff, offset, "incorrect DATA packet block"); 1780 1781 // Simulate an ACK response that is before our last block wrap 1782 tftp_data_msg ack_msg; 1783 ack_msg.opcode = htons(OPCODE_ACK); 1784 ack_msg.block = htons(kAckBlock & 0xffff); 1785 ifc.read = [](void* data, size_t* length, off_t offset, void* cookie) -> tftp_status { 1786 EXPECT_EQ(kAckBlock * kBlockSize, offset, "incorrect read offset"); 1787 reads_performed++; 1788 return TFTP_NO_ERROR; 1789 }; 1790 tftp_session_set_file_interface(ts.session, &ifc); 1791 1792 // Next DATA packet should backup to proper address (before wrap) 1793 status = tftp_process_msg(ts.session, reinterpret_cast<void*>(&ack_msg), sizeof(ack_msg), 1794 ts.out, &ts.outlen, &ts.timeout, NULL); 1795 ASSERT_EQ(TFTP_NO_ERROR, status, "no ACK generated"); 1796 EXPECT_EQ(3, reads_performed, "failed to call read function"); 1797 ASSERT_EQ(ts.outlen, sizeof(tftp_data_msg) + kBlockSize, "improper DATA packet size"); 1798 msg = reinterpret_cast<tftp_data_msg*>(ts.out); 1799 EXPECT_EQ(OPCODE_DATA, ntohs(msg->opcode) & 0xff, "incorrect DATA packet opcode"); 1800 // Opcode prefix should have been incremented when a packet was dropped 1801 EXPECT_EQ(1, (ntohs(msg->opcode) & 0xff00) >> 8, "incorrect opcode prefix"); 1802 EXPECT_EQ((kAckBlock + 1) & 0xffff, ntohs(msg->block), "incorrect DATA packet block"); 1803 EXPECT_EQ(ts.session->block_number, kAckBlock, "session offset not rewound correctly"); 1804 EXPECT_EQ(ts.session->window_index, 1, "window index not set correctly"); 1805 1806 // Try again, this time disabling opcode prefixes 1807 ifc.read = [](void* data, size_t* length, off_t offset, void* cookie) -> tftp_status { 1808 return TFTP_NO_ERROR; 1809 }; 1810 tftp_session_set_file_interface(ts.session, &ifc); 1811 tftp_session_set_opcode_prefix_use(ts.session, false); 1812 ack_msg.block = htons((kAckBlock + 1) & 0xffff); 1813 status = tftp_process_msg(ts.session, reinterpret_cast<void*>(&ack_msg), sizeof(ack_msg), 1814 ts.out, &ts.outlen, &ts.timeout, NULL); 1815 ASSERT_EQ(TFTP_NO_ERROR, status, "no ACK generated"); 1816 ASSERT_EQ(ts.outlen, sizeof(tftp_data_msg) + kBlockSize, "improper DATA packet size"); 1817 msg = reinterpret_cast<tftp_data_msg*>(ts.out); 1818 EXPECT_EQ(OPCODE_DATA, ntohs(msg->opcode) & 0xff, "incorrect DATA packet opcode"); 1819 EXPECT_EQ(0, (ntohs(msg->opcode) & 0xff00) >> 8, "incorrect opcode prefix"); 1820 EXPECT_EQ((kAckBlock + 2) & 0xffff, ntohs(msg->block), "incorrect DATA packet block"); 1821 1822 END_TEST; 1823} 1824 1825static ssize_t open_read_should_wait(const char* filename, void* cookie) { 1826 return TFTP_ERR_SHOULD_WAIT; 1827} 1828 1829static tftp_status open_write_should_wait(const char* filename, size_t size, void* cookie) { 1830 return TFTP_ERR_SHOULD_WAIT; 1831} 1832 1833/* Verify behavior when one of our open_file interface functions returns TFTP_ERR_SHOULD_WAIT. */ 1834static bool test_tftp_open_should_wait(tftp_file_direction dir) { 1835 BEGIN_TEST; 1836 1837 const uint16_t block_size = 456; 1838 const uint8_t timeout = 3; 1839 const uint16_t window_size = 128; 1840 1841 test_state ts; 1842 ts.reset(1024, 1024, 1500); 1843 tftp_file_interface ifc = {open_read_should_wait, open_write_should_wait, NULL, NULL, NULL}; 1844 tftp_session_set_file_interface(ts.session, &ifc); 1845 1846 // Construct a RRQ or WRQ packet 1847 size_t req_file_size = (dir == SEND_FILE) ? 1024 : 0; 1848 char buf[256]; 1849 buf[0] = 0x00; 1850 buf[1] = (dir == SEND_FILE) ? OPCODE_WRQ : OPCODE_RRQ; 1851 size_t buf_sz = 2; 1852 buf_sz += snprintf(&buf[buf_sz], sizeof(buf) - buf_sz, 1853 "%s%cOCTET%cTSIZE%c%zu%cBLKSIZE%c%d%cTIMEOUT%c%d%cWINDOWSIZE%c%d", 1854 kRemoteFilename, '\0', 1855 '\0', 1856 '\0', req_file_size, '\0', 1857 '\0', block_size, '\0', 1858 '\0', timeout, '\0', 1859 '\0', window_size) + 1; 1860 ASSERT_LT(buf_sz, (int)sizeof(buf), "insufficient space for request"); 1861 1862 tftp_status status = tftp_process_msg(ts.session, buf, buf_sz, ts.out, &ts.outlen, &ts.timeout, 1863 nullptr); 1864 1865 // Check API return value 1866 EXPECT_EQ(TFTP_ERR_SHOULD_WAIT, status, "expected SHOULD_WAIT status"); 1867 1868 // tftp_process_msg should have generated an error packet response - verify its fields 1869 ASSERT_GT(ts.outlen, 0); 1870 auto msg = reinterpret_cast<tftp_err_msg*>(ts.out); 1871 EXPECT_EQ(msg->opcode, htons(OPCODE_ERROR)); 1872 EXPECT_EQ(msg->err_code, htons(TFTP_ERR_CODE_BUSY)); 1873 if (dir == SEND_FILE) { 1874 const char expected_err[] = "not ready to receive"; 1875 EXPECT_STR_EQ(expected_err, msg->msg, "bad error message"); 1876 } else { 1877 const char expected_err[] = "not ready to send"; 1878 EXPECT_STR_EQ(expected_err, msg->msg, "bad error message"); 1879 } 1880 1881 END_TEST; 1882} 1883 1884static bool test_tftp_open_read_should_wait(void) { 1885 // RECV is from the perspective of the client, not the server 1886 return test_tftp_open_should_wait(RECV_FILE); 1887} 1888 1889static bool test_tftp_open_write_should_wait(void) { 1890 // SEND is from the perspective of the client, not the server 1891 return test_tftp_open_should_wait(SEND_FILE); 1892} 1893 1894static bool test_tftp_recv_busy(tftp_file_direction dir) { 1895 BEGIN_TEST; 1896 1897 test_state ts; 1898 ts.reset(1024, 1024, 1500); 1899 1900 auto status = tftp_generate_request(ts.session, dir, kLocalFilename, kRemoteFilename, 1901 MODE_OCTET, dir == SEND_FILE ? ts.msg_size : 0, 1902 NULL, NULL, NULL, ts.out, &ts.outlen, &ts.timeout); 1903 ASSERT_EQ(TFTP_NO_ERROR, status, "error generating request"); 1904 if (dir == SEND_FILE) { 1905 ASSERT_TRUE(verify_write_request(ts), "bad write request"); 1906 } else { 1907 ASSERT_TRUE(verify_read_request(ts), "bad read request"); 1908 } 1909 1910 // Simulate a BUSY error response 1911 char buf[256]; 1912 buf[0] = 0x00; 1913 buf[1] = OPCODE_ERROR; 1914 buf[2] = (TFTP_ERR_CODE_BUSY & 0xff00) >> 8; 1915 buf[3] = TFTP_ERR_CODE_BUSY & 0xff; 1916 size_t buf_sz = 4; 1917 buf_sz += snprintf(&buf[buf_sz], sizeof(buf) - buf_sz, "not ready") + 1; 1918 ASSERT_LT(buf_sz, (int)sizeof(buf), "insufficient space for request"); 1919 1920 status = tftp_process_msg(ts.session, buf, buf_sz, ts.out, &ts.outlen, &ts.timeout, nullptr); 1921 1922 // Check API return value 1923 EXPECT_EQ(TFTP_ERR_SHOULD_WAIT, status, "expected SHOULD_WAIT status"); 1924 1925 // tftp_process_msg should not have generated a response 1926 EXPECT_EQ(ts.outlen, 0); 1927 1928 // Verify session state 1929 EXPECT_EQ(NONE, ts.session->state, "bad session: state"); 1930 1931 END_TEST; 1932} 1933 1934/* Verify handling of a BUSY error packet when we send a WRQ. */ 1935static bool test_tftp_recv_busy_from_wrq(void) { 1936 return test_tftp_recv_busy(SEND_FILE); 1937} 1938 1939/* Verify handling of a BUSY error packet when we send a RRQ. */ 1940static bool test_tftp_recv_busy_from_rrq(void) { 1941 return test_tftp_recv_busy(RECV_FILE); 1942} 1943 1944/* Verify that receiving an error other than BUSY puts the session into an error state. */ 1945static bool test_tftp_recv_other_err(void) { 1946 BEGIN_TEST; 1947 1948 test_state ts; 1949 ts.reset(1024, 1024, 1500); 1950 1951 auto status = tftp_generate_request(ts.session, SEND_FILE, kLocalFilename, kRemoteFilename, 1952 MODE_OCTET, ts.msg_size, NULL, NULL, NULL, ts.out, 1953 &ts.outlen, &ts.timeout); 1954 ASSERT_EQ(TFTP_NO_ERROR, status, "error generating request"); 1955 ASSERT_TRUE(verify_write_request(ts), "bad write request"); 1956 1957 // Simulate a BUSY error response 1958 char buf[256]; 1959 buf[0] = 0x00; 1960 buf[1] = OPCODE_ERROR; 1961 buf[2] = (TFTP_ERR_CODE_DISK_FULL & 0xff00) >> 8; 1962 buf[3] = TFTP_ERR_CODE_DISK_FULL & 0xff; 1963 size_t buf_sz = 4; 1964 buf_sz += snprintf(&buf[buf_sz], sizeof(buf) - buf_sz, "disk full") + 1; 1965 ASSERT_LT(buf_sz, (int)sizeof(buf), "insufficient space for request"); 1966 1967 status = tftp_process_msg(ts.session, buf, buf_sz, ts.out, &ts.outlen, &ts.timeout, nullptr); 1968 1969 // Check API return value 1970 EXPECT_EQ(TFTP_ERR_INTERNAL, status, "expected TFTP_ERR_INTERNAL status"); 1971 1972 // tftp_process_msg should not have generated a response 1973 EXPECT_EQ(ts.outlen, 0); 1974 1975 // Verify session state 1976 EXPECT_EQ(ERROR, ts.session->state, "bad session: state"); 1977 1978 END_TEST; 1979} 1980 1981BEGIN_TEST_CASE(tftp_setup) 1982RUN_TEST(test_tftp_init) 1983RUN_TEST(test_tftp_session_options) 1984END_TEST_CASE(tftp_setup) 1985 1986BEGIN_TEST_CASE(tftp_generate_wrq) 1987RUN_TEST(test_tftp_generate_wrq_default) 1988RUN_TEST(test_tftp_generate_wrq_options) 1989RUN_TEST(test_tftp_generate_wrq_override_blocksize) 1990RUN_TEST(test_tftp_generate_wrq_override_timeout) 1991RUN_TEST(test_tftp_generate_wrq_override_windowsize) 1992END_TEST_CASE(tftp_generate_wrq) 1993 1994BEGIN_TEST_CASE(tftp_generate_rrq) 1995RUN_TEST(test_tftp_generate_rrq_default) 1996RUN_TEST(test_tftp_generate_rrq_options) 1997RUN_TEST(test_tftp_generate_rrq_override_blocksize) 1998RUN_TEST(test_tftp_generate_rrq_override_timeout) 1999RUN_TEST(test_tftp_generate_rrq_override_windowsize) 2000END_TEST_CASE(tftp_generate_rrq) 2001 2002BEGIN_TEST_CASE(tftp_receive_wrq) 2003RUN_TEST(test_tftp_receive_wrq_unexpected) 2004RUN_TEST(test_tftp_receive_wrq_too_large) 2005RUN_TEST(test_tftp_receive_wrq_no_tsize) 2006RUN_TEST(test_tftp_receive_wrq_send_oack) 2007RUN_TEST(test_tftp_receive_wrq_blocksize) 2008RUN_TEST(test_tftp_receive_wrq_timeout) 2009RUN_TEST(test_tftp_receive_wrq_windowsize) 2010RUN_TEST(test_tftp_receive_wrq_have_overrides) 2011RUN_TEST(test_tftp_receive_force_wrq_no_overrides) 2012RUN_TEST(test_tftp_receive_force_wrq_have_overrides) 2013END_TEST_CASE(tftp_receive_wrq) 2014 2015BEGIN_TEST_CASE(tftp_receive_rrq) 2016RUN_TEST(test_tftp_receive_rrq_unexpected) 2017RUN_TEST(test_tftp_receive_rrq_too_large) 2018RUN_TEST(test_tftp_receive_rrq_no_tsize) 2019RUN_TEST(test_tftp_receive_rrq_send_oack) 2020RUN_TEST(test_tftp_receive_rrq_blocksize) 2021RUN_TEST(test_tftp_receive_rrq_timeout) 2022RUN_TEST(test_tftp_receive_rrq_windowsize) 2023RUN_TEST(test_tftp_receive_rrq_have_overrides) 2024RUN_TEST(test_tftp_receive_force_rrq_no_overrides) 2025RUN_TEST(test_tftp_receive_force_rrq_have_overrides) 2026END_TEST_CASE(tftp_receive_rrq) 2027 2028BEGIN_TEST_CASE(tftp_receive_oack) 2029RUN_TEST(test_tftp_receive_wrq_oack) 2030RUN_TEST(test_tftp_receive_wrq_oack_blocksize) 2031RUN_TEST(test_tftp_receive_wrq_oack_timeout) 2032RUN_TEST(test_tftp_receive_wrq_oack_windowsize) 2033RUN_TEST(test_tftp_receive_rrq_oack) 2034RUN_TEST(test_tftp_receive_rrq_oack_blocksize) 2035RUN_TEST(test_tftp_receive_rrq_oack_timeout) 2036RUN_TEST(test_tftp_receive_rrq_oack_windowsize) 2037RUN_TEST(test_tftp_receive_oack_overrides) 2038END_TEST_CASE(tftp_receive_oack) 2039 2040BEGIN_TEST_CASE(tftp_receive_data) 2041RUN_TEST(test_tftp_receive_data) 2042RUN_TEST(test_tftp_receive_data_final_block) 2043RUN_TEST(test_tftp_receive_data_blocksize) 2044RUN_TEST(test_tftp_receive_data_windowsize) 2045RUN_TEST(test_tftp_receive_data_skipped_block) 2046RUN_TEST(test_tftp_receive_data_windowsize_skipped_block) 2047RUN_TEST(test_tftp_receive_data_block_wrapping) 2048END_TEST_CASE(tftp_receive_data) 2049 2050BEGIN_TEST_CASE(tftp_send_data) 2051RUN_TEST(test_tftp_send_data_receive_ack) 2052RUN_TEST(test_tftp_send_data_receive_final_ack) 2053RUN_TEST(test_tftp_send_data_receive_ack_skipped_block) 2054RUN_TEST(test_tftp_send_data_receive_ack_window_size) 2055RUN_TEST(test_tftp_send_data_receive_ack_block_wrapping) 2056RUN_TEST(test_tftp_send_data_receive_ack_skip_block_wrap) 2057END_TEST_CASE(tftp_send_data) 2058 2059BEGIN_TEST_CASE(tftp_send_err) 2060RUN_TEST(test_tftp_open_read_should_wait) 2061RUN_TEST(test_tftp_open_write_should_wait) 2062END_TEST_CASE(tftp_send_err) 2063 2064BEGIN_TEST_CASE(tftp_recv_err) 2065RUN_TEST(test_tftp_recv_busy_from_rrq) 2066RUN_TEST(test_tftp_recv_busy_from_wrq) 2067RUN_TEST(test_tftp_recv_other_err) 2068END_TEST_CASE(tftp_recv_err) 2069 2070int main(int argc, char* argv[]) { 2071 return unittest_run_all_tests(argc, argv) ? 0 : -1; 2072} 2073