1/* ------------------------------------------------------------------------- */ 2/* tftp.c */ 3/* */ 4/* A simple tftp client for busybox. */ 5/* Tries to follow RFC1350. */ 6/* Only "octet" mode and 512-byte data blocks are supported. */ 7/* */ 8/* Copyright (C) 2001 Magnus Damm <damm@opensource.se> */ 9/* */ 10/* Parts of the code based on: */ 11/* */ 12/* atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca> */ 13/* and Remi Lefebvre <remi@debian.org> */ 14/* */ 15/* utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> */ 16/* */ 17/* This program is free software; you can redistribute it and/or modify */ 18/* it under the terms of the GNU General Public License as published by */ 19/* the Free Software Foundation; either version 2 of the License, or */ 20/* (at your option) any later version. */ 21/* */ 22/* This program is distributed in the hope that it will be useful, */ 23/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ 24/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ 25/* General Public License for more details. */ 26/* */ 27/* You should have received a copy of the GNU General Public License */ 28/* along with this program; if not, write to the Free Software */ 29/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 30/* */ 31/* ------------------------------------------------------------------------- */ 32 33#include <stdio.h> 34#include <stdlib.h> 35#include <string.h> 36#include <sys/types.h> 37#include <sys/socket.h> 38#include <sys/time.h> 39#include <sys/stat.h> 40#include <netdb.h> 41#include <netinet/in.h> 42#include <arpa/inet.h> 43#include <unistd.h> 44#include <fcntl.h> 45 46#include "busybox.h" 47 48//#define BB_FEATURE_TFTP_DEBUG 49 50/* we don't need #ifdefs with these constants and optimization... */ 51 52#ifdef BB_FEATURE_TFTP_GET 53#define BB_TFTP_GET (1 << 0) 54#else 55#define BB_TFTP_GET 0 56#endif 57 58#ifdef BB_FEATURE_TFTP_PUT 59#define BB_TFTP_PUT (1 << 1) 60#else 61#define BB_TFTP_PUT 0 62#endif 63 64#ifdef BB_FEATURE_TFTP_DEBUG 65#define BB_TFTP_DEBUG 1 66#else 67#define BB_TFTP_DEBUG 0 68#endif 69 70#define BB_TFTP_NO_RETRIES 5 71#define BB_TFTP_TIMEOUT 5 /* seconds */ 72 73#define RRQ 1 /* read request */ 74#define WRQ 2 /* write request */ 75#define DATA 3 /* data packet */ 76#define ACK 4 /* acknowledgement */ 77#define ERROR 5 /* error code */ 78 79#define BUFSIZE (512+4) 80 81static const char *tftp_error_msg[] = { 82 "Undefined error", 83 "File not found", 84 "Access violation", 85 "Disk full or allocation error", 86 "Illegal TFTP operation", 87 "Unknown transfer ID", 88 "File already exists", 89 "No such user" 90}; 91 92static inline int tftp(int cmd, struct hostent *host, 93 char *serverfile, int localfd, int port) 94{ 95 struct sockaddr_in sa; 96 int socketfd; 97 struct timeval tv; 98 fd_set rfds; 99 struct sockaddr_in from; 100 socklen_t fromlen; 101 char *cp; 102 unsigned short tmp; 103 int len, opcode, finished; 104 int timeout, block_nr; 105 106 RESERVE_BB_BUFFER(buf, BUFSIZE); 107 108 opcode = finished = timeout = 0; 109 block_nr = 1; 110 111 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { 112 perror_msg("socket"); 113 return EXIT_FAILURE; 114 } 115 116 len = sizeof(sa); 117 118 memset(&sa, 0, len); 119 bind(socketfd, (struct sockaddr *)&sa, len); 120 121 sa.sin_family = host->h_addrtype; 122 sa.sin_port = htons(port); 123 memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr, 124 sizeof(sa.sin_addr)); 125 126 /* build opcode */ 127 128 if (cmd & BB_TFTP_GET) { 129 opcode = RRQ; 130 } 131 132 if (cmd & BB_TFTP_PUT) { 133 opcode = WRQ; 134 } 135 136 while (1) { 137 138 139 /* build packet of type "opcode" */ 140 141 142 cp = buf; 143 144 *((unsigned short *) cp) = htons(opcode); 145 146 cp += 2; 147 148 /* add filename and mode */ 149 150 if ((BB_TFTP_GET && (opcode == RRQ)) || 151 (BB_TFTP_PUT && (opcode == WRQ))) { 152 153 while (cp != &buf[BUFSIZE - 1]) { 154 if ((*cp = *serverfile++) == '\0') 155 break; 156 cp++; 157 } 158 159 if ((*cp != '\0') || (&buf[BUFSIZE - 1] - cp) < 7) { 160 error_msg("too long server-filename"); 161 break; 162 } 163 164 memcpy(cp + 1, "octet", 6); 165 cp += 7; 166 } 167 168 /* add ack and data */ 169 170 if ((BB_TFTP_GET && (opcode == ACK)) || 171 (BB_TFTP_PUT && (opcode == DATA))) { 172 173 *((unsigned short *) cp) = htons(block_nr); 174 175 cp += 2; 176 177 block_nr++; 178 179 if (BB_TFTP_PUT && (opcode == DATA)) { 180 len = read(localfd, cp, BUFSIZE - 4); 181 182 if (len < 0) { 183 perror_msg("read"); 184 break; 185 } 186 187 if (len != (BUFSIZE - 4)) { 188 finished++; 189 } 190 191 cp += len; 192 } else if (finished) { 193 break; 194 } 195 } 196 197 198 /* send packet */ 199 200 201 do { 202 203 len = cp - buf; 204 205 if (BB_TFTP_DEBUG) { 206 printf("sending %u bytes\n", len); 207 208 for (cp = buf; cp < &buf[len]; cp++) 209 printf("%02x ", *cp); 210 printf("\n"); 211 } 212 213 if (sendto(socketfd, buf, len, 0, 214 (struct sockaddr *) &sa, sizeof(sa)) < 0) { 215 perror_msg("send"); 216 len = -1; 217 break; 218 } 219 220 221 /* receive packet */ 222 223 224 memset(&from, 0, sizeof(from)); 225 fromlen = sizeof(from); 226 227 tv.tv_sec = BB_TFTP_TIMEOUT; 228 tv.tv_usec = 0; 229 230 FD_ZERO(&rfds); 231 FD_SET(socketfd, &rfds); 232 233 switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) { 234 case 1: 235 len = recvfrom(socketfd, buf, 236 BUFSIZE, 0, 237 (struct sockaddr *) &from, &fromlen); 238 239 if (len < 0) { 240 perror_msg("recvfrom"); 241 break; 242 } 243 244 timeout = 0; 245 246 if (sa.sin_port == htons(port)) { 247 sa.sin_port = from.sin_port; 248 break; 249 } 250 251 if (sa.sin_port == from.sin_port) { 252 break; 253 } 254 255 /* fall-through for bad packets! */ 256 /* discard the packet - treat as timeout */ 257 258 case 0: 259 error_msg("timeout"); 260 261 if (!timeout) { 262 timeout = BB_TFTP_NO_RETRIES; 263 } else { 264 timeout--; 265 } 266 267 if (!timeout) { 268 len = -1; 269 error_msg("last timeout"); 270 } 271 break; 272 273 default: 274 perror_msg("select"); 275 len = -1; 276 } 277 278 } while (timeout && (len >= 0)); 279 280 if (len < 0) { 281 break; 282 } 283 284 /* process received packet */ 285 286 287 opcode = ntohs(*((unsigned short *) buf)); 288 tmp = ntohs(*((unsigned short *) &buf[2])); 289 290 if (BB_TFTP_DEBUG) { 291 printf("received %d bytes: %04x %04x\n", len, opcode, tmp); 292 } 293 294 if (BB_TFTP_GET && (opcode == DATA)) { 295 296 if (tmp == block_nr) { 297 len = write(localfd, &buf[4], len - 4); 298 299 if (len < 0) { 300 perror_msg("write"); 301 break; 302 } 303 304 if (len != (BUFSIZE - 4)) { 305 finished++; 306 } 307 308 opcode = ACK; 309 continue; 310 } 311 } 312 313 if (BB_TFTP_PUT && (opcode == ACK)) { 314 315 if (tmp == (block_nr - 1)) { 316 if (finished) { 317 break; 318 } 319 320 opcode = DATA; 321 continue; 322 } 323 } 324 325 if (opcode == ERROR) { 326 char *msg = NULL; 327 328 if (buf[4] != '\0') { 329 msg = &buf[4]; 330 buf[BUFSIZE - 1] = '\0'; 331 } else if (tmp < (sizeof(tftp_error_msg) / sizeof(char *))) { 332 msg = (char *) tftp_error_msg[tmp]; 333 } 334 335 if (msg) { 336 error_msg("server says: %s", msg); 337 } 338 339 break; 340 } 341 } 342 343 close(socketfd); 344 345 return finished ? EXIT_SUCCESS : EXIT_FAILURE; 346} 347 348int tftp_main(int argc, char **argv) 349{ 350 char *cp, *s; 351 char *serverstr; 352 struct hostent *host; 353 char *serverfile; 354 char *localfile; 355 int cmd, flags, fd, bad; 356 357 host = (void *) serverstr = serverfile = localfile = NULL; 358 flags = cmd = 0; 359 bad = 1; 360 361 if (argc > 3) { 362 if (BB_TFTP_GET && (strcmp(argv[1], "get") == 0)) { 363 cmd = BB_TFTP_GET; 364 flags = O_WRONLY | O_CREAT; 365 serverstr = argv[2]; 366 localfile = argv[3]; 367 } 368 369 if (BB_TFTP_PUT && (strcmp(argv[1], "put") == 0)) { 370 cmd = BB_TFTP_PUT; 371 flags = O_RDONLY; 372 localfile = argv[2]; 373 serverstr = argv[3]; 374 } 375 376 } 377 378 if (!(cmd & (BB_TFTP_GET | BB_TFTP_PUT))) { 379 show_usage(); 380 } 381 382 for (cp = serverstr; *cp != '\0'; cp++) 383 if (*cp == ':') 384 break; 385 386 if (*cp == ':') { 387 388 serverfile = cp + 1; 389 390 s = xstrdup(serverstr); 391 s[cp - serverstr] = '\0'; 392 393 host = xgethostbyname(s); 394 395 free(s); 396 } 397 398 if (BB_TFTP_DEBUG) { 399 printf("using server \"%s\", serverfile \"%s\"," 400 "localfile \"%s\".\n", 401 inet_ntoa(*((struct in_addr *) host->h_addr)), 402 serverfile, localfile); 403 } 404 405 if ((fd = open(localfile, flags, 0644)) < 0) { 406 perror_msg_and_die("local file"); 407 } 408 409 flags = tftp(cmd, host, serverfile, fd, 69); 410 411 close(fd); 412 413 return flags; 414} 415