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