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