1 /* tftp.c - TFTP client. 2 * 3 * Copyright 2012 Madhur Verma <mad.flexi (at) gmail.com> 4 * Copyright 2015 Sameer Prakash Pradhan <sameer.p.pradhan (at) gmail.com> 5 * 6 * No Standard. 7 8 USE_TFTP(NEWTOY(tftp, "<1b#<8>65464r:l:g|p|[!gp]", TOYFLAG_USR|TOYFLAG_BIN)) 9 10 config TFTP 11 bool "tftp" 12 default n 13 help 14 usage: tftp [OPTIONS] HOST [PORT] 15 16 Transfer file from/to tftp server. 17 18 -l FILE Local FILE 19 -r FILE Remote FILE 20 -g Get file 21 -p Put file 22 -b SIZE Transfer blocks of SIZE octets(8 <= SIZE <= 65464) 23 */ 24 #define FOR_tftp 25 #include "toys.h" 26 27 GLOBALS( 28 char *local_file; 29 char *remote_file; 30 long block_size; 31 32 struct sockaddr_storage inaddr; 33 int af; 34 ) 35 36 #define TFTP_BLKSIZE 512 37 #define TFTP_RETRIES 3 38 #define TFTP_DATAHEADERSIZE 4 39 #define TFTP_MAXPACKETSIZE (TFTP_DATAHEADERSIZE + TFTP_BLKSIZE) 40 #define TFTP_PACKETSIZE TFTP_MAXPACKETSIZE 41 #define TFTP_DATASIZE (TFTP_PACKETSIZE-TFTP_DATAHEADERSIZE) 42 #define TFTP_IOBUFSIZE (TFTP_PACKETSIZE+8) 43 44 #define TFTP_OP_RRQ 1 /* Read Request RFC 1350, RFC 2090 */ 45 #define TFTP_OP_WRQ 2 /* Write Request RFC 1350 */ 46 #define TFTP_OP_DATA 3 /* Data chunk RFC 1350 */ 47 #define TFTP_OP_ACK 4 /* Acknowledgement RFC 1350 */ 48 #define TFTP_OP_ERR 5 /* Error Message RFC 1350 */ 49 #define TFTP_OP_OACK 6 /* Option acknowledgment RFC 2347 */ 50 51 #define TFTP_ER_ILLEGALOP 4 /* Illegal TFTP operation */ 52 #define TFTP_ER_UNKID 5 /* Unknown transfer ID */ 53 54 #define TFTP_ES_NOSUCHFILE "File not found" 55 #define TFTP_ES_ACCESS "Access violation" 56 #define TFTP_ES_FULL "Disk full or allocation exceeded" 57 #define TFTP_ES_ILLEGALOP "Illegal TFTP operation" 58 #define TFTP_ES_UNKID "Unknown transfer ID" 59 #define TFTP_ES_EXISTS "File already exists" 60 #define TFTP_ES_UNKUSER "No such user" 61 #define TFTP_ES_NEGOTIATE "Terminate transfer due to option negotiation" 62 63 // Initializes SERVER with ADDR and returns socket. 64 static int init_tftp(struct sockaddr_storage *server) 65 { 66 struct timeval to = { .tv_sec = 10, //Time out 67 .tv_usec = 0 }; 68 const int set = 1; 69 int port = 69, sd = xsocket(TT.af, SOCK_DGRAM, IPPROTO_UDP); 70 71 xsetsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (void *)&to, sizeof(struct timeval)); 72 xsetsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (void *)&set, sizeof(set)); 73 74 if(toys.optc == 2) port = atolx_range(toys.optargs[1], 1, 65535); 75 memset(server, 0, sizeof(struct sockaddr_storage)); 76 if (TT.af == AF_INET6) { 77 ((struct sockaddr_in6 *)server)->sin6_family = AF_INET6; 78 ((struct sockaddr_in6 *)server)->sin6_addr = 79 ((struct sockaddr_in6 *)&TT.inaddr)->sin6_addr; 80 ((struct sockaddr_in6 *)server)->sin6_port = htons(port); 81 } 82 else { 83 ((struct sockaddr_in *)server)->sin_family = AF_INET; 84 ((struct sockaddr_in *)server)->sin_addr.s_addr = 85 ((struct sockaddr_in *)&TT.inaddr)->sin_addr.s_addr; 86 ((struct sockaddr_in *)server)->sin_port = htons(port); 87 } 88 return sd; 89 } 90 91 /* 92 * Makes a request packet in BUFFER with OPCODE and file PATH of MODE 93 * and returns length of packet. 94 */ 95 static int mkpkt_request(uint8_t *buffer, int opcode, char *path, int mode) 96 { 97 buffer[0] = opcode >> 8; 98 buffer[1] = opcode & 0xff; 99 if(strlen(path) > TFTP_BLKSIZE) error_exit("path too long"); 100 return sprintf((char*) &buffer[2], "%s%c%s", path, 0, 101 (mode ? "octet" : "netascii")) + 3; 102 } 103 104 /* 105 * Makes an acknowledgement packet in BUFFER of BLOCNO 106 * and returns packet length. 107 */ 108 static int mkpkt_ack(uint8_t *buffer, uint16_t blockno) 109 { 110 buffer[0] = TFTP_OP_ACK >> 8; 111 buffer[1] = TFTP_OP_ACK & 0xff; 112 buffer[2] = blockno >> 8; 113 buffer[3] = blockno & 0xff; 114 return 4; 115 } 116 117 /* 118 * Makes an error packet in BUFFER with ERRORCODE and ERRORMSG. 119 * and returns packet length. 120 */ 121 static int mkpkt_err(uint8_t *buffer, uint16_t errorcode, char *errormsg) 122 { 123 buffer[0] = TFTP_OP_ERR >> 8; 124 buffer[1] = TFTP_OP_ERR & 0xff; 125 buffer[2] = errorcode >> 8; 126 buffer[3] = errorcode & 0xff; 127 strcpy((char*) &buffer[4], errormsg); 128 return strlen(errormsg) + 5; 129 } 130 131 /* 132 * Recieves data from server in BUFF with socket SD and updates FROM 133 * and returns read length. 134 */ 135 static ssize_t read_server(int sd, void *buf, size_t len, 136 struct sockaddr_storage *from) 137 { 138 socklen_t alen; 139 ssize_t nb; 140 141 for (;;) { 142 memset(buf, 0, len); 143 alen = sizeof(struct sockaddr_storage); 144 nb = recvfrom(sd, buf, len, 0, (struct sockaddr *) from, &alen); 145 if (nb < 0) { 146 if (errno == EAGAIN) { 147 perror_msg("server read timed out"); 148 return nb; 149 }else if (errno != EINTR) { 150 perror_msg("server read failed"); 151 return nb; 152 } 153 }else return nb; 154 } 155 return nb; 156 } 157 158 /* 159 * sends data to server TO from BUFF of length LEN through socket SD 160 * and returns successfully send bytes number. 161 */ 162 static ssize_t write_server(int sd, void *buf, size_t len, 163 struct sockaddr_storage *to) 164 { 165 ssize_t nb; 166 167 for (;;) { 168 nb = sendto(sd, buf, len, 0, (struct sockaddr *)to, 169 sizeof(struct sockaddr_storage)); 170 if (nb < 0) { 171 if (errno != EINTR) { 172 perror_msg("server write failed"); 173 return nb; 174 } 175 } else return nb; 176 } 177 return nb; 178 } 179 180 // checks packet for data and updates block no 181 static inline int check_data( uint8_t *packet, uint16_t *opcode, 182 uint16_t *blockno) 183 { 184 *opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1]; 185 if (*opcode == TFTP_OP_DATA) { 186 *blockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3]; 187 return 0; 188 } 189 return -1; 190 } 191 192 // Makes data packet through FD from file OFFSET in buffer PACKET of BLOCKNO 193 static int mkpkt_data(int fd, off_t offset, uint8_t *packet, uint16_t blockno) 194 { 195 off_t tmp; 196 int nbytesread; 197 198 packet[0] = TFTP_OP_DATA >> 8; 199 packet[1] = TFTP_OP_DATA & 0xff; 200 packet[2] = blockno >> 8; 201 packet[3] = blockno & 0xff; 202 tmp = lseek(fd, offset, SEEK_SET); 203 if (tmp == (off_t) -1) { 204 perror_msg("lseek failed"); 205 return -1; 206 } 207 nbytesread = readall(fd, &packet[TFTP_DATAHEADERSIZE], TFTP_DATASIZE); 208 if (nbytesread < 0) return -1; 209 return nbytesread + TFTP_DATAHEADERSIZE; 210 } 211 212 // Receives ACK responses from server and updates blockno 213 static int read_ack(int sd, uint8_t *packet, struct sockaddr_storage *server, 214 uint16_t *port, uint16_t *blockno) 215 { 216 struct sockaddr_storage from; 217 ssize_t nbytes; 218 uint16_t opcode, rblockno; 219 int packetlen, retry; 220 221 for (retry = 0; retry < TFTP_RETRIES; retry++) { 222 for (;;) { 223 nbytes = read_server(sd, packet, TFTP_IOBUFSIZE, &from); 224 if (nbytes < 4) { // Ack headersize = 4 225 if (nbytes == 0) error_msg("Connection lost."); 226 else if (nbytes > 0) error_msg("Short packet: %d bytes", nbytes); 227 else error_msg("Server read ACK failure."); 228 break; 229 } else { 230 if (!*port) { 231 *port = ((struct sockaddr_in *)&from)->sin_port; 232 ((struct sockaddr_in *)server)->sin_port = 233 ((struct sockaddr_in *)&from)->sin_port; 234 } 235 if (((struct sockaddr_in *)server)->sin_addr.s_addr != 236 ((struct sockaddr_in *)&from)->sin_addr.s_addr) { 237 error_msg("Invalid address in DATA."); 238 continue; 239 } 240 if (*port != ((struct sockaddr_in *)server)->sin_port) { 241 error_msg("Invalid port in DATA."); 242 packetlen = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID); 243 (void) write_server(sd, packet, packetlen, server); 244 continue; 245 } 246 opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1]; 247 rblockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3]; 248 249 if (opcode != TFTP_OP_ACK) { 250 error_msg("Bad opcode."); 251 if (opcode > 5) { 252 packetlen = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP); 253 (void) write_server(sd, packet, packetlen, server); 254 } 255 break; 256 } 257 if (blockno) *blockno = rblockno; 258 return 0; 259 } 260 } 261 } 262 error_msg("Timeout, Waiting for ACK."); 263 return -1; 264 } 265 266 // receives file from server. 267 static int file_get(void) 268 { 269 struct sockaddr_storage server, from; 270 uint8_t *packet; 271 uint16_t blockno = 0, opcode, rblockno = 0; 272 int len, sd, fd, retry, nbytesrecvd = 0, ndatabytes, ret, result = -1; 273 274 sd = init_tftp(&server); 275 276 packet = (uint8_t*) xzalloc(TFTP_IOBUFSIZE); 277 fd = xcreate(TT.local_file, O_WRONLY | O_CREAT | O_TRUNC, 0666); 278 279 len = mkpkt_request(packet, TFTP_OP_RRQ, TT.remote_file, 1); 280 ret = write_server(sd, packet, len, &server); 281 if (ret != len){ 282 unlink(TT.local_file); 283 goto errout_with_sd; 284 } 285 if (TT.af == AF_INET6) ((struct sockaddr_in6 *)&server)->sin6_port = 0; 286 else ((struct sockaddr_in *)&server)->sin_port = 0; 287 288 do { 289 blockno++; 290 for (retry = 0 ; retry < TFTP_RETRIES; retry++) { 291 nbytesrecvd = read_server(sd, packet, TFTP_IOBUFSIZE, &from); 292 if (nbytesrecvd > 0) { 293 if ( ((TT.af == AF_INET) && 294 memcmp(&((struct sockaddr_in *)&server)->sin_addr, 295 &((struct sockaddr_in *)&from)->sin_addr, 296 sizeof(struct in_addr))) || 297 ((TT.af == AF_INET6) && 298 memcmp(&((struct sockaddr_in6 *)&server)->sin6_addr, 299 &((struct sockaddr_in6 *)&from)->sin6_addr, 300 sizeof(struct in6_addr)))) { 301 error_msg("Invalid address in DATA."); 302 retry--; 303 continue; 304 } 305 if ( ((TT.af == AF_INET) && ((struct sockaddr_in *)&server)->sin_port 306 && (((struct sockaddr_in *)&server)->sin_port != 307 ((struct sockaddr_in *)&from)->sin_port)) || 308 ((TT.af == AF_INET6) && ((struct sockaddr_in6 *)&server)->sin6_port 309 && (((struct sockaddr_in6 *)&server)->sin6_port != 310 ((struct sockaddr_in6 *)&from)->sin6_port))) { 311 error_msg("Invalid port in DATA."); 312 len = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID); 313 ret = write_server(sd, packet, len, &from); 314 retry--; 315 continue; 316 } 317 if (nbytesrecvd < TFTP_DATAHEADERSIZE) { 318 error_msg("Tiny data packet ignored."); 319 continue; 320 } 321 if (check_data(packet, &opcode, &rblockno) != 0 322 || blockno != rblockno) { 323 324 if (opcode == TFTP_OP_ERR) { 325 char *message = "DATA Check failure."; 326 char *arr[] = {TFTP_ES_NOSUCHFILE, TFTP_ES_ACCESS, 327 TFTP_ES_FULL, TFTP_ES_ILLEGALOP, 328 TFTP_ES_UNKID, TFTP_ES_EXISTS, 329 TFTP_ES_UNKUSER, TFTP_ES_NEGOTIATE}; 330 if (rblockno && (rblockno < 9)) message = arr[rblockno - 1]; 331 error_msg_raw(message); 332 } 333 else if (blockno == 1 && opcode == TFTP_OP_OACK) { 334 len = mkpkt_ack(packet, 0); 335 ret = write_server(sd, packet, len, &from); 336 if (ret != len){ 337 unlink(TT.local_file); 338 goto errout_with_sd; 339 } 340 } 341 else if (opcode > 5) { 342 len = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP); 343 ret = write_server(sd, packet, len, &from); 344 } 345 continue; 346 } 347 if ((TT.af == AF_INET6) && !((struct sockaddr_in6 *)&server)->sin6_port) 348 ((struct sockaddr_in6 *)&server)->sin6_port = 349 ((struct sockaddr_in6 *)&from)->sin6_port; 350 else if ((TT.af == AF_INET) && !((struct sockaddr_in *)&server)->sin_port) 351 ((struct sockaddr_in *)&server)->sin_port = 352 ((struct sockaddr_in *)&from)->sin_port; 353 break; 354 } 355 } 356 if (retry == TFTP_RETRIES) { 357 error_msg("Retry limit exceeded."); 358 unlink(TT.local_file); 359 goto errout_with_sd; 360 } 361 ndatabytes = nbytesrecvd - TFTP_DATAHEADERSIZE; 362 if (writeall(fd, packet + TFTP_DATAHEADERSIZE, ndatabytes) < 0){ 363 unlink(TT.local_file); 364 goto errout_with_sd; 365 } 366 len = mkpkt_ack(packet, blockno); 367 ret = write_server(sd, packet, len, &server); 368 if (ret != len){ 369 unlink(TT.local_file); 370 goto errout_with_sd; 371 } 372 } while (ndatabytes >= TFTP_DATASIZE); 373 374 result = 0; 375 376 errout_with_sd: xclose(sd); 377 free(packet); 378 return result; 379 } 380 381 // Sends file to server. 382 int file_put(void) 383 { 384 struct sockaddr_storage server; 385 uint8_t *packet; 386 off_t offset = 0; 387 uint16_t blockno = 1, rblockno, port = 0; 388 int packetlen, sd, fd, retry = 0, ret, result = -1; 389 390 sd = init_tftp(&server); 391 packet = (uint8_t*)xzalloc(TFTP_IOBUFSIZE); 392 fd = xopenro(TT.local_file); 393 394 for (;;) { //first loop for request send and confirmation from server. 395 packetlen = mkpkt_request(packet, TFTP_OP_WRQ, TT.remote_file, 1); 396 ret = write_server(sd, packet, packetlen, &server); 397 if (ret != packetlen) goto errout_with_sd; 398 if (read_ack(sd, packet, &server, &port, NULL) == 0) break; 399 if (++retry > TFTP_RETRIES) { 400 error_msg("Retry count exceeded."); 401 goto errout_with_sd; 402 } 403 } 404 for (;;) { // loop for data sending and receving ack from server. 405 packetlen = mkpkt_data(fd, offset, packet, blockno); 406 if (packetlen < 0) goto errout_with_sd; 407 408 ret = write_server(sd, packet, packetlen, &server); 409 if (ret != packetlen) goto errout_with_sd; 410 411 if (read_ack(sd, packet, &server, &port, &rblockno) == 0) { 412 if (rblockno == blockno) { 413 if (packetlen < TFTP_PACKETSIZE) break; 414 blockno++; 415 offset += TFTP_DATASIZE; 416 retry = 0; 417 continue; 418 } 419 } 420 if (++retry > TFTP_RETRIES) { 421 error_msg("Retry count exceeded."); 422 goto errout_with_sd; 423 } 424 } 425 result = 0; 426 427 errout_with_sd: close(sd); 428 free(packet); 429 return result; 430 } 431 432 void tftp_main(void) 433 { 434 struct addrinfo *info, rp, *res=0; 435 int ret; 436 437 if (toys.optflags & FLAG_r) { 438 if (!(toys.optflags & FLAG_l)) { 439 char *slash = strrchr(TT.remote_file, '/'); 440 TT.local_file = (slash) ? slash + 1 : TT.remote_file; 441 } 442 } else if (toys.optflags & FLAG_l) TT.remote_file = TT.local_file; 443 else error_exit("Please provide some files."); 444 445 memset(&rp, 0, sizeof(rp)); 446 rp.ai_family = AF_UNSPEC; 447 rp.ai_socktype = SOCK_STREAM; 448 ret = getaddrinfo(toys.optargs[0], toys.optargs[1], &rp, &info); 449 if (!ret) { 450 for (res = info; res; res = res->ai_next) 451 if ( (res->ai_family == AF_INET) || (res->ai_family == AF_INET6)) break; 452 } 453 if (!res) 454 error_exit("bad address '%s' : %s", toys.optargs[0], gai_strerror(ret)); 455 TT.af = info->ai_family; 456 457 memcpy((void *)&TT.inaddr, info->ai_addr, info->ai_addrlen); 458 freeaddrinfo(info); 459 460 if (toys.optflags & FLAG_g) file_get(); 461 if (toys.optflags & FLAG_p) file_put(); 462 } 463