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