1 /* $OpenBSD: tftp.c,v 1.14 2003/06/25 15:45:10 deraadt Exp $ */ 2 /* $NetBSD: tftp.c,v 1.5 1995/04/29 05:55:25 cgd Exp $ */ 3 4 /* 5 * Copyright (c) 1983, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; 36 #endif 37 static const char rcsid[] = "$OpenBSD: tftp.c,v 1.14 2003/06/25 15:45:10 deraadt Exp $"; 38 #endif /* not lint */ 39 40 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 41 42 /* 43 * TFTP User Program -- Protocol Machines 44 */ 45 #include <sys/types.h> 46 #include <sys/socket.h> 47 #include <sys/time.h> 48 49 #include <netinet/in.h> 50 51 #include <arpa/tftp.h> 52 53 #include <errno.h> 54 #include <setjmp.h> 55 #include <signal.h> 56 #include <stdio.h> 57 #include <stddef.h> 58 #include <string.h> 59 #include <unistd.h> 60 #include <err.h> 61 62 #include "extern.h" 63 #include "tftpsubs.h" 64 65 66 extern struct sockaddr_in peeraddr; /* filled in by main */ 67 extern int f; /* the opened socket */ 68 extern int trace; 69 extern int verbose; 70 extern int rexmtval; 71 extern int maxtimeout; 72 73 #define PKTSIZE SEGSIZE+4 74 char ackbuf[PKTSIZE]; 75 int timeout; 76 jmp_buf toplevel; 77 jmp_buf timeoutbuf; 78 79 static void nak(int); 80 static int makerequest(int, const char *, struct tftphdr *, const char *); 81 static void printstats(const char *, unsigned long); 82 static void startclock(void); 83 static void stopclock(void); 84 static void timer(int); 85 static void tpacket(const char *, struct tftphdr *, int); 86 87 /* 88 * Send the requested file. 89 */ 90 void 91 sendfile(int fd, char *name, char *mode) 92 { 93 struct tftphdr *dp, *ap; /* data and ack packets */ 94 volatile int block, size, convert; 95 volatile unsigned long amount; 96 struct sockaddr_in from; 97 int n, fromlen; 98 FILE *file; 99 100 startclock(); /* start stat's clock */ 101 dp = r_init(); /* reset fillbuf/read-ahead code */ 102 ap = (struct tftphdr *)ackbuf; 103 file = fdopen(fd, "r"); 104 convert = !strcmp(mode, "netascii"); 105 block = 0; 106 amount = 0; 107 108 signal(SIGALRM, timer); 109 do { 110 if (block == 0) 111 size = makerequest(WRQ, name, dp, mode) - 4; 112 else { 113 /* size = read(fd, dp->th_data, SEGSIZE); */ 114 size = readit(file, &dp, convert); 115 if (size < 0) { 116 nak(errno + 100); 117 break; 118 } 119 dp->th_opcode = htons((u_short)DATA); 120 dp->th_block = htons((u_short)block); 121 } 122 timeout = 0; 123 (void) setjmp(timeoutbuf); 124 send_data: 125 if (trace) 126 tpacket("sent", dp, size + 4); 127 n = sendto(f, dp, size + 4, 0, 128 (struct sockaddr *)&peeraddr, sizeof(peeraddr)); 129 if (n != size + 4) { 130 warn("sendto"); 131 goto abort; 132 } 133 read_ahead(file, convert); 134 for ( ; ; ) { 135 alarm(rexmtval); 136 do { 137 fromlen = sizeof(from); 138 n = recvfrom(f, ackbuf, sizeof(ackbuf), 0, 139 (struct sockaddr *)&from, &fromlen); 140 } while (n <= 0); 141 alarm(0); 142 if (n < 0) { 143 warn("recvfrom"); 144 goto abort; 145 } 146 peeraddr.sin_port = from.sin_port; /* added */ 147 if (trace) 148 tpacket("received", ap, n); 149 /* should verify packet came from server */ 150 ap->th_opcode = ntohs(ap->th_opcode); 151 ap->th_block = ntohs(ap->th_block); 152 if (ap->th_opcode == ERROR) { 153 printf("Error code %d: %s\n", ap->th_code, 154 ap->th_msg); 155 goto abort; 156 } 157 if (ap->th_opcode == ACK) { 158 int j; 159 160 if (ap->th_block == block) { 161 break; 162 } 163 /* On an error, try to synchronize 164 * both sides. 165 */ 166 j = synchnet(f); 167 if (j && trace) { 168 printf("discarded %d packets\n", 169 j); 170 } 171 if (ap->th_block == (block-1)) { 172 goto send_data; 173 } 174 } 175 } 176 if (block > 0) 177 amount += size; 178 block++; 179 } while (size == SEGSIZE || block == 1); 180 abort: 181 fclose(file); 182 stopclock(); 183 if (amount > 0) 184 printstats("Sent", amount); 185 } 186 187 /* 188 * Receive a file. 189 */ 190 void 191 recvfile(int fd, char *name, char *mode) 192 { 193 struct tftphdr *dp, *ap; 194 volatile int block, size, firsttrip; 195 volatile unsigned long amount; 196 struct sockaddr_in from; 197 int n, fromlen; 198 FILE *file; 199 volatile int convert; /* true if converting crlf -> lf */ 200 201 startclock(); 202 dp = w_init(); 203 ap = (struct tftphdr *)ackbuf; 204 file = fdopen(fd, "w"); 205 convert = !strcmp(mode, "netascii"); 206 block = 1; 207 firsttrip = 1; 208 amount = 0; 209 210 signal(SIGALRM, timer); 211 do { 212 if (firsttrip) { 213 size = makerequest(RRQ, name, ap, mode); 214 firsttrip = 0; 215 } else { 216 ap->th_opcode = htons((u_short)ACK); 217 ap->th_block = htons((u_short)(block)); 218 size = 4; 219 block++; 220 } 221 timeout = 0; 222 (void) setjmp(timeoutbuf); 223 send_ack: 224 if (trace) 225 tpacket("sent", ap, size); 226 if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr, 227 sizeof(peeraddr)) != size) { 228 alarm(0); 229 warn("sendto"); 230 goto abort; 231 } 232 write_behind(file, convert); 233 for ( ; ; ) { 234 alarm(rexmtval); 235 do { 236 fromlen = sizeof(from); 237 n = recvfrom(f, dp, PKTSIZE, 0, 238 (struct sockaddr *)&from, &fromlen); 239 } while (n <= 0); 240 alarm(0); 241 if (n < 0) { 242 warn("recvfrom"); 243 goto abort; 244 } 245 peeraddr.sin_port = from.sin_port; /* added */ 246 if (trace) 247 tpacket("received", dp, n); 248 /* should verify client address */ 249 dp->th_opcode = ntohs(dp->th_opcode); 250 dp->th_block = ntohs(dp->th_block); 251 if (dp->th_opcode == ERROR) { 252 printf("Error code %d: %s\n", dp->th_code, 253 dp->th_msg); 254 goto abort; 255 } 256 if (dp->th_opcode == DATA) { 257 int j; 258 259 if (dp->th_block == block) { 260 break; /* have next packet */ 261 } 262 /* On an error, try to synchronize 263 * both sides. 264 */ 265 j = synchnet(f); 266 if (j && trace) { 267 printf("discarded %d packets\n", j); 268 } 269 if (dp->th_block == (block-1)) { 270 goto send_ack; /* resend ack */ 271 } 272 } 273 } 274 /* size = write(fd, dp->th_data, n - 4); */ 275 size = writeit(file, &dp, n - 4, convert); 276 if (size < 0) { 277 nak(errno + 100); 278 break; 279 } 280 amount += size; 281 } while (size == SEGSIZE); 282 abort: /* ok to ack, since user */ 283 ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 284 ap->th_block = htons((u_short)block); 285 (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, 286 sizeof(peeraddr)); 287 write_behind(file, convert); /* flush last buffer */ 288 fclose(file); 289 stopclock(); 290 if (amount > 0) 291 printstats("Received", amount); 292 } 293 294 static int 295 makerequest(int request, const char *name, struct tftphdr *tp, 296 const char *mode) 297 { 298 char *cp; 299 int len, pktlen; 300 301 tp->th_opcode = htons((u_short)request); 302 cp = tp->th_stuff; 303 pktlen = PKTSIZE - offsetof(struct tftphdr, th_stuff); 304 len = strlen(name) + 1; 305 strlcpy(cp, name, pktlen); 306 strlcpy(cp + len, mode, pktlen - len); 307 len += strlen(mode) + 1; 308 return (cp + len - (char *)tp); 309 } 310 311 struct errmsg { 312 int e_code; 313 char *e_msg; 314 } errmsgs[] = { 315 { EUNDEF, "Undefined error code" }, 316 { ENOTFOUND, "File not found" }, 317 { EACCESS, "Access violation" }, 318 { ENOSPACE, "Disk full or allocation exceeded" }, 319 { EBADOP, "Illegal TFTP operation" }, 320 { EBADID, "Unknown transfer ID" }, 321 { EEXISTS, "File already exists" }, 322 { ENOUSER, "No such user" }, 323 { -1, NULL } 324 }; 325 326 /* 327 * Send a nak packet (error message). 328 * Error code passed in is one of the 329 * standard TFTP codes, or a UNIX errno 330 * offset by 100. 331 */ 332 static void 333 nak(int error) 334 { 335 struct errmsg *pe; 336 struct tftphdr *tp; 337 int length; 338 339 tp = (struct tftphdr *)ackbuf; 340 tp->th_opcode = htons((u_short)ERROR); 341 tp->th_code = htons((u_short)error); 342 for (pe = errmsgs; pe->e_code >= 0; pe++) 343 if (pe->e_code == error) 344 break; 345 if (pe->e_code < 0) { 346 pe->e_msg = strerror(error - 100); 347 tp->th_code = EUNDEF; 348 } 349 length = strlcpy(tp->th_msg, pe->e_msg, sizeof(ackbuf)) + 5; 350 if (length > sizeof(ackbuf)) 351 length = sizeof(ackbuf); 352 if (trace) 353 tpacket("sent", tp, length); 354 if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, 355 sizeof(peeraddr)) != length) 356 warn("nak"); 357 } 358 359 static void 360 tpacket(const char *s, struct tftphdr *tp, int n) 361 { 362 static char *opcodes[] = 363 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 364 char *cp, *file; 365 u_short op = ntohs(tp->th_opcode); 366 367 if (op < RRQ || op > ERROR) 368 printf("%s opcode=%x ", s, op); 369 else 370 printf("%s %s ", s, opcodes[op]); 371 switch (op) { 372 373 case RRQ: 374 case WRQ: 375 n -= 2; 376 file = cp = tp->th_stuff; 377 cp = strchr(cp, '\0'); 378 printf("<file=%s, mode=%s>\n", file, cp + 1); 379 break; 380 381 case DATA: 382 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 383 break; 384 385 case ACK: 386 printf("<block=%d>\n", ntohs(tp->th_block)); 387 break; 388 389 case ERROR: 390 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 391 break; 392 } 393 } 394 395 struct timeval tstart; 396 struct timeval tstop; 397 398 static void 399 startclock(void) 400 { 401 402 (void)gettimeofday(&tstart, NULL); 403 } 404 405 static void 406 stopclock(void) 407 { 408 409 (void)gettimeofday(&tstop, NULL); 410 } 411 412 static void 413 printstats(const char *direction, unsigned long amount) 414 { 415 double delta; 416 /* compute delta in 1/10's second units */ 417 delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 418 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 419 delta = delta/10.; /* back to seconds */ 420 printf("%s %lu bytes in %.1f seconds", direction, amount, delta); 421 if (verbose) 422 printf(" [%.0f bits/sec]", (amount*8.)/delta); 423 putchar('\n'); 424 } 425 426 static void 427 timer(int sig) 428 { 429 int save_errno = errno; 430 431 timeout += rexmtval; 432 if (timeout >= maxtimeout) { 433 printf("Transfer timed out.\n"); 434 errno = save_errno; 435 longjmp(toplevel, -1); 436 } 437 errno = save_errno; 438 longjmp(timeoutbuf, 1); 439 } 440