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