1 /* 2 * Copyright (c) 1983 Regents of the University of California. 3 * 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 #ifndef lint 35 /*static char sccsid[] = "from: @(#)tftp.c 5.10 (Berkeley) 3/1/91";*/ 36 static char rcsid[] = "$Id: tftp.c,v 1.3 1993/08/01 18:07:06 mycroft Exp $"; 37 #endif /* not lint */ 38 39 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 40 41 /* 42 * TFTP User Program -- Protocol Machines 43 */ 44 #include <sys/types.h> 45 #include <sys/socket.h> 46 #include <sys/time.h> 47 48 #include <netinet/in.h> 49 50 #include <arpa/tftp.h> 51 52 #include <signal.h> 53 #include <stdio.h> 54 #include <errno.h> 55 #include <setjmp.h> 56 57 extern int errno; 58 59 extern struct sockaddr_in s_in; /* filled in by main */ 60 extern int f; /* the opened socket */ 61 extern int trace; 62 extern int verbose; 63 extern int rexmtval; 64 extern int maxtimeout; 65 66 #define PKTSIZE SEGSIZE+4 67 char ackbuf[PKTSIZE]; 68 int timeout; 69 jmp_buf toplevel; 70 jmp_buf timeoutbuf; 71 72 void 73 timer() 74 { 75 timeout += rexmtval; 76 if (timeout >= maxtimeout) { 77 printf("Transfer timed out.\n"); 78 longjmp(toplevel, -1); 79 } 80 longjmp(timeoutbuf, 1); 81 } 82 83 /* 84 * Send the requested file. 85 */ 86 sendfile(fd, name, mode) 87 int fd; 88 char *name; 89 char *mode; 90 { 91 register struct tftphdr *ap; /* data and ack packets */ 92 struct tftphdr *r_init(), *dp; 93 register int block = 0, size, n; 94 register unsigned long amount = 0; 95 struct sockaddr_in from; 96 int fromlen; 97 int convert; /* true if doing nl->crlf conversion */ 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 106 signal(SIGALRM, timer); 107 do { 108 if (block == 0) 109 size = makerequest(WRQ, name, dp, mode) - 4; 110 else { 111 /* size = read(fd, dp->th_data, SEGSIZE); */ 112 size = readit(file, &dp, convert); 113 if (size < 0) { 114 nak(errno + 100); 115 break; 116 } 117 dp->th_opcode = htons((u_short)DATA); 118 dp->th_block = htons((u_short)block); 119 } 120 timeout = 0; 121 (void) setjmp(timeoutbuf); 122 send_data: 123 if (trace) 124 tpacket("sent", dp, size + 4); 125 n = sendto(f, dp, size + 4, 0, 126 (struct sockaddr *)&s_in, sizeof (s_in)); 127 if (n != size + 4) { 128 perror("tftp: sendto"); 129 goto abort; 130 } 131 read_ahead(file, convert); 132 for ( ; ; ) { 133 alarm(rexmtval); 134 do { 135 fromlen = sizeof (from); 136 n = recvfrom(f, ackbuf, sizeof (ackbuf), 0, 137 (struct sockaddr *)&from, &fromlen); 138 } while (n <= 0); 139 alarm(0); 140 if (n < 0) { 141 perror("tftp: recvfrom"); 142 goto abort; 143 } 144 s_in.sin_port = from.sin_port; /* added */ 145 if (trace) 146 tpacket("received", ap, n); 147 /* should verify packet came from server */ 148 ap->th_opcode = ntohs(ap->th_opcode); 149 ap->th_block = ntohs(ap->th_block); 150 if (ap->th_opcode == ERROR) { 151 printf("Error code %d: %s\n", ap->th_code, 152 ap->th_msg); 153 goto abort; 154 } 155 if (ap->th_opcode == ACK) { 156 int j; 157 158 if (ap->th_block == block) { 159 break; 160 } 161 /* On an error, try to synchronize 162 * both sides. 163 */ 164 j = synchnet(f); 165 if (j && trace) { 166 printf("discarded %d packets\n", 167 j); 168 } 169 if (ap->th_block == (block-1)) { 170 goto send_data; 171 } 172 } 173 } 174 if (block > 0) 175 amount += size; 176 block++; 177 } while (size == SEGSIZE || block == 1); 178 abort: 179 fclose(file); 180 stopclock(); 181 if (amount > 0) 182 printstats("Sent", amount); 183 } 184 185 /* 186 * Receive a file. 187 */ 188 recvfile(fd, name, mode) 189 int fd; 190 char *name; 191 char *mode; 192 { 193 register struct tftphdr *ap; 194 struct tftphdr *dp, *w_init(); 195 register int block = 1, n, size; 196 unsigned long amount = 0; 197 struct sockaddr_in from; 198 int fromlen, firsttrip = 1; 199 FILE *file; 200 int convert; /* true if converting crlf -> lf */ 201 202 startclock(); 203 dp = w_init(); 204 ap = (struct tftphdr *)ackbuf; 205 file = fdopen(fd, "w"); 206 convert = !strcmp(mode, "netascii"); 207 208 signal(SIGALRM, timer); 209 do { 210 if (firsttrip) { 211 size = makerequest(RRQ, name, ap, mode); 212 firsttrip = 0; 213 } else { 214 ap->th_opcode = htons((u_short)ACK); 215 ap->th_block = htons((u_short)(block)); 216 size = 4; 217 block++; 218 } 219 timeout = 0; 220 (void) setjmp(timeoutbuf); 221 send_ack: 222 if (trace) 223 tpacket("sent", ap, size); 224 if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&s_in, 225 sizeof (s_in)) != size) { 226 alarm(0); 227 perror("tftp: sendto"); 228 goto abort; 229 } 230 write_behind(file, convert); 231 for ( ; ; ) { 232 alarm(rexmtval); 233 do { 234 fromlen = sizeof (from); 235 n = recvfrom(f, dp, PKTSIZE, 0, 236 (struct sockaddr *)&from, &fromlen); 237 } while (n <= 0); 238 alarm(0); 239 if (n < 0) { 240 perror("tftp: recvfrom"); 241 goto abort; 242 } 243 s_in.sin_port = from.sin_port; /* added */ 244 if (trace) 245 tpacket("received", dp, n); 246 /* should verify client address */ 247 dp->th_opcode = ntohs(dp->th_opcode); 248 dp->th_block = ntohs(dp->th_block); 249 if (dp->th_opcode == ERROR) { 250 printf("Error code %d: %s\n", dp->th_code, 251 dp->th_msg); 252 goto abort; 253 } 254 if (dp->th_opcode == DATA) { 255 int j; 256 257 if (dp->th_block == block) { 258 break; /* have next packet */ 259 } 260 /* On an error, try to synchronize 261 * both sides. 262 */ 263 j = synchnet(f); 264 if (j && trace) { 265 printf("discarded %d packets\n", j); 266 } 267 if (dp->th_block == (block-1)) { 268 goto send_ack; /* resend ack */ 269 } 270 } 271 } 272 /* size = write(fd, dp->th_data, n - 4); */ 273 size = writeit(file, &dp, n - 4, convert); 274 if (size < 0) { 275 nak(errno + 100); 276 break; 277 } 278 amount += size; 279 } while (size == SEGSIZE); 280 abort: /* ok to ack, since user */ 281 ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 282 ap->th_block = htons((u_short)block); 283 (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&s_in, sizeof (s_in)); 284 write_behind(file, convert); /* flush last buffer */ 285 fclose(file); 286 stopclock(); 287 if (amount > 0) 288 printstats("Received", amount); 289 } 290 291 makerequest(request, name, tp, mode) 292 int request; 293 char *name, *mode; 294 struct tftphdr *tp; 295 { 296 register char *cp; 297 298 tp->th_opcode = htons((u_short)request); 299 cp = tp->th_stuff; 300 strcpy(cp, name); 301 cp += strlen(name); 302 *cp++ = '\0'; 303 strcpy(cp, mode); 304 cp += strlen(mode); 305 *cp++ = '\0'; 306 return (cp - (char *)tp); 307 } 308 309 struct errmsg { 310 int e_code; 311 char *e_msg; 312 } errmsgs[] = { 313 { EUNDEF, "Undefined error code" }, 314 { ENOTFOUND, "File not found" }, 315 { EACCESS, "Access violation" }, 316 { ENOSPACE, "Disk full or allocation exceeded" }, 317 { EBADOP, "Illegal TFTP operation" }, 318 { EBADID, "Unknown transfer ID" }, 319 { EEXISTS, "File already exists" }, 320 { ENOUSER, "No such user" }, 321 { -1, 0 } 322 }; 323 324 /* 325 * Send a nak packet (error message). 326 * Error code passed in is one of the 327 * standard TFTP codes, or a UNIX errno 328 * offset by 100. 329 */ 330 nak(error) 331 int error; 332 { 333 register struct errmsg *pe; 334 register struct tftphdr *tp; 335 int length; 336 char *strerror(); 337 338 tp = (struct tftphdr *)ackbuf; 339 tp->th_opcode = htons((u_short)ERROR); 340 tp->th_code = htons((u_short)error); 341 for (pe = errmsgs; pe->e_code >= 0; pe++) 342 if (pe->e_code == error) 343 break; 344 if (pe->e_code < 0) { 345 pe->e_msg = strerror(error - 100); 346 tp->th_code = EUNDEF; 347 } 348 strcpy(tp->th_msg, pe->e_msg); 349 length = strlen(pe->e_msg) + 4; 350 if (trace) 351 tpacket("sent", tp, length); 352 if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&s_in, 353 sizeof (s_in)) != length) 354 perror("nak"); 355 } 356 357 tpacket(s, tp, n) 358 char *s; 359 struct tftphdr *tp; 360 int n; 361 { 362 static char *opcodes[] = 363 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 364 register char *cp, *file; 365 u_short op = ntohs(tp->th_opcode); 366 char *index(); 367 368 if (op < RRQ || op > ERROR) 369 printf("%s opcode=%x ", s, op); 370 else 371 printf("%s %s ", s, opcodes[op]); 372 switch (op) { 373 374 case RRQ: 375 case WRQ: 376 n -= 2; 377 file = cp = tp->th_stuff; 378 cp = index(cp, '\0'); 379 printf("<file=%s, mode=%s>\n", file, cp + 1); 380 break; 381 382 case DATA: 383 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 384 break; 385 386 case ACK: 387 printf("<block=%d>\n", ntohs(tp->th_block)); 388 break; 389 390 case ERROR: 391 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 392 break; 393 } 394 } 395 396 struct timeval tstart; 397 struct timeval tstop; 398 struct timezone zone; 399 400 startclock() { 401 gettimeofday(&tstart, &zone); 402 } 403 404 stopclock() { 405 gettimeofday(&tstop, &zone); 406 } 407 408 printstats(direction, amount) 409 char *direction; 410 unsigned long amount; 411 { 412 double delta; 413 /* compute delta in 1/10's second units */ 414 delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 415 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 416 delta = delta/10.; /* back to seconds */ 417 printf("%s %d bytes in %.1f seconds", direction, amount, delta); 418 if (verbose) 419 printf(" [%.0f bits/sec]", (amount*8.)/delta); 420 putchar('\n'); 421 } 422 423