1 /* $OpenBSD: tftp.c,v 1.4 1997/08/06 06:43:45 deraadt Exp $ */ 2 /* $NetBSD: tftp.c,v 1.5 1995/04/29 05:55:25 cgd Exp $ */ 3 4 /* 5 * Copyright (c) 1983, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; 40 #endif 41 static char rcsid[] = "$OpenBSD: tftp.c,v 1.4 1997/08/06 06:43:45 deraadt Exp $"; 42 #endif /* not lint */ 43 44 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 45 46 /* 47 * TFTP User Program -- Protocol Machines 48 */ 49 #include <sys/types.h> 50 #include <sys/socket.h> 51 #include <sys/time.h> 52 53 #include <netinet/in.h> 54 55 #include <arpa/tftp.h> 56 57 #include <errno.h> 58 #include <setjmp.h> 59 #include <signal.h> 60 #include <stdio.h> 61 #include <string.h> 62 #include <unistd.h> 63 64 #include "extern.h" 65 #include "tftpsubs.h" 66 67 extern int errno; 68 69 extern struct sockaddr_in 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)); 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 register struct tftphdr *ap; /* data and ack packets */ 100 struct tftphdr *r_init(), *dp; 101 register int n; 102 volatile int block, size, convert; 103 volatile unsigned long amount; 104 struct sockaddr_in from; 105 int fromlen; 106 FILE *file; 107 108 startclock(); /* start stat's clock */ 109 dp = r_init(); /* reset fillbuf/read-ahead code */ 110 ap = (struct tftphdr *)ackbuf; 111 file = fdopen(fd, "r"); 112 convert = !strcmp(mode, "netascii"); 113 block = 0; 114 amount = 0; 115 116 signal(SIGALRM, timer); 117 do { 118 if (block == 0) 119 size = makerequest(WRQ, name, dp, mode) - 4; 120 else { 121 /* size = read(fd, dp->th_data, SEGSIZE); */ 122 size = readit(file, &dp, convert); 123 if (size < 0) { 124 nak(errno + 100); 125 break; 126 } 127 dp->th_opcode = htons((u_short)DATA); 128 dp->th_block = htons((u_short)block); 129 } 130 timeout = 0; 131 (void) setjmp(timeoutbuf); 132 send_data: 133 if (trace) 134 tpacket("sent", dp, size + 4); 135 n = sendto(f, dp, size + 4, 0, 136 (struct sockaddr *)&peeraddr, sizeof(peeraddr)); 137 if (n != size + 4) { 138 perror("tftp: sendto"); 139 goto abort; 140 } 141 read_ahead(file, convert); 142 for ( ; ; ) { 143 alarm(rexmtval); 144 do { 145 fromlen = sizeof(from); 146 n = recvfrom(f, ackbuf, sizeof(ackbuf), 0, 147 (struct sockaddr *)&from, &fromlen); 148 } while (n <= 0); 149 alarm(0); 150 if (n < 0) { 151 perror("tftp: recvfrom"); 152 goto abort; 153 } 154 peeraddr.sin_port = from.sin_port; /* added */ 155 if (trace) 156 tpacket("received", ap, n); 157 /* should verify packet came from server */ 158 ap->th_opcode = ntohs(ap->th_opcode); 159 ap->th_block = ntohs(ap->th_block); 160 if (ap->th_opcode == ERROR) { 161 printf("Error code %d: %s\n", ap->th_code, 162 ap->th_msg); 163 goto abort; 164 } 165 if (ap->th_opcode == ACK) { 166 int j; 167 168 if (ap->th_block == block) { 169 break; 170 } 171 /* On an error, try to synchronize 172 * both sides. 173 */ 174 j = synchnet(f); 175 if (j && trace) { 176 printf("discarded %d packets\n", 177 j); 178 } 179 if (ap->th_block == (block-1)) { 180 goto send_data; 181 } 182 } 183 } 184 if (block > 0) 185 amount += size; 186 block++; 187 } while (size == SEGSIZE || block == 1); 188 abort: 189 fclose(file); 190 stopclock(); 191 if (amount > 0) 192 printstats("Sent", amount); 193 } 194 195 /* 196 * Receive a file. 197 */ 198 void 199 recvfile(fd, name, mode) 200 int fd; 201 char *name; 202 char *mode; 203 { 204 register struct tftphdr *ap; 205 struct tftphdr *dp, *w_init(); 206 register int n; 207 volatile int block, size, firsttrip; 208 volatile unsigned long amount; 209 struct sockaddr_in from; 210 int fromlen; 211 FILE *file; 212 volatile int convert; /* true if converting crlf -> lf */ 213 214 startclock(); 215 dp = w_init(); 216 ap = (struct tftphdr *)ackbuf; 217 file = fdopen(fd, "w"); 218 convert = !strcmp(mode, "netascii"); 219 block = 1; 220 firsttrip = 1; 221 amount = 0; 222 223 signal(SIGALRM, timer); 224 do { 225 if (firsttrip) { 226 size = makerequest(RRQ, name, ap, mode); 227 firsttrip = 0; 228 } else { 229 ap->th_opcode = htons((u_short)ACK); 230 ap->th_block = htons((u_short)(block)); 231 size = 4; 232 block++; 233 } 234 timeout = 0; 235 (void) setjmp(timeoutbuf); 236 send_ack: 237 if (trace) 238 tpacket("sent", ap, size); 239 if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr, 240 sizeof(peeraddr)) != size) { 241 alarm(0); 242 perror("tftp: sendto"); 243 goto abort; 244 } 245 write_behind(file, convert); 246 for ( ; ; ) { 247 alarm(rexmtval); 248 do { 249 fromlen = sizeof(from); 250 n = recvfrom(f, dp, PKTSIZE, 0, 251 (struct sockaddr *)&from, &fromlen); 252 } while (n <= 0); 253 alarm(0); 254 if (n < 0) { 255 perror("tftp: recvfrom"); 256 goto abort; 257 } 258 peeraddr.sin_port = from.sin_port; /* added */ 259 if (trace) 260 tpacket("received", dp, n); 261 /* should verify client address */ 262 dp->th_opcode = ntohs(dp->th_opcode); 263 dp->th_block = ntohs(dp->th_block); 264 if (dp->th_opcode == ERROR) { 265 printf("Error code %d: %s\n", dp->th_code, 266 dp->th_msg); 267 goto abort; 268 } 269 if (dp->th_opcode == DATA) { 270 int j; 271 272 if (dp->th_block == block) { 273 break; /* have next packet */ 274 } 275 /* On an error, try to synchronize 276 * both sides. 277 */ 278 j = synchnet(f); 279 if (j && trace) { 280 printf("discarded %d packets\n", j); 281 } 282 if (dp->th_block == (block-1)) { 283 goto send_ack; /* resend ack */ 284 } 285 } 286 } 287 /* size = write(fd, dp->th_data, n - 4); */ 288 size = writeit(file, &dp, n - 4, convert); 289 if (size < 0) { 290 nak(errno + 100); 291 break; 292 } 293 amount += size; 294 } while (size == SEGSIZE); 295 abort: /* ok to ack, since user */ 296 ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 297 ap->th_block = htons((u_short)block); 298 (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, 299 sizeof(peeraddr)); 300 write_behind(file, convert); /* flush last buffer */ 301 fclose(file); 302 stopclock(); 303 if (amount > 0) 304 printstats("Received", amount); 305 } 306 307 static int 308 makerequest(request, name, tp, mode) 309 int request; 310 const char *name; 311 struct tftphdr *tp; 312 const char *mode; 313 { 314 register char *cp; 315 316 tp->th_opcode = htons((u_short)request); 317 cp = tp->th_stuff; 318 strcpy(cp, name); 319 cp += strlen(name); 320 *cp++ = '\0'; 321 strcpy(cp, mode); 322 cp += strlen(mode); 323 *cp++ = '\0'; 324 return (cp - (char *)tp); 325 } 326 327 struct errmsg { 328 int e_code; 329 char *e_msg; 330 } errmsgs[] = { 331 { EUNDEF, "Undefined error code" }, 332 { ENOTFOUND, "File not found" }, 333 { EACCESS, "Access violation" }, 334 { ENOSPACE, "Disk full or allocation exceeded" }, 335 { EBADOP, "Illegal TFTP operation" }, 336 { EBADID, "Unknown transfer ID" }, 337 { EEXISTS, "File already exists" }, 338 { ENOUSER, "No such user" }, 339 { -1, 0 } 340 }; 341 342 /* 343 * Send a nak packet (error message). 344 * Error code passed in is one of the 345 * standard TFTP codes, or a UNIX errno 346 * offset by 100. 347 */ 348 static void 349 nak(error) 350 int error; 351 { 352 register struct errmsg *pe; 353 register struct tftphdr *tp; 354 int length; 355 char *strerror(); 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 = strchr(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 /* compute delta in 1/10's second units */ 439 delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 440 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 441 delta = delta/10.; /* back to seconds */ 442 printf("%s %d bytes in %.1f seconds", direction, amount, delta); 443 if (verbose) 444 printf(" [%.0f bits/sec]", (amount*8.)/delta); 445 putchar('\n'); 446 } 447 448 static void 449 timer(sig) 450 int sig; 451 { 452 int save_errno = errno; 453 454 timeout += rexmtval; 455 if (timeout >= maxtimeout) { 456 printf("Transfer timed out.\n"); 457 errno = save_errno; 458 longjmp(toplevel, -1); 459 } 460 errno = save_errno; 461 longjmp(timeoutbuf, 1); 462 } 463