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