1 /* $OpenBSD: tftp.c,v 1.15 2003/09/24 20:21:40 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.15 2003/09/24 20:21:40 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", j); 169 if (ap->th_block == (block-1)) 170 goto send_data; 171 } 172 } 173 if (block > 0) 174 amount += size; 175 block++; 176 } while (size == SEGSIZE || block == 1); 177 abort: 178 fclose(file); 179 stopclock(); 180 if (amount > 0) 181 printstats("Sent", amount); 182 } 183 184 /* 185 * Receive a file. 186 */ 187 void 188 recvfile(int fd, char *name, char *mode) 189 { 190 struct tftphdr *dp, *ap; 191 volatile int block, size, firsttrip; 192 volatile unsigned long amount; 193 struct sockaddr_in from; 194 int n, fromlen; 195 FILE *file; 196 volatile int convert; /* true if converting crlf -> lf */ 197 198 startclock(); 199 dp = w_init(); 200 ap = (struct tftphdr *)ackbuf; 201 file = fdopen(fd, "w"); 202 convert = !strcmp(mode, "netascii"); 203 block = 1; 204 firsttrip = 1; 205 amount = 0; 206 207 signal(SIGALRM, timer); 208 do { 209 if (firsttrip) { 210 size = makerequest(RRQ, name, ap, mode); 211 firsttrip = 0; 212 } else { 213 ap->th_opcode = htons((u_short)ACK); 214 ap->th_block = htons((u_short)(block)); 215 size = 4; 216 block++; 217 } 218 timeout = 0; 219 (void) setjmp(timeoutbuf); 220 send_ack: 221 if (trace) 222 tpacket("sent", ap, size); 223 if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr, 224 sizeof(peeraddr)) != size) { 225 alarm(0); 226 warn("sendto"); 227 goto abort; 228 } 229 write_behind(file, convert); 230 for ( ; ; ) { 231 alarm(rexmtval); 232 do { 233 fromlen = sizeof(from); 234 n = recvfrom(f, dp, PKTSIZE, 0, 235 (struct sockaddr *)&from, &fromlen); 236 } while (n <= 0); 237 alarm(0); 238 if (n < 0) { 239 warn("recvfrom"); 240 goto abort; 241 } 242 peeraddr.sin_port = from.sin_port; /* added */ 243 if (trace) 244 tpacket("received", dp, n); 245 /* should verify client address */ 246 dp->th_opcode = ntohs(dp->th_opcode); 247 dp->th_block = ntohs(dp->th_block); 248 if (dp->th_opcode == ERROR) { 249 printf("Error code %d: %s\n", dp->th_code, 250 dp->th_msg); 251 goto abort; 252 } 253 if (dp->th_opcode == DATA) { 254 int j; 255 256 if (dp->th_block == block) { 257 break; /* have next packet */ 258 } 259 /* On an error, try to synchronize 260 * both sides. 261 */ 262 j = synchnet(f); 263 if (j && trace) 264 printf("discarded %d packets\n", j); 265 if (dp->th_block == (block-1)) 266 goto send_ack; /* resend ack */ 267 } 268 } 269 /* size = write(fd, dp->th_data, n - 4); */ 270 size = writeit(file, &dp, n - 4, convert); 271 if (size < 0) { 272 nak(errno + 100); 273 break; 274 } 275 amount += size; 276 } while (size == SEGSIZE); 277 abort: /* ok to ack, since user */ 278 ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 279 ap->th_block = htons((u_short)block); 280 (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, 281 sizeof(peeraddr)); 282 write_behind(file, convert); /* flush last buffer */ 283 fclose(file); 284 stopclock(); 285 if (amount > 0) 286 printstats("Received", amount); 287 } 288 289 static int 290 makerequest(int request, const char *name, struct tftphdr *tp, 291 const char *mode) 292 { 293 char *cp; 294 int len, pktlen; 295 296 tp->th_opcode = htons((u_short)request); 297 cp = tp->th_stuff; 298 pktlen = PKTSIZE - offsetof(struct tftphdr, th_stuff); 299 len = strlen(name) + 1; 300 strlcpy(cp, name, pktlen); 301 strlcpy(cp + len, mode, pktlen - len); 302 len += strlen(mode) + 1; 303 return (cp + len - (char *)tp); 304 } 305 306 struct errmsg { 307 int e_code; 308 char *e_msg; 309 } errmsgs[] = { 310 { EUNDEF, "Undefined error code" }, 311 { ENOTFOUND, "File not found" }, 312 { EACCESS, "Access violation" }, 313 { ENOSPACE, "Disk full or allocation exceeded" }, 314 { EBADOP, "Illegal TFTP operation" }, 315 { EBADID, "Unknown transfer ID" }, 316 { EEXISTS, "File already exists" }, 317 { ENOUSER, "No such user" }, 318 { -1, NULL } 319 }; 320 321 /* 322 * Send a nak packet (error message). 323 * Error code passed in is one of the 324 * standard TFTP codes, or a UNIX errno 325 * offset by 100. 326 */ 327 static void 328 nak(int error) 329 { 330 struct errmsg *pe; 331 struct tftphdr *tp; 332 int length; 333 334 tp = (struct tftphdr *)ackbuf; 335 tp->th_opcode = htons((u_short)ERROR); 336 tp->th_code = htons((u_short)error); 337 for (pe = errmsgs; pe->e_code >= 0; pe++) 338 if (pe->e_code == error) 339 break; 340 if (pe->e_code < 0) { 341 pe->e_msg = strerror(error - 100); 342 tp->th_code = EUNDEF; 343 } 344 length = strlcpy(tp->th_msg, pe->e_msg, sizeof(ackbuf)) + 5; 345 if (length > sizeof(ackbuf)) 346 length = sizeof(ackbuf); 347 if (trace) 348 tpacket("sent", tp, length); 349 if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, 350 sizeof(peeraddr)) != length) 351 warn("nak"); 352 } 353 354 static void 355 tpacket(const char *s, struct tftphdr *tp, int n) 356 { 357 static char *opcodes[] = 358 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 359 char *cp, *file; 360 u_short op = ntohs(tp->th_opcode); 361 362 if (op < RRQ || op > ERROR) 363 printf("%s opcode=%x ", s, op); 364 else 365 printf("%s %s ", s, opcodes[op]); 366 switch (op) { 367 368 case RRQ: 369 case WRQ: 370 n -= 2; 371 file = cp = tp->th_stuff; 372 cp = strchr(cp, '\0'); 373 printf("<file=%s, mode=%s>\n", file, cp + 1); 374 break; 375 376 case DATA: 377 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 378 break; 379 380 case ACK: 381 printf("<block=%d>\n", ntohs(tp->th_block)); 382 break; 383 384 case ERROR: 385 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 386 break; 387 } 388 } 389 390 struct timeval tstart; 391 struct timeval tstop; 392 393 static void 394 startclock(void) 395 { 396 397 (void)gettimeofday(&tstart, NULL); 398 } 399 400 static void 401 stopclock(void) 402 { 403 404 (void)gettimeofday(&tstop, NULL); 405 } 406 407 static void 408 printstats(const char *direction, unsigned long amount) 409 { 410 double delta; 411 412 /* compute delta in 1/10's second units */ 413 delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 414 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 415 delta = delta/10.; /* back to seconds */ 416 printf("%s %lu bytes in %.1f seconds", direction, amount, delta); 417 if (verbose) 418 printf(" [%.0f bits/sec]", (amount*8.)/delta); 419 putchar('\n'); 420 } 421 422 static void 423 timer(int sig) 424 { 425 int save_errno = errno; 426 427 timeout += rexmtval; 428 if (timeout >= maxtimeout) { 429 printf("Transfer timed out.\n"); 430 errno = save_errno; 431 longjmp(toplevel, -1); 432 } 433 errno = save_errno; 434 longjmp(timeoutbuf, 1); 435 } 436