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