1 /* $NetBSD: tftp.c,v 1.14 2000/11/21 14:58:21 itojun Exp $ */ 2 3 /* 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; 40 #else 41 __RCSID("$NetBSD: tftp.c,v 1.14 2000/11/21 14:58:21 itojun Exp $"); 42 #endif 43 #endif /* not lint */ 44 45 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 46 47 /* 48 * TFTP User Program -- Protocol Machines 49 */ 50 #include <sys/types.h> 51 #include <sys/socket.h> 52 #include <sys/time.h> 53 54 #include <netinet/in.h> 55 56 #include <arpa/tftp.h> 57 58 #include <err.h> 59 #include <errno.h> 60 #include <setjmp.h> 61 #include <signal.h> 62 #include <stdio.h> 63 #include <string.h> 64 #include <unistd.h> 65 66 #include "extern.h" 67 #include "tftpsubs.h" 68 69 extern struct sockaddr_storage peeraddr; /* filled in by main */ 70 extern int f; /* the opened socket */ 71 extern int trace; 72 extern int verbose; 73 extern int rexmtval; 74 extern int maxtimeout; 75 76 #define PKTSIZE SEGSIZE+4 77 char ackbuf[PKTSIZE]; 78 int timeout; 79 jmp_buf toplevel; 80 jmp_buf timeoutbuf; 81 82 static void nak __P((int, struct sockaddr *)); 83 static int makerequest __P((int, const char *, struct tftphdr *, const char *)); 84 static void printstats __P((const char *, unsigned long)); 85 static void startclock __P((void)); 86 static void stopclock __P((void)); 87 static void timer __P((int)); 88 static void tpacket __P((const char *, struct tftphdr *, int)); 89 90 /* 91 * Send the requested file. 92 */ 93 void 94 sendfile(fd, name, mode) 95 int fd; 96 char *name; 97 char *mode; 98 { 99 struct tftphdr *ap; /* data and ack packets */ 100 struct tftphdr *dp; 101 int n; 102 volatile unsigned int block; 103 volatile int size, convert; 104 volatile unsigned long amount; 105 struct sockaddr_storage from; 106 int fromlen; 107 FILE *file; 108 struct sockaddr_storage peer; 109 110 startclock(); /* start stat's clock */ 111 dp = r_init(); /* reset fillbuf/read-ahead code */ 112 ap = (struct tftphdr *)ackbuf; 113 file = fdopen(fd, "r"); 114 convert = !strcmp(mode, "netascii"); 115 block = 0; 116 amount = 0; 117 memcpy(&peer, &peeraddr, peeraddr.ss_len); 118 119 signal(SIGALRM, timer); 120 do { 121 if (block == 0) 122 size = makerequest(WRQ, name, dp, mode) - 4; 123 else { 124 /* size = read(fd, dp->th_data, SEGSIZE); */ 125 size = readit(file, &dp, convert); 126 if (size < 0) { 127 nak(errno + 100, (struct sockaddr *)&peer); 128 break; 129 } 130 dp->th_opcode = htons((u_short)DATA); 131 dp->th_block = htons((u_short)block); 132 } 133 timeout = 0; 134 (void) setjmp(timeoutbuf); 135 send_data: 136 if (trace) 137 tpacket("sent", dp, size + 4); 138 n = sendto(f, dp, size + 4, 0, 139 (struct sockaddr *)&peer, peer.ss_len); 140 if (n != size + 4) { 141 warn("sendto"); 142 goto abort; 143 } 144 read_ahead(file, convert); 145 for ( ; ; ) { 146 alarm(rexmtval); 147 do { 148 fromlen = sizeof(from); 149 n = recvfrom(f, ackbuf, sizeof(ackbuf), 0, 150 (struct sockaddr *)&from, &fromlen); 151 } while (n <= 0); 152 alarm(0); 153 if (n < 0) { 154 warn("recvfrom"); 155 goto abort; 156 } 157 switch (peer.ss_family) { /* added */ 158 case AF_INET: 159 ((struct sockaddr_in *)&peer)->sin_port = 160 ((struct sockaddr_in *)&from)->sin_port; 161 break; 162 case AF_INET6: 163 ((struct sockaddr_in6 *)&peer)->sin6_port = 164 ((struct sockaddr_in6 *)&from)->sin6_port; 165 break; 166 default: 167 /* unsupported */ 168 break; 169 } 170 if (trace) 171 tpacket("received", ap, n); 172 /* should verify packet came from server */ 173 ap->th_opcode = ntohs(ap->th_opcode); 174 ap->th_block = ntohs(ap->th_block); 175 if (ap->th_opcode == ERROR) { 176 printf("Error code %d: %s\n", ap->th_code, 177 ap->th_msg); 178 goto abort; 179 } 180 if (ap->th_opcode == ACK) { 181 int j; 182 183 if (ap->th_block == block) { 184 break; 185 } 186 /* On an error, try to synchronize 187 * both sides. 188 */ 189 j = synchnet(f); 190 if (j && trace) { 191 printf("discarded %d packets\n", 192 j); 193 } 194 if (ap->th_block == (block-1)) { 195 goto send_data; 196 } 197 } 198 } 199 if (block > 0) 200 amount += size; 201 block++; 202 } while (size == SEGSIZE || block == 1); 203 abort: 204 fclose(file); 205 stopclock(); 206 if (amount > 0) 207 printstats("Sent", amount); 208 } 209 210 /* 211 * Receive a file. 212 */ 213 void 214 recvfile(fd, name, mode) 215 int fd; 216 char *name; 217 char *mode; 218 { 219 struct tftphdr *ap; 220 struct tftphdr *dp; 221 int n; 222 volatile unsigned int block; 223 volatile int size, firsttrip; 224 volatile unsigned long amount; 225 struct sockaddr_storage from; 226 int fromlen; 227 FILE *file; 228 volatile int convert; /* true if converting crlf -> lf */ 229 struct sockaddr_storage peer; 230 231 startclock(); 232 dp = w_init(); 233 ap = (struct tftphdr *)ackbuf; 234 file = fdopen(fd, "w"); 235 convert = !strcmp(mode, "netascii"); 236 block = 1; 237 firsttrip = 1; 238 amount = 0; 239 memcpy(&peer, &peeraddr, peeraddr.ss_len); 240 241 signal(SIGALRM, timer); 242 do { 243 if (firsttrip) { 244 size = makerequest(RRQ, name, ap, mode); 245 firsttrip = 0; 246 } else { 247 ap->th_opcode = htons((u_short)ACK); 248 ap->th_block = htons((u_short)(block)); 249 size = 4; 250 block++; 251 } 252 timeout = 0; 253 (void) setjmp(timeoutbuf); 254 send_ack: 255 if (trace) 256 tpacket("sent", ap, size); 257 if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peer, 258 peer.ss_len) != size) { 259 alarm(0); 260 warn("sendto"); 261 goto abort; 262 } 263 write_behind(file, convert); 264 for ( ; ; ) { 265 alarm(rexmtval); 266 do { 267 fromlen = sizeof(from); 268 n = recvfrom(f, dp, PKTSIZE, 0, 269 (struct sockaddr *)&from, &fromlen); 270 } while (n <= 0); 271 alarm(0); 272 if (n < 0) { 273 warn("recvfrom"); 274 goto abort; 275 } 276 switch (peer.ss_family) { /* added */ 277 case AF_INET: 278 ((struct sockaddr_in *)&peer)->sin_port = 279 ((struct sockaddr_in *)&from)->sin_port; 280 break; 281 case AF_INET6: 282 ((struct sockaddr_in6 *)&peer)->sin6_port = 283 ((struct sockaddr_in6 *)&from)->sin6_port; 284 break; 285 default: 286 /* unsupported */ 287 break; 288 } 289 if (trace) 290 tpacket("received", dp, n); 291 /* should verify client address */ 292 dp->th_opcode = ntohs(dp->th_opcode); 293 dp->th_block = ntohs(dp->th_block); 294 if (dp->th_opcode == ERROR) { 295 printf("Error code %d: %s\n", dp->th_code, 296 dp->th_msg); 297 goto abort; 298 } 299 if (dp->th_opcode == DATA) { 300 int j; 301 302 if (dp->th_block == block) { 303 break; /* have next packet */ 304 } 305 /* On an error, try to synchronize 306 * both sides. 307 */ 308 j = synchnet(f); 309 if (j && trace) { 310 printf("discarded %d packets\n", j); 311 } 312 if (dp->th_block == (block-1)) { 313 goto send_ack; /* resend ack */ 314 } 315 } 316 } 317 /* size = write(fd, dp->th_data, n - 4); */ 318 size = writeit(file, &dp, n - 4, convert); 319 if (size < 0) { 320 nak(errno + 100, (struct sockaddr *)&peer); 321 break; 322 } 323 amount += size; 324 } while (size == SEGSIZE); 325 abort: /* ok to ack, since user */ 326 ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 327 ap->th_block = htons((u_short)block); 328 (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peer, 329 peer.ss_len); 330 write_behind(file, convert); /* flush last buffer */ 331 fclose(file); 332 stopclock(); 333 if (amount > 0) 334 printstats("Received", amount); 335 } 336 337 static int 338 makerequest(request, name, tp, mode) 339 int request; 340 const char *name; 341 struct tftphdr *tp; 342 const char *mode; 343 { 344 char *cp; 345 346 tp->th_opcode = htons((u_short)request); 347 #ifndef __SVR4 348 cp = tp->th_stuff; 349 #else 350 cp = (void *)&tp->th_stuff; 351 #endif 352 strcpy(cp, name); 353 cp += strlen(name); 354 *cp++ = '\0'; 355 strcpy(cp, mode); 356 cp += strlen(mode); 357 *cp++ = '\0'; 358 return (cp - (char *)tp); 359 } 360 361 const struct errmsg { 362 int e_code; 363 const char *e_msg; 364 } errmsgs[] = { 365 { EUNDEF, "Undefined error code" }, 366 { ENOTFOUND, "File not found" }, 367 { EACCESS, "Access violation" }, 368 { ENOSPACE, "Disk full or allocation exceeded" }, 369 { EBADOP, "Illegal TFTP operation" }, 370 { EBADID, "Unknown transfer ID" }, 371 { EEXISTS, "File already exists" }, 372 { ENOUSER, "No such user" }, 373 { -1, 0 } 374 }; 375 376 /* 377 * Send a nak packet (error message). 378 * Error code passed in is one of the 379 * standard TFTP codes, or a UNIX errno 380 * offset by 100. 381 */ 382 static void 383 nak(error, peer) 384 int error; 385 struct sockaddr *peer; 386 { 387 const struct errmsg *pe; 388 struct tftphdr *tp; 389 int length; 390 size_t msglen; 391 392 tp = (struct tftphdr *)ackbuf; 393 tp->th_opcode = htons((u_short)ERROR); 394 msglen = sizeof(ackbuf) - (&tp->th_msg[0] - ackbuf); 395 for (pe = errmsgs; pe->e_code >= 0; pe++) 396 if (pe->e_code == error) 397 break; 398 if (pe->e_code < 0) { 399 tp->th_code = EUNDEF; 400 strlcpy(tp->th_msg, strerror(error - 100), msglen); 401 } else { 402 tp->th_code = htons((u_short)error); 403 strlcpy(tp->th_msg, pe->e_msg, msglen); 404 } 405 length = strlen(tp->th_msg); 406 msglen = &tp->th_msg[length + 1] - ackbuf; 407 if (trace) 408 tpacket("sent", tp, (int)msglen); 409 if (sendto(f, ackbuf, msglen, 0, peer, peer->sa_len) != length) 410 warn("nak"); 411 } 412 413 static void 414 tpacket(s, tp, n) 415 const char *s; 416 struct tftphdr *tp; 417 int n; 418 { 419 static char *opcodes[] = 420 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 421 char *cp, *file; 422 u_short op = ntohs(tp->th_opcode); 423 424 if (op < RRQ || op > ERROR) 425 printf("%s opcode=%x ", s, op); 426 else 427 printf("%s %s ", s, opcodes[op]); 428 switch (op) { 429 430 case RRQ: 431 case WRQ: 432 n -= 2; 433 #ifndef __SVR4 434 cp = tp->th_stuff; 435 #else 436 cp = (void *) &tp->th_stuff; 437 #endif 438 file = cp; 439 cp = strchr(cp, '\0'); 440 printf("<file=%s, mode=%s>\n", file, cp + 1); 441 break; 442 443 case DATA: 444 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 445 break; 446 447 case ACK: 448 printf("<block=%d>\n", ntohs(tp->th_block)); 449 break; 450 451 case ERROR: 452 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 453 break; 454 } 455 } 456 457 struct timeval tstart; 458 struct timeval tstop; 459 460 static void 461 startclock() 462 { 463 464 (void)gettimeofday(&tstart, NULL); 465 } 466 467 static void 468 stopclock() 469 { 470 471 (void)gettimeofday(&tstop, NULL); 472 } 473 474 static void 475 printstats(direction, amount) 476 const char *direction; 477 unsigned long amount; 478 { 479 double delta; 480 481 /* compute delta in 1/10's second units */ 482 delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 483 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 484 delta = delta/10.; /* back to seconds */ 485 printf("%s %ld bytes in %.1f seconds", direction, amount, delta); 486 if (verbose) 487 printf(" [%.0f bits/sec]", (amount*8.)/delta); 488 putchar('\n'); 489 } 490 491 static void 492 timer(sig) 493 int sig; 494 { 495 496 timeout += rexmtval; 497 if (timeout >= maxtimeout) { 498 printf("Transfer timed out.\n"); 499 longjmp(toplevel, -1); 500 } 501 longjmp(timeoutbuf, 1); 502 } 503