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[] = "@(#)tftp.c 5.10 (Berkeley) 3/1/91"; 36 #endif /* not lint */ 37 38 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 39 40 /* 41 * TFTP User Program -- Protocol Machines 42 */ 43 #include <sys/types.h> 44 #include <sys/socket.h> 45 #include <sys/time.h> 46 47 #include <netinet/in.h> 48 49 #include <arpa/tftp.h> 50 51 #include <signal.h> 52 #include <stdio.h> 53 #include <errno.h> 54 #include <setjmp.h> 55 56 extern int errno; 57 58 extern struct sockaddr_in s_in; /* filled in by main */ 59 extern int f; /* the opened socket */ 60 extern int trace; 61 extern int verbose; 62 extern int rexmtval; 63 extern int maxtimeout; 64 65 #define PKTSIZE SEGSIZE+4 66 char ackbuf[PKTSIZE]; 67 int timeout; 68 jmp_buf toplevel; 69 jmp_buf timeoutbuf; 70 71 void 72 timer() 73 { 74 timeout += rexmtval; 75 if (timeout >= maxtimeout) { 76 printf("Transfer timed out.\n"); 77 longjmp(toplevel, -1); 78 } 79 longjmp(timeoutbuf, 1); 80 } 81 82 /* 83 * Send the requested file. 84 */ 85 sendfile(fd, name, mode) 86 int fd; 87 char *name; 88 char *mode; 89 { 90 register struct tftphdr *ap; /* data and ack packets */ 91 struct tftphdr *r_init(), *dp; 92 register int block = 0, size, n; 93 register unsigned long amount = 0; 94 struct sockaddr_in from; 95 int fromlen; 96 int convert; /* true if doing nl->crlf conversion */ 97 FILE *file; 98 99 startclock(); /* start stat's clock */ 100 dp = r_init(); /* reset fillbuf/read-ahead code */ 101 ap = (struct tftphdr *)ackbuf; 102 file = fdopen(fd, "r"); 103 convert = !strcmp(mode, "netascii"); 104 105 signal(SIGALRM, timer); 106 do { 107 if (block == 0) 108 size = makerequest(WRQ, name, dp, mode) - 4; 109 else { 110 /* size = read(fd, dp->th_data, SEGSIZE); */ 111 size = readit(file, &dp, convert); 112 if (size < 0) { 113 nak(errno + 100); 114 break; 115 } 116 dp->th_opcode = htons((u_short)DATA); 117 dp->th_block = htons((u_short)block); 118 } 119 timeout = 0; 120 (void) setjmp(timeoutbuf); 121 send_data: 122 if (trace) 123 tpacket("sent", dp, size + 4); 124 n = sendto(f, dp, size + 4, 0, 125 (struct sockaddr *)&s_in, sizeof (s_in)); 126 if (n != size + 4) { 127 perror("tftp: sendto"); 128 goto abort; 129 } 130 read_ahead(file, convert); 131 for ( ; ; ) { 132 alarm(rexmtval); 133 do { 134 fromlen = sizeof (from); 135 n = recvfrom(f, ackbuf, sizeof (ackbuf), 0, 136 (struct sockaddr *)&from, &fromlen); 137 } while (n <= 0); 138 alarm(0); 139 if (n < 0) { 140 perror("tftp: recvfrom"); 141 goto abort; 142 } 143 s_in.sin_port = from.sin_port; /* added */ 144 if (trace) 145 tpacket("received", ap, n); 146 /* should verify packet came from server */ 147 ap->th_opcode = ntohs(ap->th_opcode); 148 ap->th_block = ntohs(ap->th_block); 149 if (ap->th_opcode == ERROR) { 150 printf("Error code %d: %s\n", ap->th_code, 151 ap->th_msg); 152 goto abort; 153 } 154 if (ap->th_opcode == ACK) { 155 int j; 156 157 if (ap->th_block == block) { 158 break; 159 } 160 /* On an error, try to synchronize 161 * both sides. 162 */ 163 j = synchnet(f); 164 if (j && trace) { 165 printf("discarded %d packets\n", 166 j); 167 } 168 if (ap->th_block == (block-1)) { 169 goto send_data; 170 } 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 recvfile(fd, name, mode) 188 int fd; 189 char *name; 190 char *mode; 191 { 192 register struct tftphdr *ap; 193 struct tftphdr *dp, *w_init(); 194 register int block = 1, n, size; 195 unsigned long amount = 0; 196 struct sockaddr_in from; 197 int fromlen, firsttrip = 1; 198 FILE *file; 199 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 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 *)&s_in, 224 sizeof (s_in)) != size) { 225 alarm(0); 226 perror("tftp: 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 perror("tftp: recvfrom"); 240 goto abort; 241 } 242 s_in.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 } 266 if (dp->th_block == (block-1)) { 267 goto send_ack; /* resend ack */ 268 } 269 } 270 } 271 /* size = write(fd, dp->th_data, n - 4); */ 272 size = writeit(file, &dp, n - 4, convert); 273 if (size < 0) { 274 nak(errno + 100); 275 break; 276 } 277 amount += size; 278 } while (size == SEGSIZE); 279 abort: /* ok to ack, since user */ 280 ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 281 ap->th_block = htons((u_short)block); 282 (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&s_in, sizeof (s_in)); 283 write_behind(file, convert); /* flush last buffer */ 284 fclose(file); 285 stopclock(); 286 if (amount > 0) 287 printstats("Received", amount); 288 } 289 290 makerequest(request, name, tp, mode) 291 int request; 292 char *name, *mode; 293 struct tftphdr *tp; 294 { 295 register char *cp; 296 297 tp->th_opcode = htons((u_short)request); 298 cp = tp->th_stuff; 299 strcpy(cp, name); 300 cp += strlen(name); 301 *cp++ = '\0'; 302 strcpy(cp, mode); 303 cp += strlen(mode); 304 *cp++ = '\0'; 305 return (cp - (char *)tp); 306 } 307 308 struct errmsg { 309 int e_code; 310 char *e_msg; 311 } errmsgs[] = { 312 { EUNDEF, "Undefined error code" }, 313 { ENOTFOUND, "File not found" }, 314 { EACCESS, "Access violation" }, 315 { ENOSPACE, "Disk full or allocation exceeded" }, 316 { EBADOP, "Illegal TFTP operation" }, 317 { EBADID, "Unknown transfer ID" }, 318 { EEXISTS, "File already exists" }, 319 { ENOUSER, "No such user" }, 320 { -1, 0 } 321 }; 322 323 /* 324 * Send a nak packet (error message). 325 * Error code passed in is one of the 326 * standard TFTP codes, or a UNIX errno 327 * offset by 100. 328 */ 329 nak(error) 330 int error; 331 { 332 register struct errmsg *pe; 333 register struct tftphdr *tp; 334 int length; 335 char *strerror(); 336 337 tp = (struct tftphdr *)ackbuf; 338 tp->th_opcode = htons((u_short)ERROR); 339 tp->th_code = htons((u_short)error); 340 for (pe = errmsgs; pe->e_code >= 0; pe++) 341 if (pe->e_code == error) 342 break; 343 if (pe->e_code < 0) { 344 pe->e_msg = strerror(error - 100); 345 tp->th_code = EUNDEF; 346 } 347 strcpy(tp->th_msg, pe->e_msg); 348 length = strlen(pe->e_msg) + 4; 349 if (trace) 350 tpacket("sent", tp, length); 351 if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&s_in, 352 sizeof (s_in)) != length) 353 perror("nak"); 354 } 355 356 tpacket(s, tp, n) 357 char *s; 358 struct tftphdr *tp; 359 int n; 360 { 361 static char *opcodes[] = 362 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 363 register char *cp, *file; 364 u_short op = ntohs(tp->th_opcode); 365 char *index(); 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 = index(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 struct timezone zone; 398 399 startclock() { 400 gettimeofday(&tstart, &zone); 401 } 402 403 stopclock() { 404 gettimeofday(&tstop, &zone); 405 } 406 407 printstats(direction, amount) 408 char *direction; 409 unsigned long amount; 410 { 411 double delta; 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 %d bytes in %.1f seconds", direction, amount, delta); 417 if (verbose) 418 printf(" [%.0f bits/sec]", (amount*8.)/delta); 419 putchar('\n'); 420 } 421 422