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