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