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.2 (Berkeley) 02/06/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 if (trace) 95 tpacket("sent", dp, size + 4); 96 n = sendto(f, dp, size + 4, 0, (caddr_t)&sin, sizeof (sin)); 97 if (n != size + 4) { 98 perror("tftp: sendto"); 99 goto abort; 100 } 101 read_ahead(file, convert); 102 do { 103 alarm(rexmtval); 104 do { 105 fromlen = sizeof (from); 106 n = recvfrom(f, ackbuf, sizeof (ackbuf), 0, 107 (caddr_t)&from, &fromlen); 108 } while (n <= 0); 109 alarm(0); 110 if (n < 0) { 111 perror("tftp: recvfrom"); 112 goto abort; 113 } 114 sin.sin_port = from.sin_port; /* added */ 115 if (trace) 116 tpacket("received", ap, n); 117 /* should verify packet came from server */ 118 ap->th_opcode = ntohs(ap->th_opcode); 119 ap->th_block = ntohs(ap->th_block); 120 if (ap->th_opcode == ERROR) { 121 printf("Error code %d: %s\n", ap->th_code, 122 ap->th_msg); 123 goto abort; 124 } 125 } while (ap->th_opcode != ACK || block != ap->th_block); 126 if (block > 0) 127 amount += size; 128 block++; 129 } while (size == SEGSIZE || block == 1); 130 abort: 131 fclose(file); 132 stopclock(); 133 if (amount > 0) 134 printstats("Sent", amount); 135 } 136 137 /* 138 * Receive a file. 139 */ 140 recvfile(fd, name, mode) 141 int fd; 142 char *name; 143 char *mode; 144 { 145 register struct tftphdr *ap; 146 struct tftphdr *dp, *w_init(); 147 register int block = 1, n, size; 148 unsigned long amount = 0; 149 struct sockaddr_in from; 150 int fromlen, firsttrip = 1; 151 FILE *file; 152 int convert; /* true if converting crlf -> lf */ 153 154 startclock(); 155 dp = w_init(); 156 ap = (struct tftphdr *)ackbuf; 157 file = fdopen(fd, "w"); 158 convert = !strcmp(mode, "netascii"); 159 160 signal(SIGALRM, timer); 161 do { 162 if (firsttrip) { 163 size = makerequest(RRQ, name, ap, mode); 164 firsttrip = 0; 165 } else { 166 ap->th_opcode = htons((u_short)ACK); 167 ap->th_block = htons((u_short)(block)); 168 size = 4; 169 block++; 170 } 171 timeout = 0; 172 (void) setjmp(timeoutbuf); 173 send_ack: 174 if (trace) 175 tpacket("sent", ap, size); 176 if (sendto(f, ackbuf, size, 0, (caddr_t)&sin, 177 sizeof (sin)) != size) { 178 alarm(0); 179 perror("tftp: sendto"); 180 goto abort; 181 } 182 write_behind(file, convert); 183 for ( ; ; ) { 184 alarm(rexmtval); 185 do { 186 fromlen = sizeof (from); 187 n = recvfrom(f, dp, PKTSIZE, 0, 188 (caddr_t)&from, &fromlen); 189 } while (n <= 0); 190 alarm(0); 191 if (n < 0) { 192 perror("tftp: recvfrom"); 193 goto abort; 194 } 195 sin.sin_port = from.sin_port; /* added */ 196 if (trace) 197 tpacket("received", dp, n); 198 /* should verify client address */ 199 dp->th_opcode = ntohs(dp->th_opcode); 200 dp->th_block = ntohs(dp->th_block); 201 if (dp->th_opcode == ERROR) { 202 printf("Error code %d: %s\n", dp->th_code, 203 dp->th_msg); 204 goto abort; 205 } 206 if (dp->th_opcode == DATA) { 207 if (dp->th_block == block) 208 break; /* have next packet */ 209 if (dp->th_block == (block-1)) 210 goto send_ack; /* resend ack */ 211 } 212 } 213 /* size = write(fd, dp->th_data, n - 4); */ 214 size = writeit(file, &dp, n - 4, convert); 215 if (size < 0) { 216 nak(errno + 100); 217 break; 218 } 219 amount += size; 220 } while (size == SEGSIZE); 221 abort: /* ok to ack, since user */ 222 ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 223 ap->th_block = htons((u_short)block); 224 (void) sendto(f, ackbuf, 4, 0, &sin, sizeof (sin)); 225 write_behind(file, convert); /* flush last buffer */ 226 fclose(file); 227 stopclock(); 228 if (amount > 0) 229 printstats("Received", amount); 230 } 231 232 makerequest(request, name, tp, mode) 233 int request; 234 char *name, *mode; 235 struct tftphdr *tp; 236 { 237 register char *cp; 238 239 tp->th_opcode = htons((u_short)request); 240 cp = tp->th_stuff; 241 strcpy(cp, name); 242 cp += strlen(name); 243 *cp++ = '\0'; 244 strcpy(cp, mode); 245 cp += strlen(mode); 246 *cp++ = '\0'; 247 return (cp - (char *)tp); 248 } 249 250 struct errmsg { 251 int e_code; 252 char *e_msg; 253 } errmsgs[] = { 254 { EUNDEF, "Undefined error code" }, 255 { ENOTFOUND, "File not found" }, 256 { EACCESS, "Access violation" }, 257 { ENOSPACE, "Disk full or allocation exceeded" }, 258 { EBADOP, "Illegal TFTP operation" }, 259 { EBADID, "Unknown transfer ID" }, 260 { EEXISTS, "File already exists" }, 261 { ENOUSER, "No such user" }, 262 { -1, 0 } 263 }; 264 265 /* 266 * Send a nak packet (error message). 267 * Error code passed in is one of the 268 * standard TFTP codes, or a UNIX errno 269 * offset by 100. 270 */ 271 nak(error) 272 int error; 273 { 274 register struct tftphdr *tp; 275 int length; 276 register struct errmsg *pe; 277 extern char *sys_errlist[]; 278 279 tp = (struct tftphdr *)ackbuf; 280 tp->th_opcode = htons((u_short)ERROR); 281 tp->th_code = htons((u_short)error); 282 for (pe = errmsgs; pe->e_code >= 0; pe++) 283 if (pe->e_code == error) 284 break; 285 if (pe->e_code < 0) { 286 pe->e_msg = sys_errlist[error - 100]; 287 tp->th_code = EUNDEF; 288 } 289 strcpy(tp->th_msg, pe->e_msg); 290 length = strlen(pe->e_msg) + 4; 291 if (trace) 292 tpacket("sent", tp, length); 293 if (sendto(f, ackbuf, length, 0, &sin, sizeof (sin)) != length) 294 perror("nak"); 295 } 296 297 tpacket(s, tp, n) 298 struct tftphdr *tp; 299 int n; 300 { 301 static char *opcodes[] = 302 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 303 register char *cp, *file; 304 u_short op = ntohs(tp->th_opcode); 305 char *index(); 306 307 if (op < RRQ || op > ERROR) 308 printf("%s opcode=%x ", s, op); 309 else 310 printf("%s %s ", s, opcodes[op]); 311 switch (op) { 312 313 case RRQ: 314 case WRQ: 315 n -= 2; 316 file = cp = tp->th_stuff; 317 cp = index(cp, '\0'); 318 printf("<file=%s, mode=%s>\n", file, cp + 1); 319 break; 320 321 case DATA: 322 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 323 break; 324 325 case ACK: 326 printf("<block=%d>\n", ntohs(tp->th_block)); 327 break; 328 329 case ERROR: 330 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 331 break; 332 } 333 } 334 335 struct timeval tstart; 336 struct timeval tstop; 337 struct timezone zone; 338 339 startclock() { 340 gettimeofday(&tstart, &zone); 341 } 342 343 stopclock() { 344 gettimeofday(&tstop, &zone); 345 } 346 347 printstats(direction, amount) 348 char *direction; 349 unsigned long amount; 350 { 351 double delta; 352 /* compute delta in 1/10's second units */ 353 delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 354 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 355 delta = delta/10.; /* back to seconds */ 356 printf("%s %d bytes in %.1f seconds", direction, amount, delta); 357 if (verbose) 358 printf(" [%.0f bits/sec]", (amount*8.)/delta); 359 putchar('\n'); 360 } 361 362