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#define _POSIX_C_SOURCE 200809L // for strnlen 6#include <arpa/inet.h> 7#include <ctype.h> 8#include <inttypes.h> 9#include <stdarg.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <string.h> 13#include <strings.h> 14 15#include "internal.h" 16 17// TODO: update this 18// RRQ -> 19// <- DATA or OACK or ERROR 20// ACK(0) -> (to confirm reception of OACK) 21// ERROR -> (on OACK with non requested options) 22// <- DATA(1) 23// ACK(1) -> 24 25// WRQ -> 26// <- ACK or OACK or ERROR 27// DATA(1) -> 28// ERROR -> (on OACK with non requested options) 29// <- DATA(2) 30// ACK(2) -> 31 32// MODE 33static const char* kNetascii = "NETASCII"; 34static const char* kOctet = "OCTET"; 35static const char* kMail = "MAIL"; 36static const size_t kMaxMode = 9; // strlen(NETASCII) + 1 37 38// TSIZE 39// Limit transfer to less than 10GB 40static const char* kTsize = "TSIZE"; 41static const size_t kTsizeLen = 5; // strlen(kTsize) 42static const size_t kMaxTsizeOpt = 17; // strlen(TSIZE) + 1 + strlen(1000000000) + 1 43 44// BLKSIZE 45// Max size is 65535 (max IP datagram) 46static const char* kBlkSize = "BLKSIZE"; 47static const size_t kBlkSizeLen = 7; // strlen(kBlkSize) 48static const size_t kMaxBlkSizeOpt = 15; // kBlkSizeLen + strlen("!") + 1 + strlen(65535) + 1 49 50// TIMEOUT 51// Max is 255 (RFC 2349) 52static const char* kTimeout = "TIMEOUT"; 53static const size_t kTimeoutLen = 7; // strlen(kTimeout) 54static const size_t kMaxTimeoutOpt = 13; // kTimeoutLen + strlen("!") + 1 + strlen(255) + 1; 55 56// WINDOWSIZE 57// Max is 65535 (RFC 7440) 58static const char* kWindowSize = "WINDOWSIZE"; 59static const size_t kWindowSizeLen = 10; // strlen(kWindowSize); 60static const size_t kMaxWindowSizeOpt = 18; // kWindowSizeLen + strlen("!") + 1 + strlen(65535) + 1; 61 62// Since RRQ and WRQ come before option negotation, they are limited to max TFTP 63// blocksize of 512 (RFC 1350 and 2347). 64static const size_t kMaxRequestSize = 512; 65 66#if defined(TFTP_HOSTLIB) 67// Host (e.g., netcp, bootserver) 68#include <time.h> 69#define DEBUG 0 70#elif defined(TFTP_USERLIB) 71// Fuchsia (e.g., netsvc) 72#define DEBUG 0 73#elif defined(TFTP_EFILIB) 74// Bootloader: use judiciously, since the console can easily become overwhelmed and hang 75#define DEBUG 0 76#else 77#error unable to identify target environment 78#endif 79 80#if DEBUG 81# define xprintf(args...) fprintf(stderr, args) 82#else 83# define xprintf(args...) 84#endif 85 86#define __ATTR_PRINTF(__fmt, __varargs) \ 87 __attribute__((__format__(__printf__, __fmt, __varargs))) 88#define MIN(x,y) ((x) < (y) ? (x) : (y)) 89 90static void append_option_name(char** body, size_t* left, const char* name) { 91 size_t offset = strlen(name); 92 memcpy(*body, name, offset); 93 offset++; 94 *body += offset; 95 *left -= offset; 96} 97 98static void __ATTR_PRINTF(5, 6) append_option(char** body, size_t* left, const char* name, 99 bool force, const char* fmt, ...) { 100 char* bodyp = *body; 101 size_t leftp = *left; 102 103 size_t offset = strlen(name); 104 memcpy(bodyp, name, offset); 105 if (force) { 106 bodyp[offset] = '!'; 107 offset++; 108 } 109 offset++; 110 bodyp += offset; 111 leftp -= offset; 112 va_list args; 113 va_start(args, fmt); 114 offset = vsnprintf(bodyp, leftp - 1, fmt, args); 115 va_end(args); 116 offset++; 117 bodyp += offset; 118 leftp -= offset; 119 120 *body = bodyp; 121 *left = leftp; 122} 123 124#define OPCODE(session, msg, value) \ 125 do { \ 126 if (session->use_opcode_prefix) { \ 127 (msg)->opcode = htons((value & 0xff) | ((uint16_t)session->opcode_prefix << 8)); \ 128 } else { \ 129 (msg)->opcode = htons(value); \ 130 } \ 131 } while (0) 132 133#define TRANSMIT_MORE 1 134#define TRANSMIT_WAIT_ON_ACK 2 135 136static size_t next_option(char* buffer, size_t len, char** option, char** value) { 137 size_t left = len; 138 size_t option_len = strnlen(buffer, left); 139 if (option_len == len) { 140 return 0; 141 } 142 143 *option = buffer; 144 xprintf("'%s' %ld\n", *option, option_len); 145 buffer += option_len + 1; 146 left -= option_len + 1; 147 size_t value_len = strnlen(buffer, left); 148 if (value_len == left) { 149 return 0; 150 } 151 *value = buffer; 152 xprintf("'%s' %ld\n", *value, value_len); 153 left -= value_len + 1; 154 return len - left; 155} 156 157/* Build an err packet in resp_buf and set session state to ERROR 158 159 2 bytes 2 bytes string 1 byte 160 +--------------+----------+---------+------+ 161 | OPCODE_ERROR | ERR_CODE | ERR_MSG | 0 | 162 +--------------+----------+---------+------+ 163*/ 164static void set_error(tftp_session* session, uint16_t err_code, void* resp_buf, 165 size_t* resp_len, const char* err_msg) { 166 tftp_err_msg* resp = resp_buf; 167 OPCODE(session, resp, OPCODE_ERROR); 168 resp->err_code = htons(err_code); 169 size_t err_msg_len = strlen(err_msg); 170 size_t max_msg_sz = *resp_len - (sizeof(tftp_err_msg) + 1); 171 if (err_msg_len >= max_msg_sz) { 172 memcpy(resp->msg, err_msg, max_msg_sz); 173 resp->msg[max_msg_sz] = '\0'; 174 // *resp_len is unchanged - the whole buffer was used 175 } else { 176 strcpy(resp->msg, err_msg); 177 *resp_len = sizeof(tftp_err_msg) + err_msg_len + 1; 178 } 179 session->state = ERROR; 180} 181 182tftp_status tx_data(tftp_session* session, tftp_data_msg* resp, size_t* outlen, void* cookie) { 183 session->offset = (session->block_number + session->window_index) * session->block_size; 184 *outlen = 0; 185 if (session->offset <= session->file_size) { 186 session->window_index++; 187 OPCODE(session, resp, OPCODE_DATA); 188 resp->block = htons(session->block_number + session->window_index); 189 size_t len = MIN(session->file_size - session->offset, session->block_size); 190 xprintf(" -> Copying block #%" PRIu64 " (size:%zu/%d) from %zu/%zu [%d/%d]\n", 191 session->block_number + session->window_index, len, session->block_size, 192 session->offset, session->file_size, session->window_index, session->window_size); 193 void* buf = resp->data; 194 size_t len_remaining = len; 195 size_t off = session->offset; 196 while (len_remaining > 0) { 197 // TODO(tkilbourn): assert that these function pointers are set 198 size_t rr = len_remaining; 199 tftp_status s = session->file_interface.read(buf, &rr, off, cookie); 200 if (s < 0) { 201 xprintf("Err reading: %d\n", s); 202 return s; 203 } 204 buf += rr; 205 off += rr; 206 len_remaining -= rr; 207 } 208 *outlen = sizeof(*resp) + len; 209 210 if (session->window_index < session->window_size) { 211 xprintf(" -> TRANSMIT_MORE(%d < %d)\n", session->window_index, session->window_size); 212 } else { 213 xprintf(" -> TRANSMIT_WAIT_ON_ACK(%d >= %d)\n", session->window_index, 214 session->window_size); 215 } 216 } else { 217 xprintf(" -> TRANSMIT_WAIT_ON_ACK(completed)\n"); 218 } 219 return TFTP_NO_ERROR; 220} 221 222size_t tftp_sizeof_session(void) { 223 return sizeof(tftp_session); 224} 225 226int tftp_init(tftp_session** session, void* buffer, size_t size) { 227 if (buffer == NULL) { 228 return TFTP_ERR_INVALID_ARGS; 229 } 230 if (size < sizeof(tftp_session)) { 231 return TFTP_ERR_BUFFER_TOO_SMALL; 232 } 233 *session = buffer; 234 tftp_session* s = *session; 235 memset(s, 0, sizeof(tftp_session)); 236 237 // Sensible defaults for non-negotiated values 238 s->file_size = DEFAULT_FILESIZE; 239 s->mode = DEFAULT_MODE; 240 s->max_timeouts = DEFAULT_MAX_TIMEOUTS; 241 s->use_opcode_prefix = DEFAULT_USE_OPCODE_PREFIX; 242 243 return TFTP_NO_ERROR; 244} 245 246tftp_status tftp_session_set_file_interface(tftp_session* session, 247 tftp_file_interface* callbacks) { 248 if (session == NULL) { 249 return TFTP_ERR_INVALID_ARGS; 250 } 251 252 session->file_interface = *callbacks; 253 return TFTP_NO_ERROR; 254} 255 256tftp_status tftp_session_set_transport_interface(tftp_session* session, 257 tftp_transport_interface* callbacks) { 258 if (session == NULL) { 259 return TFTP_ERR_INVALID_ARGS; 260 } 261 session->transport_interface = *callbacks; 262 return TFTP_NO_ERROR; 263} 264 265bool tftp_session_has_pending(tftp_session* session) { 266 return session->direction == SEND_FILE && 267 session->window_index > 0 && 268 session->window_index < session->window_size && 269 ((session->block_number + session->window_index) * session->block_size) <= 270 session->file_size; 271} 272 273tftp_status tftp_set_options(tftp_session* session, const uint16_t* block_size, 274 const uint8_t* timeout, const uint16_t* window_size) { 275 session->options.mask = 0; 276 if (block_size) { 277 session->options.block_size = *block_size; 278 session->options.mask |= BLOCKSIZE_OPTION; 279 } 280 if (timeout) { 281 session->options.timeout = *timeout; 282 session->options.mask |= TIMEOUT_OPTION; 283 } 284 if (window_size) { 285 session->options.window_size = *window_size; 286 session->options.mask |= WINDOWSIZE_OPTION; 287 } 288 return TFTP_NO_ERROR; 289} 290 291tftp_status tftp_generate_request(tftp_session* session, 292 tftp_file_direction direction, 293 const char* local_filename, 294 const char* remote_filename, 295 tftp_mode mode, 296 size_t datalen, 297 const uint16_t* block_size, 298 const uint8_t* timeout, 299 const uint16_t* window_size, 300 void* outgoing, 301 size_t* outlen, 302 uint32_t* timeout_ms) { 303 if (*outlen < 2) { 304 xprintf("outlen too short: %zd\n", *outlen); 305 return TFTP_ERR_BUFFER_TOO_SMALL; 306 } 307 308 // The actual options are not set until we get a confirmation OACK message. Until then, 309 // we have to assume the TFTP defaults. 310 session->block_size = DEFAULT_BLOCKSIZE; 311 session->timeout = DEFAULT_TIMEOUT; 312 session->window_size = DEFAULT_WINDOWSIZE; 313 314 tftp_msg* ack = outgoing; 315 OPCODE(session, ack, (direction == SEND_FILE) ? OPCODE_WRQ : OPCODE_RRQ); 316 char* body = ack->data; 317 memset(body, 0, *outlen - sizeof(*ack)); 318 size_t left = *outlen - sizeof(*ack); 319 size_t remote_filename_len = strlen(remote_filename); 320 if (remote_filename_len + 1 > left - kMaxMode) { 321 xprintf("filename too long %zd > %zd\n", remote_filename_len, left - kMaxMode); 322 return TFTP_ERR_INVALID_ARGS; 323 } 324 memcpy(body, remote_filename, remote_filename_len); 325 body += remote_filename_len + 1; 326 left -= remote_filename_len + 1; 327 strncpy(session->filename, local_filename, sizeof(session->filename) - 1); 328 session->filename[sizeof(session->filename) - 1] = '\0'; 329 switch (mode) { 330 case MODE_NETASCII: 331 append_option_name(&body, &left, kNetascii); 332 break; 333 case MODE_OCTET: 334 append_option_name(&body, &left, kOctet); 335 break; 336 case MODE_MAIL: 337 append_option_name(&body, &left, kMail); 338 break; 339 default: 340 return TFTP_ERR_INVALID_ARGS; 341 } 342 session->mode = mode; 343 344 if (left < kMaxTsizeOpt) { 345 return TFTP_ERR_BUFFER_TOO_SMALL; 346 } 347 append_option(&body, &left, kTsize, false, "%zu", datalen); 348 session->file_size = datalen; 349 tftp_options* sent_opts = &session->client_sent_opts; 350 sent_opts->mask = 0; 351 352 if (block_size || session->options.mask & BLOCKSIZE_OPTION) { 353 if (left < kMaxBlkSizeOpt) { 354 return TFTP_ERR_BUFFER_TOO_SMALL; 355 } 356 bool force_value; 357 if (block_size) { 358 force_value = true; 359 sent_opts->block_size = *block_size; 360 } else { 361 force_value = false; 362 sent_opts->block_size = session->options.block_size; 363 } 364 append_option(&body, &left, kBlkSize, force_value, "%"PRIu16, sent_opts->block_size); 365 sent_opts->mask |= BLOCKSIZE_OPTION; 366 } 367 368 if (timeout || session->options.mask & TIMEOUT_OPTION) { 369 if (left < kMaxTimeoutOpt) { 370 return TFTP_ERR_BUFFER_TOO_SMALL; 371 } 372 bool force_value; 373 if (timeout) { 374 force_value = true; 375 sent_opts->timeout = *timeout; 376 } else { 377 force_value = false; 378 sent_opts->timeout = session->options.timeout; 379 } 380 append_option(&body, &left, kTimeout, force_value, "%"PRIu8, sent_opts->timeout); 381 sent_opts->mask |= TIMEOUT_OPTION; 382 } 383 384 if (window_size || session->options.mask & WINDOWSIZE_OPTION) { 385 if (left < kMaxWindowSizeOpt) { 386 return TFTP_ERR_BUFFER_TOO_SMALL; 387 } 388 bool force_value; 389 if (window_size) { 390 force_value = true; 391 sent_opts->window_size = *window_size; 392 } else { 393 force_value = false; 394 sent_opts->window_size = session->options.window_size; 395 } 396 append_option(&body, &left, kWindowSize, force_value, "%"PRIu16, sent_opts->window_size); 397 sent_opts->mask |= WINDOWSIZE_OPTION; 398 } 399 400 *outlen = *outlen - left; 401 // Nothing has been negotiated yet so use default 402 *timeout_ms = 1000 * session->timeout; 403 404 session->direction = direction; 405 session->state = REQ_SENT; 406 xprintf("Generated %s request, len=%zu\n", 407 (direction == SEND_FILE) ? "write" : "read", *outlen); 408 return TFTP_NO_ERROR; 409} 410 411tftp_status tftp_handle_request(tftp_session* session, 412 tftp_file_direction direction, 413 tftp_msg* req, 414 size_t req_len, 415 tftp_msg* resp, 416 size_t* resp_len, 417 uint32_t* timeout_ms, 418 void* cookie) { 419 // We could be in REQ_RECEIVED if our OACK was dropped. 420 if (session->state != NONE && session->state != REQ_RECEIVED) { 421 xprintf("Invalid state transition %d -> %d\n", session->state, REQ_RECEIVED); 422 set_error(session, TFTP_ERR_CODE_UNDEF, resp, resp_len, "invalid state transition"); 423 return TFTP_ERR_BAD_STATE; 424 } 425 // opcode, filename, 0, mode, 0, opt1, 0, value1 ... optN, 0, valueN, 0 426 // Max length is 512 no matter 427 if (req_len > kMaxRequestSize) { 428 xprintf("Write request is too large\n"); 429 set_error(session, TFTP_ERR_CODE_UNDEF, resp, resp_len, "write request is too large"); 430 return TFTP_ERR_INTERNAL; 431 } 432 // Skip opcode 433 size_t left = req_len - sizeof(*resp); 434 char* cur = req->data; 435 char *option, *value; 436 // filename, 0, mode, 0 can be interpreted like option, 0, value, 0 437 size_t offset = next_option(cur, left, &option, &value); 438 if (!offset) { 439 xprintf("No options\n"); 440 set_error(session, TFTP_ERR_CODE_BAD_OPTIONS, resp, resp_len, "no options"); 441 return TFTP_ERR_INTERNAL; 442 } 443 left -= offset; 444 445 xprintf("filename = '%s', mode = '%s'\n", option, value); 446 447 strncpy(session->filename, option, sizeof(session->filename) - 1); 448 session->filename[sizeof(session->filename) - 1] = '\0'; 449 char* mode = value; 450 if (!strncasecmp(mode, kNetascii, strlen(kNetascii))) { 451 session->mode = MODE_NETASCII; 452 } else if (!strncasecmp(mode, kOctet, strlen(kOctet))) { 453 session->mode = MODE_OCTET; 454 } else if (!strncasecmp(mode, kMail, strlen(kMail))) { 455 session->mode = MODE_MAIL; 456 } else { 457 xprintf("Unknown write request mode\n"); 458 set_error(session, TFTP_ERR_CODE_BAD_OPTIONS, resp, resp_len, "unknown write request mode"); 459 return TFTP_ERR_INTERNAL; 460 } 461 462 // Initialize the values to TFTP defaults 463 session->block_size = DEFAULT_BLOCKSIZE; 464 session->timeout = DEFAULT_TIMEOUT; 465 session->window_size = DEFAULT_WINDOWSIZE; 466 467 // TODO(tkilbourn): refactor option handling code to share with 468 // tftp_handle_oack 469 cur += offset; 470 bool file_size_seen = false; 471 tftp_options requested_options = {.mask = 0}; 472 tftp_options* override_opts = &session->options; 473 while (offset > 0 && left > 0) { 474 offset = next_option(cur, left, &option, &value); 475 if (!offset) { 476 xprintf("No more options\n"); 477 set_error(session, TFTP_ERR_CODE_BAD_OPTIONS, resp, resp_len, "no more options"); 478 return TFTP_ERR_INTERNAL; 479 } 480 481 if (!strncasecmp(option, kTsize, kTsizeLen)) { // RFC 2349 482 if (direction == RECV_FILE) { 483 long val = atol(value); 484 if (val < 0) { 485 xprintf("invalid file size\n"); 486 set_error(session, TFTP_ERR_CODE_BAD_OPTIONS, resp, resp_len, 487 "invalid file size"); 488 return TFTP_ERR_INTERNAL; 489 } 490 session->file_size = val; 491 } 492 file_size_seen = true; 493 } else if (!strncasecmp(option, kBlkSize, kBlkSizeLen)) { // RFC 2348 494 bool force_block_size = (option[kBlkSizeLen] == '!'); 495 // Valid values range between "8" and "65464" octets, inclusive 496 long val = atol(value); 497 // TODO(tkilbourn): with an MTU of 1500, shouldn't be more than 1428 498 if (val < 8 || val > 65464) { 499 xprintf("invalid block size\n"); 500 set_error(session, TFTP_ERR_CODE_BAD_OPTIONS, resp, resp_len, "invalid block size"); 501 return TFTP_ERR_INTERNAL; 502 } 503 requested_options.block_size = val; 504 requested_options.mask |= BLOCKSIZE_OPTION; 505 if (force_block_size || !(override_opts->mask & BLOCKSIZE_OPTION)) { 506 session->block_size = val; 507 } else { 508 session->block_size = override_opts->block_size; 509 } 510 } else if (!strncasecmp(option, kTimeout, kTimeoutLen)) { // RFC 2349 511 bool force_timeout_val = (option[kTimeoutLen] == '!'); 512 // Valid values range between "1" and "255" seconds inclusive. 513 long val = atol(value); 514 if (val < 1 || val > 255) { 515 xprintf("invalid timeout\n"); 516 set_error(session, TFTP_ERR_CODE_BAD_OPTIONS, resp, resp_len, "invalid timeout"); 517 return TFTP_ERR_INTERNAL; 518 } 519 requested_options.timeout = val; 520 requested_options.mask |= TIMEOUT_OPTION; 521 if (force_timeout_val || !(override_opts->mask & TIMEOUT_OPTION)) { 522 session->timeout = val; 523 } else { 524 session->timeout = override_opts->timeout; 525 } 526 } else if (!strncasecmp(option, kWindowSize, kWindowSizeLen)) { // RFC 7440 527 bool force_window_size = (option[kWindowSizeLen] == '!'); 528 // The valid values range MUST be between 1 and 65535 blocks, inclusive. 529 long val = atol(value); 530 if (val < 1 || val > 65535) { 531 xprintf("invalid window size\n"); 532 set_error(session, TFTP_ERR_CODE_BAD_OPTIONS, resp, resp_len, 533 "invalid window size"); 534 return TFTP_ERR_INTERNAL; 535 } 536 requested_options.window_size = val; 537 requested_options.mask |= WINDOWSIZE_OPTION; 538 if (force_window_size || !(override_opts->mask & WINDOWSIZE_OPTION)) { 539 session->window_size = val; 540 } else { 541 session->window_size = override_opts->window_size; 542 } 543 } else { 544 // Options which the server does not support should be omitted from the 545 // OACK; they should not cause an ERROR packet to be generated. 546 } 547 548 cur += offset; 549 left -= offset; 550 } 551 552 char* body = resp->data; 553 memset(body, 0, *resp_len - sizeof(*resp)); 554 left = *resp_len - sizeof(*resp); 555 556 OPCODE(session, resp, OPCODE_OACK); 557 558 // Open file, if we haven't already 559 if (session->state == NONE) { 560 if (direction == RECV_FILE) { 561 if (!session->file_interface.open_write) { 562 xprintf("Unable to service write request: no open_write implementation\n"); 563 set_error(session, TFTP_ERR_CODE_UNDEF, resp, resp_len, "internal error"); 564 return TFTP_ERR_BAD_STATE; 565 } 566 switch(session->file_interface.open_write(session->filename, session->file_size, 567 cookie)) { 568 case TFTP_ERR_SHOULD_WAIT: 569 // The open_write() callback can return an ERR_SHOULD_WAIT response if it isn't 570 // prepared to service another requst at the moment and the client should retry 571 // later. 572 xprintf("Denying write request received when not ready\n"); 573 set_error(session, TFTP_ERR_CODE_BUSY, resp, resp_len, "not ready to receive"); 574 session->state = NONE; 575 return TFTP_ERR_SHOULD_WAIT; 576 case TFTP_NO_ERROR: 577 break; 578 default: 579 xprintf("Could not open file on write request\n"); 580 set_error(session, TFTP_ERR_CODE_ACCESS_VIOLATION, resp, resp_len, 581 "could not open file for writing"); 582 return TFTP_ERR_BAD_STATE; 583 } 584 } else { 585 ssize_t file_size; 586 if (!session->file_interface.open_read) { 587 xprintf("Unable to service read request: no open_read implementation\n"); 588 set_error(session, TFTP_ERR_CODE_UNDEF, resp, resp_len, "internal error"); 589 return TFTP_ERR_BAD_STATE; 590 } 591 592 file_size = session->file_interface.open_read(session->filename, cookie); 593 if (file_size == TFTP_ERR_SHOULD_WAIT) { 594 // The open_read() callback can return an ERR_SHOULD_WAIT response if it isn't 595 // prepared to service another requst at the moment and the client should retry 596 // later. 597 xprintf("Denying read request received when not ready\n"); 598 set_error(session, TFTP_ERR_CODE_BUSY, resp, resp_len, "not ready to send"); 599 session->state = NONE; 600 return TFTP_ERR_SHOULD_WAIT; 601 } 602 if (file_size < 0) { 603 xprintf("Unable to open file %s for reading\n", session->filename); 604 set_error(session, TFTP_ERR_CODE_FILE_NOT_FOUND, resp, resp_len, 605 "could not open file for reading"); 606 return TFTP_ERR_BAD_STATE; 607 } 608 session->file_size = file_size; 609 } 610 } 611 612 if (file_size_seen) { 613 append_option(&body, &left, kTsize, false, "%zu", session->file_size); 614 } else { 615 xprintf("No TSIZE option specified\n"); 616 set_error(session, TFTP_ERR_CODE_BAD_OPTIONS, resp, resp_len, "no TSIZE option"); 617 if (session->file_interface.close) { 618 session->file_interface.close(cookie); 619 } 620 return TFTP_ERR_BAD_STATE; 621 } 622 if (requested_options.mask & BLOCKSIZE_OPTION) { 623 // TODO(jpoichet) Make sure this block size is possible. Need API upwards to 624 // request allocation of block size * window size memory 625 append_option(&body, &left, kBlkSize, false, "%d", session->block_size); 626 } 627 if (requested_options.mask & TIMEOUT_OPTION) { 628 // TODO(jpoichet) Make sure this timeout is possible. Need API upwards to 629 // request allocation of block size * window size memory 630 append_option(&body, &left, kTimeout, false, "%d", session->timeout); 631 *timeout_ms = 1000 * session->timeout; 632 } 633 if (requested_options.mask & WINDOWSIZE_OPTION) { 634 append_option(&body, &left, kWindowSize, false, "%d", session->window_size); 635 } 636 *resp_len = *resp_len - left; 637 session->state = REQ_RECEIVED; 638 session->direction = direction; 639 640 xprintf("%s Request Parsed\n", (direction == SEND_FILE) ? "Read" : "Write"); 641 xprintf(" Mode : %s\n", session->mode == MODE_NETASCII ? "netascii" : 642 session->mode == MODE_OCTET ? "octet" : 643 session->mode == MODE_MAIL ? "mail" : 644 "unrecognized"); 645 xprintf(" File Size : %zu\n", session->file_size); 646 xprintf("Options requested: %08x\n", requested_options.mask); 647 xprintf(" Block Size : %d\n", requested_options.block_size); 648 xprintf(" Timeout : %d\n", requested_options.timeout); 649 xprintf(" Window Size: %d\n", requested_options.window_size); 650 xprintf("Using options\n"); 651 xprintf(" Block Size : %d\n", session->block_size); 652 xprintf(" Timeout : %d\n", session->timeout); 653 xprintf(" Window Size: %d\n", session->window_size); 654 655 return TFTP_NO_ERROR; 656} 657 658tftp_status tftp_handle_wrq(tftp_session* session, 659 tftp_msg* wrq, 660 size_t wrq_len, 661 tftp_msg* resp, 662 size_t* resp_len, 663 uint32_t* timeout_ms, 664 void* cookie) { 665 return tftp_handle_request(session, RECV_FILE, wrq, wrq_len, resp, resp_len, timeout_ms, 666 cookie); 667} 668 669tftp_status tftp_handle_rrq(tftp_session* session, 670 tftp_msg* rrq, 671 size_t rrq_len, 672 tftp_msg* resp, 673 size_t* resp_len, 674 uint32_t* timeout_ms, 675 void* cookie) { 676 return tftp_handle_request(session, SEND_FILE, rrq, rrq_len, resp, resp_len, timeout_ms, 677 cookie); 678} 679 680static void tftp_prepare_ack(tftp_session* session, 681 tftp_msg* msg, 682 size_t* msg_len) { 683 tftp_data_msg* ack_data = (tftp_data_msg*)msg; 684 xprintf(" -> Ack %" PRIu64 "\n", session->block_number); 685 session->window_index = 0; 686 OPCODE(session, ack_data, OPCODE_ACK); 687 ack_data->block = htons(session->block_number & 0xffff); 688 *msg_len = sizeof(*ack_data); 689} 690 691tftp_status tftp_handle_data(tftp_session* session, 692 tftp_msg* msg, 693 size_t msg_len, 694 tftp_msg* resp, 695 size_t* resp_len, 696 uint32_t* timeout_ms, 697 void* cookie) { 698 if ((session->direction == RECV_FILE) && 699 ((session->state == REQ_RECEIVED) || 700 (session->state == FIRST_DATA) || 701 (session->state == RECEIVING_DATA))) { 702 session->state = RECEIVING_DATA; 703 } else { 704 set_error(session, TFTP_ERR_CODE_UNDEF, resp, resp_len, "internal error: bad state"); 705 return TFTP_ERR_INTERNAL; 706 } 707 708 tftp_data_msg* data = (tftp_data_msg*)msg; 709 710 uint16_t block_num = ntohs(data->block); 711 712 // The block field of the message is only 16 bits wide. To support large files 713 // (> 65535 * blocksize bytes), we allow the block number to wrap. We use signed modulo 714 // math to determine the relative location of the block to our current position. 715 int16_t block_delta = block_num - (uint16_t)session->block_number; 716 xprintf(" <- Block %" PRIu64 " (Last = %" PRIu64 ", Offset = %" PRIu64 717 ", Size = %zd, Left = %" PRIu64 ")\n", 718 session->block_number + block_delta, session->block_number, 719 session->block_number * session->block_size, session->file_size, 720 session->file_size - session->block_number * session->block_size); 721 if (block_delta == 1) { 722 xprintf("Advancing normally + 1\n"); 723 void* buf = data->data; 724 size_t len = msg_len - sizeof(tftp_data_msg); 725 size_t off = session->block_number * session->block_size; 726 while (len > 0) { 727 tftp_status ret; 728 // TODO(tkilbourn): assert that these function pointers are set 729 size_t wr = len; 730 ret = session->file_interface.write(buf, &wr, off, cookie); 731 if (ret < 0) { 732 xprintf("Error writing: %d\n", ret); 733 return ret; 734 } 735 buf += wr; 736 off += wr; 737 len -= wr; 738 } 739 session->block_number++; 740 session->window_index++; 741 } else if (block_delta > 1) { 742 // Force sending a ACK with the last block_number we received 743 xprintf("Skipped: got %" PRIu64 ", expected %" PRIu64 "\n", 744 session->block_number + block_delta, session->block_number + 1); 745 session->window_index = session->window_size; 746 // It's possible that a previous ACK wasn't received, increment the prefix 747 if (session->use_opcode_prefix) { 748 session->opcode_prefix++; 749 } 750 } 751 752 if (session->window_index == session->window_size || 753 session->block_number * session->block_size > session->file_size) { 754 tftp_prepare_ack(session, resp, resp_len); 755 if (session->block_number * session->block_size > session->file_size) { 756 return TFTP_TRANSFER_COMPLETED; 757 } 758 } else { 759 // Nothing to send 760 *resp_len = 0; 761 } 762 return TFTP_NO_ERROR; 763} 764 765tftp_status tftp_handle_ack(tftp_session* session, 766 tftp_msg* ack, 767 size_t ack_len, 768 tftp_msg* resp, 769 size_t* resp_len, 770 uint32_t* timeout_ms, 771 void* cookie) { 772 if ((session->direction != SEND_FILE) || 773 ((session->state != FIRST_DATA) && 774 (session->state != REQ_RECEIVED) && 775 (session->state != SENDING_DATA))) { 776 set_error(session, TFTP_ERR_CODE_UNDEF, resp, resp_len, "internal error: bad state"); 777 return TFTP_ERR_INTERNAL; 778 } 779 // Need to move forward in data and send it 780 tftp_data_msg* ack_data = (void*)ack; 781 tftp_data_msg* resp_data = (void*)resp; 782 783 uint16_t ack_block = ntohs(ack_data->block); 784 xprintf(" <- Ack %d\n", ack_block); 785 786 // Since we track blocks in 32 bits, but the packets only support 16 bits, calculate the 787 // signed 16 bit offset to determine the adjustment to the current position. 788 int16_t block_offset = ack_block - (uint16_t)session->block_number; 789 790 if (session->state != FIRST_DATA && session->state != REQ_RECEIVED && block_offset == 0) { 791 // Don't acknowledge duplicate ACKs, avoiding the "Sorcerer's Apprentice Syndrome" 792 *resp_len = 0; 793 return TFTP_NO_ERROR; 794 } 795 796 if (block_offset < session->window_size) { 797 // If it looks like some of our data might have been dropped, modify the prefix 798 // before resending. 799 if (session->use_opcode_prefix) { 800 session->opcode_prefix++; 801 } 802 } 803 session->state = SENDING_DATA; 804 session->block_number += block_offset; 805 session->window_index = 0; 806 807 if (session->block_number * session->block_size > session->file_size) { 808 *resp_len = 0; 809 return TFTP_TRANSFER_COMPLETED; 810 } 811 812 tftp_status ret = tx_data(session, resp_data, resp_len, cookie); 813 if (ret < 0) { 814 set_error(session, TFTP_ERR_CODE_UNDEF, resp, resp_len, "could not transmit data"); 815 } 816 return ret; 817} 818 819tftp_status tftp_handle_error(tftp_session* session, 820 tftp_err_msg* err, 821 size_t err_len, 822 tftp_msg* resp, 823 size_t* resp_len, 824 uint32_t* timeout_ms, 825 void* cookie) { 826 uint16_t err_code = ntohs(err->err_code); 827 828 // There's no need to respond to an error 829 *resp_len = 0; 830 831 if (err_code == TFTP_ERR_CODE_BUSY) { 832 xprintf("Target busy\n"); 833 session->state = NONE; 834 return TFTP_ERR_SHOULD_WAIT; 835 } 836 xprintf("Target sent error %d\n", err_code); 837 session->state = ERROR; 838 return TFTP_ERR_INTERNAL; 839} 840 841tftp_status tftp_handle_oack(tftp_session* session, 842 tftp_msg* oack, 843 size_t oack_len, 844 tftp_msg* resp, 845 size_t* resp_len, 846 uint32_t* timeout_ms, 847 void* cookie) { 848 xprintf("Option Ack\n"); 849 if (session->state == REQ_SENT || session->state == FIRST_DATA) { 850 session->state = FIRST_DATA; 851 } 852 853 size_t left = oack_len - sizeof(*oack); 854 char* cur = oack->data; 855 size_t offset; 856 char *option, *value; 857 858 while (left > 0) { 859 offset = next_option(cur, left, &option, &value); 860 if (!offset) { 861 set_error(session, TFTP_ERR_CODE_BAD_OPTIONS, resp, resp_len, "invalid option format"); 862 return TFTP_ERR_INTERNAL; 863 } 864 865 if (!strncasecmp(option, kTsize, kTsizeLen)) { // RFC 2349 866 if (session->direction == RECV_FILE) { 867 session->file_size = atol(value); 868 } 869 // If we are sending the file, we don't care what value the server wrote in here 870 } else if (!strncasecmp(option, kBlkSize, kBlkSizeLen)) { // RFC 2348 871 if (!(session->client_sent_opts.mask & BLOCKSIZE_OPTION)) { 872 xprintf("block size not requested\n"); 873 set_error(session, TFTP_ERR_CODE_BAD_OPTIONS, resp, resp_len, "no block size"); 874 return TFTP_ERR_INTERNAL; 875 } 876 // Valid values range between "8" and "65464" octets, inclusive 877 long val = atol(value); 878 if (val < 8 || val > 65464) { 879 xprintf("invalid block size\n"); 880 set_error(session, TFTP_ERR_CODE_BAD_OPTIONS, resp, resp_len, "invalid block size"); 881 return TFTP_ERR_INTERNAL; 882 } 883 // TODO(tkilbourn): with an MTU of 1500, shouldn't be more than 1428 884 session->block_size = val; 885 } else if (!strncasecmp(option, kTimeout, kTimeoutLen)) { // RFC 2349 886 if (!(session->client_sent_opts.mask & TIMEOUT_OPTION)) { 887 xprintf("timeout not requested\n"); 888 set_error(session, TFTP_ERR_CODE_BAD_OPTIONS, resp, resp_len, "no timeout"); 889 return TFTP_ERR_INTERNAL; 890 } 891 // Valid values range between "1" and "255" seconds inclusive. 892 long val = atol(value); 893 if (val < 1 || val > 255) { 894 xprintf("invalid timeout\n"); 895 set_error(session, TFTP_ERR_CODE_BAD_OPTIONS, resp, resp_len, "invalid timeout"); 896 return TFTP_ERR_INTERNAL; 897 } 898 session->timeout = val; 899 } else if (!strncasecmp(option, kWindowSize, kWindowSizeLen)) { // RFC 7440 900 if (!(session->client_sent_opts.mask & WINDOWSIZE_OPTION)) { 901 xprintf("window size not requested\n"); 902 set_error(session, TFTP_ERR_CODE_BAD_OPTIONS, resp, resp_len, "no window size"); 903 return TFTP_ERR_INTERNAL; 904 } 905 // The valid values range MUST be between 1 and 65535 blocks, inclusive. 906 long val = atol(value); 907 if (val < 1 || val > 65535) { 908 xprintf("invalid window size\n"); 909 set_error(session, TFTP_ERR_CODE_BAD_OPTIONS, resp, resp_len, 910 "invalid window size"); 911 return TFTP_ERR_INTERNAL; 912 } 913 session->window_size = val; 914 } else { 915 // Options which the server does not support should be omitted from the 916 // OACK; they should not cause an ERROR packet to be generated. 917 } 918 919 cur += offset; 920 left -= offset; 921 } 922 *timeout_ms = 1000 * session->timeout; 923 924 xprintf("Options negotiated\n"); 925 xprintf(" File Size : %zu\n", session->file_size); 926 xprintf(" Block Size : %d\n", session->block_size); 927 xprintf(" Timeout : %d\n", session->timeout); 928 xprintf(" Window Size: %d\n", session->window_size); 929 930 session->offset = 0; 931 session->block_number = 0; 932 session->window_index = 0; 933 934 if (session->direction == SEND_FILE) { 935 tftp_data_msg* resp_data = (void*)resp; 936 tftp_status ret = tx_data(session, resp_data, resp_len, cookie); 937 if (ret < 0) { 938 set_error(session, TFTP_ERR_CODE_UNDEF, resp, resp_len, "failure to transmit data"); 939 } 940 return ret; 941 } else { 942 if (!session->file_interface.open_write || 943 session->file_interface.open_write(session->filename, session->file_size, 944 cookie)) { 945 xprintf("Could not open file on write request\n"); 946 set_error(session, TFTP_ERR_CODE_UNDEF, resp, resp_len, "could not open file for writing"); 947 return TFTP_ERR_BAD_STATE; 948 } 949 tftp_prepare_ack(session, resp, resp_len); 950 return TFTP_NO_ERROR; 951 } 952} 953 954tftp_status tftp_process_msg(tftp_session* session, 955 void* incoming, 956 size_t inlen, 957 void* outgoing, 958 size_t* outlen, 959 uint32_t* timeout_ms, 960 void* cookie) { 961 tftp_msg* msg = incoming; 962 tftp_msg* resp = outgoing; 963 964 // Decode opcode 965 uint16_t opcode = ntohs(msg->opcode) & 0xff; 966 xprintf("handle_msg opcode=%u length=%d\n", opcode, (int)inlen); 967 968 // Set default timeout 969 *timeout_ms = 1000 * session->timeout; 970 971 // Reset timeout count 972 session->consecutive_timeouts = 0; 973 974 switch (opcode) { 975 case OPCODE_RRQ: 976 return tftp_handle_rrq(session, incoming, inlen, resp, outlen, timeout_ms, cookie); 977 case OPCODE_WRQ: 978 return tftp_handle_wrq(session, incoming, inlen, resp, outlen, timeout_ms, cookie); 979 case OPCODE_DATA: 980 return tftp_handle_data(session, incoming, inlen, resp, outlen, timeout_ms, cookie); 981 case OPCODE_ACK: 982 return tftp_handle_ack(session, incoming, inlen, resp, outlen, timeout_ms, cookie); 983 case OPCODE_ERROR: 984 return tftp_handle_error(session, incoming, inlen, resp, outlen, timeout_ms, cookie); 985 case OPCODE_OACK: 986 return tftp_handle_oack(session, incoming, inlen, resp, outlen, timeout_ms, cookie); 987 default: 988 xprintf("Unknown opcode\n"); 989 session->state = ERROR; 990 return TFTP_ERR_INTERNAL; 991 } 992} 993 994tftp_status tftp_prepare_data(tftp_session* session, 995 void* outgoing, 996 size_t* outlen, 997 uint32_t* timeout_ms, 998 void* cookie) { 999 tftp_data_msg* resp_data = outgoing; 1000 1001 if ((session->block_number + session->window_index) * session->block_size > session->file_size) { 1002 *outlen = 0; 1003 return TFTP_TRANSFER_COMPLETED; 1004 } 1005 1006 tftp_status ret = tx_data(session, resp_data, outlen, cookie); 1007 if (ret < 0) { 1008 set_error(session, TFTP_ERR_CODE_UNDEF, outgoing, outlen, "failure to transmit data"); 1009 } 1010 return ret; 1011} 1012 1013void tftp_session_set_max_timeouts(tftp_session* session, 1014 uint16_t max_timeouts) { 1015 session->max_timeouts = max_timeouts; 1016} 1017 1018void tftp_session_set_opcode_prefix_use(tftp_session* session, 1019 bool enable) { 1020 session->use_opcode_prefix = enable; 1021} 1022 1023tftp_status tftp_timeout(tftp_session* session, 1024 void* msg_buf, 1025 size_t* msg_len, 1026 size_t buf_sz, 1027 uint32_t* timeout_ms, 1028 void* file_cookie) { 1029 xprintf("Timeout\n"); 1030 if (++session->consecutive_timeouts > session->max_timeouts) { 1031 return TFTP_ERR_TIMED_OUT; 1032 } 1033 // It's possible our previous transmission was dropped because of checksum errors. 1034 // Use a different opcode prefix when we resend. 1035 if (session->use_opcode_prefix) { 1036 session->opcode_prefix++; 1037 } 1038 if (session->state == REQ_SENT || session->state == REQ_RECEIVED) { 1039 // Resend previous message 1040 return TFTP_NO_ERROR; 1041 } 1042 *msg_len = buf_sz; 1043 if (session->direction == SEND_FILE) { 1044 // Reset back to the last-acknowledged block 1045 session->window_index = 0; 1046 return tftp_prepare_data(session, msg_buf, msg_len, timeout_ms, file_cookie); 1047 } else { 1048 // ACK up to the last block read 1049 tftp_prepare_ack(session, msg_buf, msg_len); 1050 return TFTP_NO_ERROR; 1051 } 1052} 1053 1054#define REPORT_ERR(opts,...) \ 1055 if (opts->err_msg) { \ 1056 snprintf(opts->err_msg, opts->err_msg_sz, __VA_ARGS__); \ 1057 } 1058 1059typedef struct { 1060 void* incoming; 1061 size_t in_buf_sz; 1062 void* outgoing; 1063 size_t out_buf_sz; 1064 char* err_msg; 1065 size_t err_msg_sz; 1066} tftp_msg_loop_opts; 1067 1068static tftp_status tftp_msg_loop(tftp_session* session, 1069 void* transport_cookie, 1070 void* file_cookie, 1071 tftp_msg_loop_opts* opts, 1072 uint32_t timeout_ms) { 1073 tftp_status ret; 1074 size_t out_sz = 0; 1075 1076 do { 1077 tftp_status send_status; 1078 int result = session->transport_interface.timeout_set(timeout_ms, transport_cookie); 1079 if (result < 0) { 1080 REPORT_ERR(opts, "failed during transport timeout set callback"); 1081 return TFTP_ERR_INTERNAL; 1082 } 1083 1084 bool pending = tftp_session_has_pending(session); 1085 int n = session->transport_interface.recv(opts->incoming, opts->in_buf_sz, !pending, 1086 transport_cookie); 1087 if (n == TFTP_ERR_TIMED_OUT) { 1088 if (pending) { 1089 out_sz = opts->out_buf_sz; 1090 ret = tftp_prepare_data(session, 1091 opts->outgoing, 1092 &out_sz, 1093 &timeout_ms, 1094 file_cookie); 1095 if (out_sz) { 1096 send_status = session->transport_interface.send(opts->outgoing, out_sz, 1097 transport_cookie); 1098 if (send_status != TFTP_NO_ERROR) { 1099 REPORT_ERR(opts, "failed during transport send callback"); 1100 return send_status; 1101 } 1102 } 1103 if (ret < 0) { 1104 REPORT_ERR(opts, "failed to prepare data to send"); 1105 return ret; 1106 } 1107 } else if (session->state != NONE) { 1108 ret = tftp_timeout(session, 1109 opts->outgoing, 1110 &out_sz, 1111 opts->out_buf_sz, 1112 &timeout_ms, 1113 file_cookie); 1114 if (ret == TFTP_ERR_TIMED_OUT) { 1115 REPORT_ERR(opts, "too many consecutive timeouts, aborting"); 1116 return ret; 1117 } 1118 if (ret < 0) { 1119 REPORT_ERR(opts, "failed during timeout processing"); 1120 return ret; 1121 } 1122 if (out_sz) { 1123 send_status = session->transport_interface.send(opts->outgoing, out_sz, 1124 transport_cookie); 1125 if (send_status != TFTP_NO_ERROR) { 1126 REPORT_ERR(opts, "failed during transport send callback"); 1127 return n; 1128 } 1129 } 1130 } 1131 continue; 1132 } else if (n < 0) { 1133 REPORT_ERR(opts, "failed during transport recv callback"); 1134 return n; 1135 } 1136 1137 out_sz = opts->out_buf_sz; 1138 ret = tftp_process_msg(session, 1139 opts->incoming, 1140 n, 1141 opts->outgoing, 1142 &out_sz, 1143 &timeout_ms, 1144 file_cookie); 1145 if (out_sz) { 1146 send_status = session->transport_interface.send(opts->outgoing, out_sz, 1147 transport_cookie); 1148 if (send_status != TFTP_NO_ERROR) { 1149 REPORT_ERR(opts, "failed during transport send callback"); 1150 return send_status; 1151 } 1152 } 1153 if (ret < 0) { 1154 REPORT_ERR(opts, "failed to handle message"); 1155 return ret; 1156 } else if (ret == TFTP_TRANSFER_COMPLETED) { 1157 return ret; 1158 } 1159 } while (1); 1160} 1161 1162static tftp_status transfer_file(tftp_session* session, 1163 void* transport_cookie, 1164 void* file_cookie, 1165 tftp_file_direction xfer_direction, 1166 const char* local_filename, 1167 const char* remote_filename, 1168 tftp_request_opts* opts) { 1169 if (!opts || !opts->inbuf || !opts->inbuf_sz || !opts->outbuf || !opts->outbuf_sz) { 1170 return TFTP_ERR_INVALID_ARGS; 1171 } 1172 1173 tftp_status status; 1174 1175 ssize_t file_size = 0; 1176 if (xfer_direction == SEND_FILE) { 1177 file_size = session->file_interface.open_read(local_filename, file_cookie); 1178 if (file_size < 0) { 1179 REPORT_ERR(opts, "failed during file open callback"); 1180 return file_size; 1181 } 1182 } 1183 1184 tftp_mode mode = opts->mode ? *opts->mode : TFTP_DEFAULT_CLIENT_MODE; 1185 1186 size_t out_sz = opts->outbuf_sz; 1187 uint32_t timeout_ms; 1188 status = tftp_generate_request(session, 1189 xfer_direction, 1190 local_filename, 1191 remote_filename, 1192 mode, 1193 file_size, 1194 opts->block_size, 1195 opts->timeout, 1196 opts->window_size, 1197 opts->outbuf, 1198 &out_sz, 1199 &timeout_ms); 1200 1201 const char* xfer_direction_str = (xfer_direction == SEND_FILE) ? "write" : "read"; 1202 if (status < 0) { 1203 REPORT_ERR(opts, "failed to generate %s request", xfer_direction_str); 1204 goto done; 1205 } 1206 if (!out_sz) { 1207 REPORT_ERR(opts, "no %s request generated", xfer_direction_str); 1208 status = TFTP_ERR_INTERNAL; 1209 goto done; 1210 } 1211 1212 status = session->transport_interface.send(opts->outbuf, out_sz, transport_cookie); 1213 if (status != TFTP_NO_ERROR) { 1214 REPORT_ERR(opts, "failed during transport send callback"); 1215 goto done; 1216 } 1217 1218 tftp_msg_loop_opts msg_loop_opts = {.incoming = opts->inbuf, 1219 .in_buf_sz = opts->inbuf_sz, 1220 .outgoing = opts->outbuf, 1221 .out_buf_sz = opts->outbuf_sz, 1222 .err_msg = opts->err_msg, 1223 .err_msg_sz = opts->err_msg_sz}; 1224 status = tftp_msg_loop(session, transport_cookie, file_cookie, &msg_loop_opts, timeout_ms); 1225 1226done: 1227 if ((xfer_direction == SEND_FILE) || (session->state != NONE)) { 1228 if (session->file_interface.close) { 1229 session->file_interface.close(file_cookie); 1230 } 1231 } 1232 return status; 1233} 1234 1235tftp_status tftp_push_file(tftp_session* session, 1236 void* transport_cookie, 1237 void* file_cookie, 1238 const char* local_filename, 1239 const char* remote_filename, 1240 tftp_request_opts* opts) { 1241 return transfer_file(session, transport_cookie, file_cookie, SEND_FILE, 1242 local_filename, remote_filename, opts); 1243} 1244 1245tftp_status tftp_pull_file(tftp_session* session, 1246 void* transport_cookie, 1247 void* file_cookie, 1248 const char* local_filename, 1249 const char* remote_filename, 1250 tftp_request_opts* opts) { 1251 return transfer_file(session, transport_cookie, file_cookie, RECV_FILE, 1252 local_filename, remote_filename, opts); 1253} 1254 1255tftp_status tftp_service_request(tftp_session* session, 1256 void* transport_cookie, 1257 void* file_cookie, 1258 tftp_handler_opts* opts) { 1259 if (!opts || !opts->inbuf || !opts->outbuf || !opts->outbuf_sz) { 1260 return TFTP_ERR_INVALID_ARGS; 1261 } 1262 tftp_msg_loop_opts msg_loop_opts = {.incoming = opts->inbuf, 1263 .in_buf_sz = opts->inbuf_sz, 1264 .outgoing = opts->outbuf, 1265 .out_buf_sz = *opts->outbuf_sz, 1266 .err_msg = opts->err_msg, 1267 .err_msg_sz = opts->err_msg_sz}; 1268 uint32_t timeout_ms = session->timeout * 1000; 1269 tftp_status status = tftp_msg_loop(session, transport_cookie, file_cookie, &msg_loop_opts, 1270 timeout_ms); 1271 if ((session->state != NONE) && session->file_interface.close) { 1272 session->file_interface.close(file_cookie); 1273 } 1274 return status; 1275} 1276 1277tftp_status tftp_handle_msg(tftp_session* session, 1278 void* transport_cookie, 1279 void* file_cookie, 1280 tftp_handler_opts* opts) { 1281 if (!opts || !opts->inbuf || !opts->outbuf || !opts->outbuf_sz) { 1282 return TFTP_ERR_INVALID_ARGS; 1283 } 1284 uint32_t timeout_ms; 1285 tftp_status ret; 1286 ret = tftp_process_msg(session, opts->inbuf, opts->inbuf_sz, 1287 opts->outbuf, opts->outbuf_sz, &timeout_ms, file_cookie); 1288 if (*opts->outbuf_sz) { 1289 tftp_status send_status = session->transport_interface.send(opts->outbuf, *opts->outbuf_sz, 1290 transport_cookie); 1291 if (send_status != TFTP_NO_ERROR) { 1292 REPORT_ERR(opts, "failed during transport send callback"); 1293 return send_status; 1294 } 1295 } 1296 if (ret == TFTP_ERR_SHOULD_WAIT) { 1297 REPORT_ERR(opts, "request received, host is busy"); 1298 } else if (ret < 0) { 1299 REPORT_ERR(opts, "handling tftp request failed (file might not exist)"); 1300 } else if (ret == TFTP_TRANSFER_COMPLETED) { 1301 if (session->file_interface.close) { 1302 session->file_interface.close(file_cookie); 1303 } 1304 } else { 1305 ret = session->transport_interface.timeout_set(timeout_ms, transport_cookie); 1306 if (ret < 0) { 1307 REPORT_ERR(opts, "failed during transport timeout set callback"); 1308 } 1309 } 1310 return ret; 1311} 1312