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