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