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