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