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