1 /* $NetBSD: tftp.c,v 1.11 2000/01/21 17:08:36 mycroft 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.11 2000/01/21 17:08:36 mycroft 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 int block, size, convert; 103 volatile unsigned long amount; 104 struct sockaddr_storage from; 105 int fromlen; 106 FILE *file; 107 struct sockaddr_storage peer; 108 109 startclock(); /* start stat's clock */ 110 dp = r_init(); /* reset fillbuf/read-ahead code */ 111 ap = (struct tftphdr *)ackbuf; 112 file = fdopen(fd, "r"); 113 convert = !strcmp(mode, "netascii"); 114 block = 0; 115 amount = 0; 116 memcpy(&peer, &peeraddr, peeraddr.ss_len); 117 118 signal(SIGALRM, timer); 119 do { 120 if (block == 0) 121 size = makerequest(WRQ, name, dp, mode) - 4; 122 else { 123 /* size = read(fd, dp->th_data, SEGSIZE); */ 124 size = readit(file, &dp, convert); 125 if (size < 0) { 126 nak(errno + 100, (struct sockaddr *)&peer); 127 break; 128 } 129 dp->th_opcode = htons((u_short)DATA); 130 dp->th_block = htons((u_short)block); 131 } 132 timeout = 0; 133 (void) setjmp(timeoutbuf); 134 send_data: 135 if (trace) 136 tpacket("sent", dp, size + 4); 137 n = sendto(f, dp, size + 4, 0, 138 (struct sockaddr *)&peer, peer.ss_len); 139 if (n != size + 4) { 140 warn("sendto"); 141 goto abort; 142 } 143 read_ahead(file, convert); 144 for ( ; ; ) { 145 alarm(rexmtval); 146 do { 147 fromlen = sizeof(from); 148 n = recvfrom(f, ackbuf, sizeof(ackbuf), 0, 149 (struct sockaddr *)&from, &fromlen); 150 } while (n <= 0); 151 alarm(0); 152 if (n < 0) { 153 warn("recvfrom"); 154 goto abort; 155 } 156 switch (peer.ss_family) { /* added */ 157 case AF_INET: 158 ((struct sockaddr_in *)&peer)->sin_port = 159 ((struct sockaddr_in *)&from)->sin_port; 160 break; 161 case AF_INET6: 162 ((struct sockaddr_in6 *)&peer)->sin6_port = 163 ((struct sockaddr_in6 *)&from)->sin6_port; 164 break; 165 default: 166 /* unsupported */ 167 break; 168 } 169 if (trace) 170 tpacket("received", ap, n); 171 /* should verify packet came from server */ 172 ap->th_opcode = ntohs(ap->th_opcode); 173 ap->th_block = ntohs(ap->th_block); 174 if (ap->th_opcode == ERROR) { 175 printf("Error code %d: %s\n", ap->th_code, 176 ap->th_msg); 177 goto abort; 178 } 179 if (ap->th_opcode == ACK) { 180 int j; 181 182 if (ap->th_block == block) { 183 break; 184 } 185 /* On an error, try to synchronize 186 * both sides. 187 */ 188 j = synchnet(f); 189 if (j && trace) { 190 printf("discarded %d packets\n", 191 j); 192 } 193 if (ap->th_block == (block-1)) { 194 goto send_data; 195 } 196 } 197 } 198 if (block > 0) 199 amount += size; 200 block++; 201 } while (size == SEGSIZE || block == 1); 202 abort: 203 fclose(file); 204 stopclock(); 205 if (amount > 0) 206 printstats("Sent", amount); 207 } 208 209 /* 210 * Receive a file. 211 */ 212 void 213 recvfile(fd, name, mode) 214 int fd; 215 char *name; 216 char *mode; 217 { 218 struct tftphdr *ap; 219 struct tftphdr *dp; 220 int n; 221 volatile int block, size, firsttrip; 222 volatile unsigned long amount; 223 struct sockaddr_storage from; 224 int fromlen; 225 FILE *file; 226 volatile int convert; /* true if converting crlf -> lf */ 227 struct sockaddr_storage peer; 228 229 startclock(); 230 dp = w_init(); 231 ap = (struct tftphdr *)ackbuf; 232 file = fdopen(fd, "w"); 233 convert = !strcmp(mode, "netascii"); 234 block = 1; 235 firsttrip = 1; 236 amount = 0; 237 memcpy(&peer, &peeraddr, peeraddr.ss_len); 238 239 signal(SIGALRM, timer); 240 do { 241 if (firsttrip) { 242 size = makerequest(RRQ, name, ap, mode); 243 firsttrip = 0; 244 } else { 245 ap->th_opcode = htons((u_short)ACK); 246 ap->th_block = htons((u_short)(block)); 247 size = 4; 248 block++; 249 } 250 timeout = 0; 251 (void) setjmp(timeoutbuf); 252 send_ack: 253 if (trace) 254 tpacket("sent", ap, size); 255 if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peer, 256 peer.ss_len) != size) { 257 alarm(0); 258 warn("sendto"); 259 goto abort; 260 } 261 write_behind(file, convert); 262 for ( ; ; ) { 263 alarm(rexmtval); 264 do { 265 fromlen = sizeof(from); 266 n = recvfrom(f, dp, PKTSIZE, 0, 267 (struct sockaddr *)&from, &fromlen); 268 } while (n <= 0); 269 alarm(0); 270 if (n < 0) { 271 warn("recvfrom"); 272 goto abort; 273 } 274 switch (peer.ss_family) { /* added */ 275 case AF_INET: 276 ((struct sockaddr_in *)&peer)->sin_port = 277 ((struct sockaddr_in *)&from)->sin_port; 278 break; 279 case AF_INET6: 280 ((struct sockaddr_in6 *)&peer)->sin6_port = 281 ((struct sockaddr_in6 *)&from)->sin6_port; 282 break; 283 default: 284 /* unsupported */ 285 break; 286 } 287 if (trace) 288 tpacket("received", dp, n); 289 /* should verify client address */ 290 dp->th_opcode = ntohs(dp->th_opcode); 291 dp->th_block = ntohs(dp->th_block); 292 if (dp->th_opcode == ERROR) { 293 printf("Error code %d: %s\n", dp->th_code, 294 dp->th_msg); 295 goto abort; 296 } 297 if (dp->th_opcode == DATA) { 298 int j; 299 300 if (dp->th_block == block) { 301 break; /* have next packet */ 302 } 303 /* On an error, try to synchronize 304 * both sides. 305 */ 306 j = synchnet(f); 307 if (j && trace) { 308 printf("discarded %d packets\n", j); 309 } 310 if (dp->th_block == (block-1)) { 311 goto send_ack; /* resend ack */ 312 } 313 } 314 } 315 /* size = write(fd, dp->th_data, n - 4); */ 316 size = writeit(file, &dp, n - 4, convert); 317 if (size < 0) { 318 nak(errno + 100, (struct sockaddr *)&peer); 319 break; 320 } 321 amount += size; 322 } while (size == SEGSIZE); 323 abort: /* ok to ack, since user */ 324 ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 325 ap->th_block = htons((u_short)block); 326 (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peer, 327 peer.ss_len); 328 write_behind(file, convert); /* flush last buffer */ 329 fclose(file); 330 stopclock(); 331 if (amount > 0) 332 printstats("Received", amount); 333 } 334 335 static int 336 makerequest(request, name, tp, mode) 337 int request; 338 const char *name; 339 struct tftphdr *tp; 340 const char *mode; 341 { 342 char *cp; 343 344 tp->th_opcode = htons((u_short)request); 345 #ifndef __SVR4 346 cp = tp->th_stuff; 347 #else 348 cp = (void *)&tp->th_stuff; 349 #endif 350 strcpy(cp, name); 351 cp += strlen(name); 352 *cp++ = '\0'; 353 strcpy(cp, mode); 354 cp += strlen(mode); 355 *cp++ = '\0'; 356 return (cp - (char *)tp); 357 } 358 359 const struct errmsg { 360 int e_code; 361 const char *e_msg; 362 } errmsgs[] = { 363 { EUNDEF, "Undefined error code" }, 364 { ENOTFOUND, "File not found" }, 365 { EACCESS, "Access violation" }, 366 { ENOSPACE, "Disk full or allocation exceeded" }, 367 { EBADOP, "Illegal TFTP operation" }, 368 { EBADID, "Unknown transfer ID" }, 369 { EEXISTS, "File already exists" }, 370 { ENOUSER, "No such user" }, 371 { -1, 0 } 372 }; 373 374 /* 375 * Send a nak packet (error message). 376 * Error code passed in is one of the 377 * standard TFTP codes, or a UNIX errno 378 * offset by 100. 379 */ 380 static void 381 nak(error, peer) 382 int error; 383 struct sockaddr *peer; 384 { 385 const struct errmsg *pe; 386 struct tftphdr *tp; 387 int length; 388 389 tp = (struct tftphdr *)ackbuf; 390 tp->th_opcode = htons((u_short)ERROR); 391 for (pe = errmsgs; pe->e_code >= 0; pe++) 392 if (pe->e_code == error) 393 break; 394 if (pe->e_code < 0) { 395 tp->th_code = EUNDEF; 396 strcpy(tp->th_msg, strerror(error - 100)); 397 } else { 398 tp->th_code = htons((u_short)error); 399 strcpy(tp->th_msg, pe->e_msg); 400 } 401 length = strlen(pe->e_msg) + 4; 402 if (trace) 403 tpacket("sent", tp, length); 404 if (sendto(f, ackbuf, length, 0, peer, peer->sa_len) != length) 405 warn("nak"); 406 } 407 408 static void 409 tpacket(s, tp, n) 410 const char *s; 411 struct tftphdr *tp; 412 int n; 413 { 414 static char *opcodes[] = 415 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 416 char *cp, *file; 417 u_short op = ntohs(tp->th_opcode); 418 419 if (op < RRQ || op > ERROR) 420 printf("%s opcode=%x ", s, op); 421 else 422 printf("%s %s ", s, opcodes[op]); 423 switch (op) { 424 425 case RRQ: 426 case WRQ: 427 n -= 2; 428 #ifndef __SVR4 429 cp = tp->th_stuff; 430 #else 431 cp = (void *) &tp->th_stuff; 432 #endif 433 file = cp; 434 cp = strchr(cp, '\0'); 435 printf("<file=%s, mode=%s>\n", file, cp + 1); 436 break; 437 438 case DATA: 439 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 440 break; 441 442 case ACK: 443 printf("<block=%d>\n", ntohs(tp->th_block)); 444 break; 445 446 case ERROR: 447 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 448 break; 449 } 450 } 451 452 struct timeval tstart; 453 struct timeval tstop; 454 455 static void 456 startclock() 457 { 458 459 (void)gettimeofday(&tstart, NULL); 460 } 461 462 static void 463 stopclock() 464 { 465 466 (void)gettimeofday(&tstop, NULL); 467 } 468 469 static void 470 printstats(direction, amount) 471 const char *direction; 472 unsigned long amount; 473 { 474 double delta; 475 476 /* compute delta in 1/10's second units */ 477 delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 478 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 479 delta = delta/10.; /* back to seconds */ 480 printf("%s %ld bytes in %.1f seconds", direction, amount, delta); 481 if (verbose) 482 printf(" [%.0f bits/sec]", (amount*8.)/delta); 483 putchar('\n'); 484 } 485 486 static void 487 timer(sig) 488 int sig; 489 { 490 491 timeout += rexmtval; 492 if (timeout >= maxtimeout) { 493 printf("Transfer timed out.\n"); 494 longjmp(toplevel, -1); 495 } 496 longjmp(timeoutbuf, 1); 497 } 498