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