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