1 #ifndef lint 2 static char sccsid[] = "@(#)tftpd.c 4.12 (Berkeley) 04/11/84"; 3 #endif 4 5 /* 6 * Trivial file transfer protocol server. 7 */ 8 #include <sys/types.h> 9 #include <sys/socket.h> 10 #include <sys/ioctl.h> 11 #include <sys/wait.h> 12 #include <sys/stat.h> 13 14 #include <netinet/in.h> 15 16 #include <arpa/tftp.h> 17 18 #include <signal.h> 19 #include <stdio.h> 20 #include <errno.h> 21 #include <ctype.h> 22 #include <netdb.h> 23 #include <setjmp.h> 24 25 #define TIMEOUT 5 26 27 extern int errno; 28 struct sockaddr_in sin = { AF_INET }; 29 int peer; 30 int rexmtval = TIMEOUT; 31 int maxtimeout = 5*TIMEOUT; 32 char buf[BUFSIZ]; 33 struct sockaddr_in from; 34 int fromlen; 35 36 main() 37 { 38 register struct tftphdr *tp; 39 register int n; 40 41 alarm(10); 42 fromlen = sizeof (from); 43 n = recvfrom(0, buf, sizeof (buf), 0, 44 (caddr_t)&from, &fromlen); 45 if (n < 0) { 46 perror("tftpd: recvfrom"); 47 exit(1); 48 } 49 from.sin_family = AF_INET; 50 alarm(0); 51 #ifdef do_it_right_and_use_a_new_port 52 if (fork()) 53 exit(0); 54 close(0); 55 close(1); 56 peer = socket(AF_INET, SOCK_DGRAM, 0); 57 if (peer < 0) { 58 perror("tftpd: socket"); 59 exit(1); 60 } 61 if (bind(peer, (caddr_t)&sin, sizeof (sin)) < 0) { 62 perror("tftpd: bind"); 63 exit(1); 64 } 65 #else 66 /* 67 * The current 4.2 tftp client neglects to switch its destination 68 * port after the first ACK. 69 */ 70 peer = 0; 71 #endif 72 if (connect(peer, (caddr_t)&from, sizeof(from)) < 0) { 73 perror("tftpd: connect"); 74 exit(1); 75 } 76 tp = (struct tftphdr *)buf; 77 tp->th_opcode = ntohs(tp->th_opcode); 78 if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) 79 tftp(tp, n); 80 exit(1); 81 } 82 83 int validate_access(); 84 int sendfile(), recvfile(); 85 86 struct formats { 87 char *f_mode; 88 int (*f_validate)(); 89 int (*f_send)(); 90 int (*f_recv)(); 91 } formats[] = { 92 { "netascii", validate_access, sendfile, recvfile }, 93 { "octet", validate_access, sendfile, recvfile }, 94 #ifdef notdef 95 { "mail", validate_user, sendmail, recvmail }, 96 #endif 97 { 0 } 98 }; 99 100 /* 101 * Handle initial connection protocol. 102 */ 103 tftp(tp, size) 104 struct tftphdr *tp; 105 int size; 106 { 107 register char *cp; 108 int first = 1, ecode; 109 register struct formats *pf; 110 char *filename, *mode; 111 112 filename = cp = tp->th_stuff; 113 again: 114 while (cp < buf + size) { 115 if (*cp == '\0') 116 break; 117 cp++; 118 } 119 if (*cp != '\0') { 120 nak(EBADOP); 121 exit(1); 122 } 123 if (first) { 124 mode = ++cp; 125 first = 0; 126 goto again; 127 } 128 for (cp = mode; *cp; cp++) 129 if (isupper(*cp)) 130 *cp = tolower(*cp); 131 for (pf = formats; pf->f_mode; pf++) 132 if (strcmp(pf->f_mode, mode) == 0) 133 break; 134 if (pf->f_mode == 0) { 135 nak(EBADOP); 136 exit(1); 137 } 138 ecode = (*pf->f_validate)(filename, tp->th_opcode); 139 if (ecode) { 140 nak(ecode); 141 exit(1); 142 } 143 if (tp->th_opcode == WRQ) 144 (*pf->f_recv)(pf); 145 else 146 (*pf->f_send)(pf); 147 exit(0); 148 } 149 150 int fd; 151 152 /* 153 * Validate file access. Since we 154 * have no uid or gid, for now require 155 * file to exist and be publicly 156 * readable/writable. 157 * Note also, full path name must be 158 * given as we have no login directory. 159 */ 160 validate_access(file, mode) 161 char *file; 162 int mode; 163 { 164 struct stat stbuf; 165 166 if (*file != '/') 167 return (EACCESS); 168 if (stat(file, &stbuf) < 0) 169 return (errno == ENOENT ? ENOTFOUND : EACCESS); 170 if (mode == RRQ) { 171 if ((stbuf.st_mode&(S_IREAD >> 6)) == 0) 172 return (EACCESS); 173 } else { 174 if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0) 175 return (EACCESS); 176 } 177 fd = open(file, mode == RRQ ? 0 : 1); 178 if (fd < 0) 179 return (errno + 100); 180 return (0); 181 } 182 183 int timeout; 184 jmp_buf timeoutbuf; 185 186 timer() 187 { 188 189 timeout += rexmtval; 190 if (timeout >= maxtimeout) 191 exit(1); 192 longjmp(timeoutbuf, 1); 193 } 194 195 /* 196 * Send the requested file. 197 */ 198 sendfile(pf) 199 struct format *pf; 200 { 201 register struct tftphdr *tp; 202 register int block = 1, size, n; 203 204 signal(SIGALRM, timer); 205 tp = (struct tftphdr *)buf; 206 do { 207 size = read(fd, tp->th_data, SEGSIZE); 208 if (size < 0) { 209 nak(errno + 100); 210 return; 211 } 212 tp->th_opcode = htons((u_short)DATA); 213 tp->th_block = htons((u_short)block); 214 timeout = 0; 215 (void) setjmp(timeoutbuf); 216 if (send(peer, buf, size + 4, 0) != size + 4) { 217 perror("tftpd: send"); 218 return; 219 } 220 do { 221 alarm(rexmtval); 222 n = recv(peer, buf, sizeof (buf), 0); 223 alarm(0); 224 if (n < 0) { 225 perror("tftpd: recv"); 226 return; 227 } 228 tp->th_opcode = ntohs((u_short)tp->th_opcode); 229 tp->th_block = ntohs((u_short)tp->th_block); 230 if (tp->th_opcode == ERROR) 231 return; 232 } while (tp->th_opcode != ACK || tp->th_block != block); 233 block++; 234 } while (size == SEGSIZE); 235 } 236 237 /* 238 * Receive a file. 239 */ 240 recvfile(pf) 241 struct format *pf; 242 { 243 register struct tftphdr *tp; 244 register int block = 0, n, size; 245 246 signal(SIGALRM, timer); 247 tp = (struct tftphdr *)buf; 248 do { 249 timeout = 0; 250 tp->th_opcode = htons((u_short)ACK); 251 tp->th_block = htons((u_short)block); 252 block++; 253 (void) setjmp(timeoutbuf); 254 if (send(peer, buf, 4, 0) != 4) { 255 perror("tftpd: send"); 256 goto abort; 257 } 258 do { 259 alarm(rexmtval); 260 n = recv(peer, buf, sizeof (buf), 0); 261 alarm(0); 262 if (n < 0) { 263 perror("tftpd: recv"); 264 goto abort; 265 } 266 tp->th_opcode = ntohs((u_short)tp->th_opcode); 267 tp->th_block = ntohs((u_short)tp->th_block); 268 if (tp->th_opcode == ERROR) 269 goto abort; 270 } while (tp->th_opcode != DATA || block != tp->th_block); 271 size = write(fd, tp->th_data, n - 4); 272 if (size < 0) { 273 nak(errno + 100); 274 goto abort; 275 } 276 } while (size == SEGSIZE); 277 abort: 278 tp->th_opcode = htons((u_short)ACK); 279 tp->th_block = htons((u_short)(block)); 280 (void) send(peer, buf, 4, 0); 281 } 282 283 struct errmsg { 284 int e_code; 285 char *e_msg; 286 } errmsgs[] = { 287 { EUNDEF, "Undefined error code" }, 288 { ENOTFOUND, "File not found" }, 289 { EACCESS, "Access violation" }, 290 { ENOSPACE, "Disk full or allocation exceeded" }, 291 { EBADOP, "Illegal TFTP operation" }, 292 { EBADID, "Unknown transfer ID" }, 293 { EEXISTS, "File already exists" }, 294 { ENOUSER, "No such user" }, 295 { -1, 0 } 296 }; 297 298 /* 299 * Send a nak packet (error message). 300 * Error code passed in is one of the 301 * standard TFTP codes, or a UNIX errno 302 * offset by 100. 303 */ 304 nak(error) 305 int error; 306 { 307 register struct tftphdr *tp; 308 int length; 309 register struct errmsg *pe; 310 extern char *sys_errlist[]; 311 312 tp = (struct tftphdr *)buf; 313 tp->th_opcode = htons((u_short)ERROR); 314 tp->th_code = htons((u_short)error); 315 for (pe = errmsgs; pe->e_code >= 0; pe++) 316 if (pe->e_code == error) 317 break; 318 if (pe->e_code < 0) 319 pe->e_msg = sys_errlist[error - 100]; 320 strcpy(tp->th_msg, pe->e_msg); 321 length = strlen(pe->e_msg); 322 tp->th_msg[length] = '\0'; 323 length += 5; 324 if (send(peer, buf, length, 0) != length) 325 perror("nak"); 326 exit(1); 327 } 328