tftp.c revision 50477
1183234Ssimon/* 2183234Ssimon * Copyright (c) 1983, 1993 3183234Ssimon * The Regents of the University of California. All rights reserved. 4183234Ssimon * 5183234Ssimon * Redistribution and use in source and binary forms, with or without 6183234Ssimon * modification, are permitted provided that the following conditions 7183234Ssimon * are met: 8183234Ssimon * 1. Redistributions of source code must retain the above copyright 9280297Sjkim * notice, this list of conditions and the following disclaimer. 10183234Ssimon * 2. Redistributions in binary form must reproduce the above copyright 11183234Ssimon * notice, this list of conditions and the following disclaimer in the 12183234Ssimon * documentation and/or other materials provided with the distribution. 13183234Ssimon * 3. All advertising materials mentioning features or use of this software 14183234Ssimon * must display the following acknowledgement: 15183234Ssimon * This product includes software developed by the University of 16183234Ssimon * California, Berkeley and its contributors. 17183234Ssimon * 4. Neither the name of the University nor the names of its contributors 18183234Ssimon * may be used to endorse or promote products derived from this software 19183234Ssimon * without specific prior written permission. 20183234Ssimon * 21183234Ssimon * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22183234Ssimon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23183234Ssimon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24183234Ssimon * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25183234Ssimon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26183234Ssimon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27183234Ssimon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28183234Ssimon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29183234Ssimon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30183234Ssimon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31183234Ssimon * SUCH DAMAGE. 32183234Ssimon */ 33183234Ssimon 34183234Ssimon#ifndef lint 35183234Ssimon#if 0 36183234Ssimonstatic char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; 37183234Ssimon#endif 38183234Ssimonstatic const char rcsid[] = 39183234Ssimon "$FreeBSD: head/usr.bin/tftp/tftp.c 50477 1999-08-28 01:08:13Z peter $"; 40183234Ssimon#endif /* not lint */ 41183234Ssimon 42183234Ssimon/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 43183234Ssimon 44183234Ssimon/* 45183234Ssimon * TFTP User Program -- Protocol Machines 46183234Ssimon */ 47183234Ssimon#include <sys/types.h> 48183234Ssimon#include <sys/socket.h> 49183234Ssimon#include <sys/time.h> 50183234Ssimon 51183234Ssimon#include <netinet/in.h> 52183234Ssimon 53183234Ssimon#include <arpa/tftp.h> 54183234Ssimon 55183234Ssimon#include <err.h> 56280297Sjkim#include <errno.h> 57183234Ssimon#include <setjmp.h> 58237657Sjkim#include <signal.h> 59237657Sjkim#include <stdio.h> 60237657Sjkim#include <string.h> 61237657Sjkim#include <unistd.h> 62183234Ssimon 63280297Sjkim#include "extern.h" 64280297Sjkim#include "tftpsubs.h" 65183234Ssimon 66183234Ssimonextern struct sockaddr_in peeraddr; /* filled in by main */ 67183234Ssimonextern int f; /* the opened socket */ 68183234Ssimonextern int trace; 69183234Ssimonextern int verbose; 70280297Sjkimextern int rexmtval; 71183234Ssimonextern int maxtimeout; 72183234Ssimon 73183234Ssimon#define PKTSIZE SEGSIZE+4 74183234Ssimonchar ackbuf[PKTSIZE]; 75280297Sjkimint timeout; 76280297Sjkimjmp_buf toplevel; 77280297Sjkimjmp_buf timeoutbuf; 78280297Sjkim 79280297Sjkimstatic void nak __P((int)); 80280297Sjkimstatic int makerequest __P((int, const char *, struct tftphdr *, const char *)); 81280297Sjkimstatic void printstats __P((const char *, unsigned long)); 82280297Sjkimstatic void startclock __P((void)); 83280297Sjkimstatic void stopclock __P((void)); 84280297Sjkimstatic void timer __P((int)); 85280297Sjkimstatic void tpacket __P((const char *, struct tftphdr *, int)); 86280297Sjkim 87280297Sjkim/* 88280297Sjkim * Send the requested file. 89280297Sjkim */ 90280297Sjkimvoid 91280297Sjkimxmitfile(fd, name, mode) 92280297Sjkim int fd; 93280297Sjkim char *name; 94183234Ssimon char *mode; 95183234Ssimon{ 96280297Sjkim register struct tftphdr *ap; /* data and ack packets */ 97280297Sjkim struct tftphdr *r_init(), *dp; 98280297Sjkim register int n; 99280297Sjkim volatile int block, size, convert; 100280297Sjkim volatile unsigned long amount; 101280297Sjkim struct sockaddr_in from; 102280297Sjkim int fromlen; 103280297Sjkim FILE *file; 104280297Sjkim 105280297Sjkim startclock(); /* start stat's clock */ 106280297Sjkim dp = r_init(); /* reset fillbuf/read-ahead code */ 107280297Sjkim ap = (struct tftphdr *)ackbuf; 108280297Sjkim file = fdopen(fd, "r"); 109280297Sjkim convert = !strcmp(mode, "netascii"); 110280297Sjkim block = 0; 111280297Sjkim amount = 0; 112280297Sjkim 113280297Sjkim signal(SIGALRM, timer); 114280297Sjkim do { 115280297Sjkim if (block == 0) 116280297Sjkim size = makerequest(WRQ, name, dp, mode) - 4; 117280297Sjkim else { 118280297Sjkim /* size = read(fd, dp->th_data, SEGSIZE); */ 119280297Sjkim size = readit(file, &dp, convert); 120280297Sjkim if (size < 0) { 121280297Sjkim nak(errno + 100); 122280297Sjkim break; 123280297Sjkim } 124280297Sjkim dp->th_opcode = htons((u_short)DATA); 125183234Ssimon dp->th_block = htons((u_short)block); 126183234Ssimon } 127183234Ssimon timeout = 0; 128183234Ssimon (void) setjmp(timeoutbuf); 129183234Ssimonsend_data: 130 if (trace) 131 tpacket("sent", dp, size + 4); 132 n = sendto(f, dp, size + 4, 0, 133 (struct sockaddr *)&peeraddr, sizeof(peeraddr)); 134 if (n != size + 4) { 135 warn("sendto"); 136 goto abort; 137 } 138 read_ahead(file, convert); 139 for ( ; ; ) { 140 alarm(rexmtval); 141 do { 142 fromlen = sizeof(from); 143 n = recvfrom(f, ackbuf, sizeof(ackbuf), 0, 144 (struct sockaddr *)&from, &fromlen); 145 } while (n <= 0); 146 alarm(0); 147 if (n < 0) { 148 warn("recvfrom"); 149 goto abort; 150 } 151 peeraddr.sin_port = from.sin_port; /* added */ 152 if (trace) 153 tpacket("received", ap, n); 154 /* should verify packet came from server */ 155 ap->th_opcode = ntohs(ap->th_opcode); 156 ap->th_block = ntohs(ap->th_block); 157 if (ap->th_opcode == ERROR) { 158 printf("Error code %d: %s\n", ap->th_code, 159 ap->th_msg); 160 goto abort; 161 } 162 if (ap->th_opcode == ACK) { 163 int j; 164 165 if (ap->th_block == block) { 166 break; 167 } 168 /* On an error, try to synchronize 169 * both sides. 170 */ 171 j = synchnet(f); 172 if (j && trace) { 173 printf("discarded %d packets\n", 174 j); 175 } 176 if (ap->th_block == (block-1)) { 177 goto send_data; 178 } 179 } 180 } 181 if (block > 0) 182 amount += size; 183 block++; 184 } while (size == SEGSIZE || block == 1); 185abort: 186 fclose(file); 187 stopclock(); 188 if (amount > 0) 189 printstats("Sent", amount); 190} 191 192/* 193 * Receive a file. 194 */ 195void 196recvfile(fd, name, mode) 197 int fd; 198 char *name; 199 char *mode; 200{ 201 register struct tftphdr *ap; 202 struct tftphdr *dp, *w_init(); 203 register int n; 204 volatile int block, size, firsttrip; 205 volatile unsigned long amount; 206 struct sockaddr_in from; 207 int fromlen; 208 FILE *file; 209 volatile int convert; /* true if converting crlf -> lf */ 210 211 startclock(); 212 dp = w_init(); 213 ap = (struct tftphdr *)ackbuf; 214 file = fdopen(fd, "w"); 215 convert = !strcmp(mode, "netascii"); 216 block = 1; 217 firsttrip = 1; 218 amount = 0; 219 220 signal(SIGALRM, timer); 221 do { 222 if (firsttrip) { 223 size = makerequest(RRQ, name, ap, mode); 224 firsttrip = 0; 225 } else { 226 ap->th_opcode = htons((u_short)ACK); 227 ap->th_block = htons((u_short)(block)); 228 size = 4; 229 block++; 230 } 231 timeout = 0; 232 (void) setjmp(timeoutbuf); 233send_ack: 234 if (trace) 235 tpacket("sent", ap, size); 236 if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr, 237 sizeof(peeraddr)) != size) { 238 alarm(0); 239 warn("sendto"); 240 goto abort; 241 } 242 write_behind(file, convert); 243 for ( ; ; ) { 244 alarm(rexmtval); 245 do { 246 fromlen = sizeof(from); 247 n = recvfrom(f, dp, PKTSIZE, 0, 248 (struct sockaddr *)&from, &fromlen); 249 } while (n <= 0); 250 alarm(0); 251 if (n < 0) { 252 warn("recvfrom"); 253 goto abort; 254 } 255 peeraddr.sin_port = from.sin_port; /* added */ 256 if (trace) 257 tpacket("received", dp, n); 258 /* should verify client address */ 259 dp->th_opcode = ntohs(dp->th_opcode); 260 dp->th_block = ntohs(dp->th_block); 261 if (dp->th_opcode == ERROR) { 262 printf("Error code %d: %s\n", dp->th_code, 263 dp->th_msg); 264 goto abort; 265 } 266 if (dp->th_opcode == DATA) { 267 int j; 268 269 if (dp->th_block == block) { 270 break; /* have next packet */ 271 } 272 /* On an error, try to synchronize 273 * both sides. 274 */ 275 j = synchnet(f); 276 if (j && trace) { 277 printf("discarded %d packets\n", j); 278 } 279 if (dp->th_block == (block-1)) { 280 goto send_ack; /* resend ack */ 281 } 282 } 283 } 284 /* size = write(fd, dp->th_data, n - 4); */ 285 size = writeit(file, &dp, n - 4, convert); 286 if (size < 0) { 287 nak(errno + 100); 288 break; 289 } 290 amount += size; 291 } while (size == SEGSIZE); 292abort: /* ok to ack, since user */ 293 ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 294 ap->th_block = htons((u_short)block); 295 (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, 296 sizeof(peeraddr)); 297 write_behind(file, convert); /* flush last buffer */ 298 fclose(file); 299 stopclock(); 300 if (amount > 0) 301 printstats("Received", amount); 302} 303 304static int 305makerequest(request, name, tp, mode) 306 int request; 307 const char *name; 308 struct tftphdr *tp; 309 const char *mode; 310{ 311 register char *cp; 312 313 tp->th_opcode = htons((u_short)request); 314 cp = tp->th_stuff; 315 strcpy(cp, name); 316 cp += strlen(name); 317 *cp++ = '\0'; 318 strcpy(cp, mode); 319 cp += strlen(mode); 320 *cp++ = '\0'; 321 return (cp - (char *)tp); 322} 323 324struct errmsg { 325 int e_code; 326 char *e_msg; 327} errmsgs[] = { 328 { EUNDEF, "Undefined error code" }, 329 { ENOTFOUND, "File not found" }, 330 { EACCESS, "Access violation" }, 331 { ENOSPACE, "Disk full or allocation exceeded" }, 332 { EBADOP, "Illegal TFTP operation" }, 333 { EBADID, "Unknown transfer ID" }, 334 { EEXISTS, "File already exists" }, 335 { ENOUSER, "No such user" }, 336 { -1, 0 } 337}; 338 339/* 340 * Send a nak packet (error message). 341 * Error code passed in is one of the 342 * standard TFTP codes, or a UNIX errno 343 * offset by 100. 344 */ 345static void 346nak(error) 347 int error; 348{ 349 register struct errmsg *pe; 350 register struct tftphdr *tp; 351 int length; 352 char *strerror(); 353 354 tp = (struct tftphdr *)ackbuf; 355 tp->th_opcode = htons((u_short)ERROR); 356 tp->th_code = htons((u_short)error); 357 for (pe = errmsgs; pe->e_code >= 0; pe++) 358 if (pe->e_code == error) 359 break; 360 if (pe->e_code < 0) { 361 pe->e_msg = strerror(error - 100); 362 tp->th_code = EUNDEF; 363 } 364 strcpy(tp->th_msg, pe->e_msg); 365 length = strlen(pe->e_msg) + 4; 366 if (trace) 367 tpacket("sent", tp, length); 368 if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, 369 sizeof(peeraddr)) != length) 370 warn("nak"); 371} 372 373static void 374tpacket(s, tp, n) 375 const char *s; 376 struct tftphdr *tp; 377 int n; 378{ 379 static char *opcodes[] = 380 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 381 register char *cp, *file; 382 u_short op = ntohs(tp->th_opcode); 383 char *index(); 384 385 if (op < RRQ || op > ERROR) 386 printf("%s opcode=%x ", s, op); 387 else 388 printf("%s %s ", s, opcodes[op]); 389 switch (op) { 390 391 case RRQ: 392 case WRQ: 393 n -= 2; 394 file = cp = tp->th_stuff; 395 cp = index(cp, '\0'); 396 printf("<file=%s, mode=%s>\n", file, cp + 1); 397 break; 398 399 case DATA: 400 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 401 break; 402 403 case ACK: 404 printf("<block=%d>\n", ntohs(tp->th_block)); 405 break; 406 407 case ERROR: 408 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 409 break; 410 } 411} 412 413struct timeval tstart; 414struct timeval tstop; 415 416static void 417startclock() 418{ 419 420 (void)gettimeofday(&tstart, NULL); 421} 422 423static void 424stopclock() 425{ 426 427 (void)gettimeofday(&tstop, NULL); 428} 429 430static void 431printstats(direction, amount) 432 const char *direction; 433 unsigned long amount; 434{ 435 double delta; 436 /* compute delta in 1/10's second units */ 437 delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 438 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 439 delta = delta/10.; /* back to seconds */ 440 printf("%s %d bytes in %.1f seconds", direction, amount, delta); 441 if (verbose) 442 printf(" [%.0f bits/sec]", (amount*8.)/delta); 443 putchar('\n'); 444} 445 446static void 447timer(sig) 448 int sig; 449{ 450 451 timeout += rexmtval; 452 if (timeout >= maxtimeout) { 453 printf("Transfer timed out.\n"); 454 longjmp(toplevel, -1); 455 } 456 longjmp(timeoutbuf, 1); 457} 458