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