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