1 /* 2 * Copyright (c) 1983 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.1 (Berkeley) 06/06/85"; 9 #endif not lint 10 11 /* 12 * TFTP User Program -- Protocol Machines 13 */ 14 #include <sys/types.h> 15 #include <sys/socket.h> 16 17 #include <netinet/in.h> 18 #include <arpa/inet.h> 19 #include <arpa/tftp.h> 20 21 #include <signal.h> 22 #include <stdio.h> 23 #include <errno.h> 24 #include <setjmp.h> 25 #include <netdb.h> 26 27 extern int errno; 28 extern struct sockaddr_in sin; 29 extern char mode[]; 30 int f; 31 int trace; 32 int connected; 33 char sbuf[BUFSIZ]; /* send buffer */ 34 char rbuf[BUFSIZ]; /* receive buffer */ 35 int rexmtval; 36 int maxtimeout; 37 int timeout; 38 jmp_buf toplevel; 39 jmp_buf timeoutbuf; 40 41 timer() 42 { 43 44 timeout += rexmtval; 45 if (timeout >= maxtimeout) { 46 printf("Transfer timed out.\n"); 47 longjmp(toplevel, -1); 48 } 49 longjmp(timeoutbuf, 1); 50 } 51 52 /* 53 * Send the requested file. 54 */ 55 sendfile(fd, name) 56 int fd; 57 char *name; 58 { 59 register struct tftphdr *stp = (struct tftphdr *)sbuf; 60 register struct tftphdr *rtp = (struct tftphdr *)rbuf; 61 register int block = 0, size, n, amount = 0; 62 struct sockaddr_in from, to; 63 time_t start = time(0), delta; 64 int fromlen, aborted = 0; 65 66 to = sin; 67 signal(SIGALRM, timer); 68 do { 69 if (block == 0) 70 size = makerequest(WRQ, name) - 4; 71 else { 72 size = read(fd, stp->th_data, SEGSIZE); 73 if (size < 0) { 74 nak(&to, errno + 100); 75 break; 76 } 77 stp->th_opcode = htons((u_short)DATA); 78 stp->th_block = htons((u_short)block); 79 } 80 timeout = 0; 81 (void) setjmp(timeoutbuf); 82 if (trace) 83 tpacket("sent", &to, stp, size + 4); 84 n = sendto(f, sbuf, size + 4, 0, (caddr_t)&to, sizeof (to)); 85 if (n != size + 4) { 86 perror("tftp: sendto"); 87 aborted = 1; 88 goto done; 89 } 90 do { 91 again: 92 alarm(rexmtval); 93 do { 94 fromlen = sizeof (from); 95 n = recvfrom(f, rbuf, sizeof (rbuf), 0, 96 (caddr_t)&from, &fromlen); 97 } while (n <= 0); 98 alarm(0); 99 if (n < 0) { 100 perror("tftp: recvfrom"); 101 aborted = 1; 102 goto done; 103 } 104 if (to.sin_addr.s_addr != from.sin_addr.s_addr) { 105 tpacket("discarded (wrong host)", 106 &from, rtp, n); 107 goto again; 108 } 109 if (to.sin_port = sin.sin_port) 110 to.sin_port = from.sin_port; 111 if (to.sin_port != from.sin_port) { 112 tpacket("discarded (wrong port)", 113 &from, rtp, n); 114 goto again; 115 } 116 if (trace) 117 tpacket("received", &from, rtp, n); 118 /* should verify packet came from server */ 119 rtp->th_opcode = ntohs(rtp->th_opcode); 120 rtp->th_block = ntohs(rtp->th_block); 121 if (rtp->th_opcode == ERROR) { 122 printf("Error code %d: %s\n", rtp->th_code, 123 rtp->th_msg); 124 aborted = 1; 125 goto done; 126 } 127 } while (rtp->th_opcode != ACK && block != rtp->th_block); 128 if (block > 0) 129 amount += size; 130 block++; 131 } while (size == SEGSIZE || block == 1); 132 if (!aborted && amount > 0) { 133 delta = time(0) - start; 134 printf("Sent %d bytes in %d seconds.\n", amount, delta); 135 } 136 done: 137 (void) close(fd); 138 return (aborted); 139 } 140 141 /* 142 * Receive a file. 143 */ 144 recvfile(fd, name) 145 int fd; 146 char *name; 147 { 148 register struct tftphdr *stp = (struct tftphdr *)sbuf; 149 register struct tftphdr *rtp = (struct tftphdr *)rbuf; 150 register int block = 1, n, size, amount = 0; 151 struct sockaddr_in from, to; 152 time_t start = time(0), delta; 153 int fromlen, firsttrip = 1, aborted = 0; 154 155 to = sin; 156 signal(SIGALRM, timer); 157 do { 158 if (firsttrip) { 159 size = makerequest(RRQ, name); 160 firsttrip = 0; 161 } else { 162 stp->th_opcode = htons((u_short)ACK); 163 stp->th_block = htons((u_short)(block)); 164 size = 4; 165 block++; 166 } 167 timeout = 0; 168 (void) setjmp(timeoutbuf); 169 if (trace) 170 tpacket("sent", &to, stp, size); 171 if (sendto(f, sbuf, size, 0, (caddr_t)&to, 172 sizeof (to)) != size) { 173 alarm(0); 174 perror("tftp: sendto"); 175 aborted = 1; 176 goto done; 177 } 178 do { 179 again: 180 alarm(rexmtval); 181 do { 182 fromlen = sizeof (from); 183 n = recvfrom(f, rbuf, sizeof (rbuf), 0, 184 (caddr_t)&from, &fromlen); 185 } while (n <= 0); 186 alarm(0); 187 if (n < 0) { 188 perror("tftp: recvfrom"); 189 aborted = 1; 190 goto done; 191 } 192 if (to.sin_addr.s_addr != from.sin_addr.s_addr) { 193 tpacket("discarded (wrong host)", 194 &from, rtp, n); 195 goto again; 196 } 197 if (to.sin_port = sin.sin_port) 198 to.sin_port = from.sin_port; 199 if (to.sin_port != from.sin_port) { 200 tpacket("discarded (wrong port)", 201 &from, rtp, n); 202 goto again; 203 } 204 if (trace) 205 tpacket("received", &from, rtp, n); 206 rtp->th_opcode = ntohs(rtp->th_opcode); 207 rtp->th_block = ntohs(rtp->th_block); 208 if (rtp->th_opcode == ERROR) { 209 printf("Error code %d: %s\n", rtp->th_code, 210 rtp->th_msg); 211 aborted = 1; 212 goto done; 213 } 214 } while (rtp->th_opcode != DATA && rtp->th_block != block); 215 size = write(fd, rtp->th_data, n - 4); 216 if (size < 0) { 217 perror("tftp: write"); 218 nak(&to, errno + 100); 219 aborted = 1; 220 goto done; 221 } 222 amount += size; 223 } while (size == SEGSIZE); 224 done: 225 stp->th_opcode = htons((u_short)ACK); 226 stp->th_block = htons((u_short)block); 227 (void) sendto(f, sbuf, 4, 0, &to, sizeof (to)); 228 (void) close(fd); 229 if (!aborted && amount > 0) { 230 delta = time(0) - start; 231 printf("Received %d bytes in %d seconds.\n", amount, delta); 232 } 233 return (aborted); 234 } 235 236 makerequest(request, name) 237 int request; 238 char *name; 239 { 240 register struct tftphdr *stp; 241 int size; 242 register char *cp; 243 244 stp = (struct tftphdr *)sbuf; 245 stp->th_opcode = htons((u_short)request); 246 strcpy(stp->th_stuff, name); 247 size = strlen(name); 248 cp = stp->th_stuff + strlen(name); 249 *cp++ = '\0'; 250 strcpy(cp, mode); 251 cp += sizeof ("netascii") - 1; 252 *cp++ = '\0'; 253 return (cp - sbuf); 254 } 255 256 struct errmsg { 257 int e_code; 258 char *e_msg; 259 } errmsgs[] = { 260 { EUNDEF, "Undefined error code" }, 261 { ENOTFOUND, "File not found" }, 262 { EACCESS, "Access violation" }, 263 { ENOSPACE, "Disk full or allocation exceeded" }, 264 { EBADOP, "Illegal TFTP operation" }, 265 { EBADID, "Unknown transfer ID" }, 266 { EEXISTS, "File already exists" }, 267 { ENOUSER, "No such user" }, 268 { -1, 0 } 269 }; 270 271 /* 272 * Send a nak packet (error message). 273 * Error code passed in is one of the 274 * standard TFTP codes, or a UNIX errno 275 * offset by 100. 276 */ 277 nak(to, error) 278 struct sockaddr_in *to; 279 int error; 280 { 281 register struct tftphdr *stp; 282 int length; 283 register struct errmsg *pe; 284 extern char *sys_errlist[]; 285 286 stp = (struct tftphdr *)sbuf; 287 stp->th_opcode = htons((u_short)ERROR); 288 stp->th_code = htons((u_short)error); 289 for (pe = errmsgs; pe->e_code >= 0; pe++) 290 if (pe->e_code == error) 291 break; 292 if (pe->e_code < 0) 293 pe->e_msg = sys_errlist[error - 100]; 294 strcpy(stp->th_msg, pe->e_msg); 295 length = strlen(pe->e_msg) + 4; 296 if (trace) 297 tpacket("sent", to, stp, length); 298 if (sendto(f, sbuf, length, 0, to, sizeof (*to)) != length) 299 perror("tftp: nak"); 300 } 301 302 tpacket(s, sin, tp, n) 303 struct sockaddr_in *sin; 304 struct tftphdr *tp; 305 int n; 306 { 307 static char *opcodes[] = 308 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 309 register char *cp, *file; 310 u_short op = ntohs(tp->th_opcode); 311 char *index(); 312 313 printf("%s ", s); 314 if (sin) { 315 struct hostent *hp = gethostbyaddr(&sin->sin_addr, 316 sizeof (sin->sin_addr), AF_INET); 317 318 printf("%s.%d ", 319 hp == 0 ? inet_ntoa(sin->sin_addr) : hp->h_name, 320 ntohs(sin->sin_port)); 321 } 322 if (op < RRQ || op > ERROR) 323 printf("opcode=%x ", op); 324 else 325 printf("%s ", opcodes[op]); 326 switch (op) { 327 328 case RRQ: 329 case WRQ: 330 n -= 2; 331 file = cp = tp->th_stuff; 332 cp = index(cp, '\0'); 333 printf("<file=%s, mode=%s>\n", file, cp + 1); 334 break; 335 336 case DATA: 337 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 338 break; 339 340 case ACK: 341 printf("<block=%d>\n", ntohs(tp->th_block)); 342 break; 343 344 case ERROR: 345 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 346 break; 347 348 default: 349 putchar('\n'); 350 break; 351 } 352 } 353