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