void recvinfo(struct worker_info *info) { while (1) { uint8_t data[MAX_PACKET_SIZE]; int len; myrecvfrom(data, &len); if (data[0] != STRUCT_WORKER_INFO) continue; assert(len == 1 + sizeof(struct worker_info)); memcpy(info, data + 1, sizeof(struct worker_info)); break; } }
int main(int argc, char **argv) { struct tftphdr *tp; struct passwd *pw; struct options *opt; struct sockaddr_in myaddr; struct sockaddr_in servaddr; int n = 0; int on = 1; int fd = 0; int pid, xx; int c; int setrv; int timeout = 900; /* Default timeout */ // char *user = "******"; /* Default user */ char *user = "******"; /* Default user */ __progname = basename(argv[0]); openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); while ((c = getopt(argc, argv, "csu:r:t:")) != -1) switch (c) { case 'c': cancreate = 1; break; case 's': secure = 1; break; case 't': timeout = atoi(optarg); break; case 'u': user = optarg; break; case 'r': for ( opt = options ; opt->o_opt ; opt++ ) { if ( !strcasecmp(optarg, opt->o_opt) ) { opt->o_opt = ""; /* Don't support this option */ break; } } if ( !opt->o_opt ) { syslog(LOG_ERR, "Unknown option: %s", optarg); exit(1); } break; default: usage(); break; } for (; optind != argc; optind++) { if (dirs) dirs = realloc(dirs, (ndirs+2) * sizeof (char *)); else dirs = calloc(ndirs+2, sizeof(char *)); if (dirs == NULL) { syslog(LOG_ERR, "malloc: %m"); exit(1); } dirs[n++] = argv[optind]; dirs[n] = NULL; ndirs++; } if (secure) { if (ndirs == 0) { syslog(LOG_ERR, "no -s directory"); exit(1); } if (ndirs > 1) { syslog(LOG_ERR, "too many -s directories"); exit(1); } if (chdir(dirs[0])) { syslog(LOG_ERR, "%s: %m", dirs[0]); exit(1); } } pw = getpwnam(user); if (!pw) { syslog(LOG_ERR, "no user %s: %m", user); exit(1); } #if KAM_LATER // not needed if not by inetd if (ioctl(fd, FIONBIO, &on) < 0) { syslog(LOG_ERR, "ioctl(FIONBIO): %m"); exit(1); } #else // bind to default tftp port if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { exit(1); } //printf("fd=%d\n",fd); on = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)); bzero(&servaddr, sizeof(servaddr)); /* zero the struct */ servaddr.sin_family = AF_INET; /* host byte order */ servaddr.sin_port = htons(IPPORT_TFTP);/* short, network byte order */ servaddr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */ if (bind(fd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr)) == -1) { close(fd); exit(1); } #endif /* This means we don't want to wait() for children */ bsd_signal(SIGCHLD, SIG_IGN); // Set up signal handlers so we can clear up our child processes // signal(SIGTERM, signal_handler); signal(SIGHUP, signal_handler); signal(SIGINT, signal_handler); signal(SIGCHLD, signal_handler); signal(SIGKILL, signal_handler); xx = fork(); if(xx==-1) { syslog(LOG_CRIT, "Could not fork() tftpd into background\n"); closelog(); close(fd); exit(1); } if(xx != 0) { // Parent... printf("tftp in background pid=%d\n",xx); exit(0); } do { fromlen = sizeof (from); // printf("recvfrom %d: ",fd); #if KAM_LATER n = myrecvfrom(fd, buf, sizeof (buf), 0, (struct sockaddr *)&from, &fromlen, &myaddr); #else bzero(&myaddr, sizeof(struct sockaddr_in)); myaddr.sin_family = AF_INET; myaddr.sin_port = htons(IPPORT_TFTP); n = recvfrom(fd, buf, sizeof (buf), 0, (struct sockaddr *)&from, &fromlen); #endif if (n < 0) { syslog(LOG_ERR, "recvfrom: %m"); exit(1); } //printf("%d bytes ",n); #if KAM_LATER //#ifdef HAVE_TCPWRAPPERS /* Verify if this was a legal request for us. This has to be done before the chroot, while /etc is still accessible. */ request_init(&wrap_request, RQ_DAEMON, __progname, RQ_FILE, fd, RQ_CLIENT_SIN, &from, RQ_SERVER_SIN, &myaddr, 0); sock_methods(&wrap_request); if ( hosts_access(&wrap_request) == 0 ) { if ( deny_severity != -1 ) syslog(deny_severity, "connection refused from %s", inet_ntoa(from.sin_addr)); exit(1); /* Access denied */ } else if ( allow_severity != -1 ) { syslog(allow_severity, "connect from %s", inet_ntoa(from.sin_addr)); } #endif /* * Now that we have read the message out of the UDP * socket, we fork and go back to listening to the * socket. */ pid = fork(); if (pid < 0) { syslog(LOG_ERR, "fork: %m"); exit(1); /* Return to inetd, just in case */ } } while ( pid > 0 ); /* Parent process continues... */ /* Child process: handle the actual request here */ /* Chroot and drop privileges */ if (secure && chroot(".")) { syslog(LOG_ERR, "chroot: %m"); exit(1); } #ifdef HAVE_SETREGID setrv = setregid(pw->pw_gid, pw->pw_gid); #else setrv = setegid(pw->pw_gid) || setgid(pw->pw_gid); #endif #ifdef HAVE_SETREUID setrv = setrv || setreuid(pw->pw_uid, pw->pw_uid); #else /* Important: setuid() must come first */ setrv = setrv || setuid(pw->pw_uid) || (geteuid() != pw->pw_uid && seteuid(pw->pw_uid)); #endif if ( setrv ) { syslog(LOG_ERR, "cannot drop privileges: %m"); exit(1); } /* Close file descriptors we don't need */ from.sin_family = AF_INET; alarm(0); #if KAM_LATER close(fd); close(1); #endif /* Process the request... */ peer = socket(AF_INET, SOCK_DGRAM, 0); if (peer < 0) { syslog(LOG_ERR, "socket: %m"); exit(1); } myaddr.sin_port = htons(0); /* We want a new local port */ if (bind(peer, (struct sockaddr *)&myaddr, sizeof (myaddr)) < 0) { syslog(LOG_ERR, "bind: %m"); exit(1); } if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { syslog(LOG_ERR, "connect: %m"); exit(1); } tp = (struct tftphdr *)buf; tp->th_opcode = ntohs(tp->th_opcode); if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) { tftp(tp, n); } exit(0); }
int main (int argc, char **argv) { struct tftphdr *tp; struct passwd *pw; struct options *opt; struct sockaddr_in myaddr; struct sockaddr_in bindaddr; int n; int fd = 0; int standalone = 0; /* Standalone (listen) mode */ char *address = NULL; /* Address to listen to */ pid_t pid; mode_t my_umask = 0; int spec_umask = 0; int c; int setrv; int waittime = 900; /* Default time to wait for a connect */ const char *user = "******"; /* Default user */ char *p, *ep; #ifdef WITH_REGEX char *rewrite_file = NULL; #endif /* basename() is way too much of a pain from a portability standpoint */ p = strrchr (argv[0], '/'); __progname = (p && p[1]) ? p + 1 : argv[0]; openlog (__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); while ((c = getopt (argc, argv, "cspvVla:u:U:r:t:m:")) != -1) switch (c) { case 'c': cancreate = 1; break; case 's': secure = 1; break; case 'p': unixperms = 1; break; case 'l': standalone = 1; break; case 'a': address = optarg; break; case 't': waittime = atoi (optarg); break; case 'u': user = optarg; break; case 'U': my_umask = strtoul (optarg, &ep, 8); if (*ep) { syslog (LOG_ERR, "Invalid umask: %s", optarg); exit (EX_USAGE); } spec_umask = 1; break; case 'r': for (opt = options; opt->o_opt; opt++) { if (!strcasecmp (optarg, opt->o_opt)) { opt->o_opt = ""; /* Don't support this option */ break; } } if (!opt->o_opt) { syslog (LOG_ERR, "Unknown option: %s", optarg); exit (EX_USAGE); } break; #ifdef WITH_REGEX case 'm': if (rewrite_file) { syslog (LOG_ERR, "Multiple -m options"); exit (EX_USAGE); } rewrite_file = optarg; break; #endif case 'v': verbosity++; break; case 'V': /* Print configuration to stdout and exit */ printf ("%s\n", TFTPD_CONFIG_STR); exit (0); break; default: usage (); break; } dirs = xmalloc ((argc - optind + 1) * sizeof (char *)); for (ndirs = 0; optind != argc; optind++) dirs[ndirs++] = argv[optind]; dirs[ndirs] = NULL; if (secure) { if (ndirs == 0) { syslog (LOG_ERR, "no -s directory"); exit (EX_USAGE); } if (ndirs > 1) { syslog (LOG_ERR, "too many -s directories"); exit (EX_USAGE); } if (chdir (dirs[0])) { syslog (LOG_ERR, "%s: %m", dirs[0]); exit (EX_NOINPUT); } } pw = getpwnam (user); if (!pw) { syslog (LOG_ERR, "no user %s: %m", user); exit (EX_NOUSER); } if (spec_umask || !unixperms) umask (my_umask); /* Note: on Cygwin, select() on a nonblocking socket becomes a nonblocking select. */ #ifndef __CYGWIN__ set_socket_nonblock (fd, 1); #endif #ifdef WITH_REGEX if (rewrite_file) rewrite_rules = read_remap_rules (rewrite_file); #endif /* If we're running standalone, set up the input port */ if (standalone) { fd = socket (PF_INET, SOCK_DGRAM, 0); memset (&bindaddr, 0, sizeof bindaddr); bindaddr.sin_family = AF_INET; bindaddr.sin_addr.s_addr = INADDR_ANY; bindaddr.sin_port = htons (IPPORT_TFTP); if (address) { char *portptr, *eportptr; struct hostent *hostent; struct servent *servent; unsigned long port; address = tfstrdup (address); portptr = strrchr (address, ':'); if (portptr) *portptr++ = '\0'; if (*address) { hostent = gethostbyname (address); if (!hostent || hostent->h_addrtype != AF_INET) { syslog (LOG_ERR, "cannot resolve local bind address: %s", address); exit (EX_NOINPUT); } memcpy (&bindaddr.sin_addr, hostent->h_addr, hostent->h_length); } else { /* Default to using INADDR_ANY */ } if (portptr && *portptr) { servent = getservbyname (portptr, "udp"); if (servent) { bindaddr.sin_port = servent->s_port; } else if ((port = strtoul (portptr, &eportptr, 0)) && !*eportptr) { bindaddr.sin_port = htons (port); } else if (!strcmp (portptr, "tftp")) { /* It's TFTP, we're OK */ } else { syslog (LOG_ERR, "cannot resolve local bind port: %s", portptr); exit (EX_NOINPUT); } } } if (bind (fd, (struct sockaddr *) &bindaddr, sizeof bindaddr) < 0) { syslog (LOG_ERR, "cannot bind to local socket: %m"); exit (EX_OSERR); } /* Daemonize this process */ { pid_t f = fork (); int nfd; if (f > 0) exit (0); if (f < 0) { syslog (LOG_ERR, "cannot fork: %m"); exit (EX_OSERR); } nfd = open ("/dev/null", O_RDWR); if (nfd >= 0) { #ifdef HAVE_DUP2 dup2 (nfd, 0); dup2 (nfd, 1); dup2 (nfd, 2); #else close (0); dup (nfd); close (1); dup (nfd); close (2); dup (nfd); #endif close (nfd); } else { close (0); close (1); close (2); } #ifdef HAVE_SETSID #ifndef __CYGWIN__ /* Kills the process on Cygwin? */ setsid (); #endif #endif } } else { /* 0 is our socket descriptor */ close (1); close (2); } /* This means we don't want to wait() for children */ #ifdef SA_NOCLDWAIT set_signal (SIGCHLD, SIG_IGN, SA_NOCLDSTOP | SA_NOCLDWAIT); #else set_signal (SIGCHLD, SIG_IGN, SA_NOCLDSTOP); #endif /* Take SIGHUP and use it to set a variable. This is polled synchronously to make sure we don't lose packets as a result. */ set_signal (SIGHUP, handle_sighup, 0); while (1) { fd_set readset; struct timeval tv_waittime; int rv; if (caught_sighup) { caught_sighup = 0; if (standalone) { #ifdef HAVE_REGEX if (rewrite_file) { freerules (rewrite_rules); rewrite_rules = read_remap_rules (rewrite_file); } #endif } else { /* Return to inetd for respawn */ exit (0); } } FD_ZERO (&readset); FD_SET (fd, &readset); tv_waittime.tv_sec = waittime; tv_waittime.tv_usec = 0; #ifdef __CYGWIN__ /* On Cygwin, select() on a nonblocking socket returns immediately, with a rv of 0! */ set_socket_nonblock (fd, 0); #endif /* Never time out if we're in standalone mode */ rv = select (fd + 1, &readset, NULL, NULL, standalone ? NULL : &tv_waittime); if (rv == -1 && errno == EINTR) continue; /* Signal caught, reloop */ if (rv == -1) { syslog (LOG_ERR, "select loop: %m"); exit (EX_IOERR); } else if (rv == 0) { exit (0); /* Timeout, return to inetd */ } #ifdef __CYGWIN__ set_socket_nonblock (fd, 1); #endif fromlen = sizeof (from); n = myrecvfrom (fd, buf, sizeof (buf), 0, (struct sockaddr *) &from, &fromlen, &myaddr); if (n < 0) { if (E_WOULD_BLOCK (errno) || errno == EINTR) { continue; /* Again, from the top */ } else { syslog (LOG_ERR, "recvfrom: %m"); exit (EX_IOERR); } } if (standalone && myaddr.sin_addr.s_addr == INADDR_ANY) { /* myrecvfrom() didn't capture the source address; but we might have bound to a specific address, if so we should use it */ memcpy (&myaddr.sin_addr, &bindaddr.sin_addr, sizeof bindaddr.sin_addr); } /* * Now that we have read the request packet from the UDP * socket, we fork and go back to listening to the socket. */ pid = fork (); if (pid < 0) { syslog (LOG_ERR, "fork: %m"); exit (EX_OSERR); /* Return to inetd, just in case */ } else if (pid == 0) break; /* Child exit, parent loop */ } /* Child process: handle the actual request here */ /* Ignore SIGHUP */ set_signal (SIGHUP, SIG_IGN, 0); #ifdef HAVE_TCPWRAPPERS /* Verify if this was a legal request for us. This has to be done before the chroot, while /etc is still accessible. */ request_init (&wrap_request, RQ_DAEMON, __progname, RQ_FILE, fd, RQ_CLIENT_SIN, &from, RQ_SERVER_SIN, &myaddr, 0); sock_methods (&wrap_request); if (hosts_access (&wrap_request) == 0) { if (deny_severity != -1) syslog (deny_severity, "connection refused from %s", inet_ntoa (from.sin_addr)); exit (EX_NOPERM); /* Access denied */ } else if (allow_severity != -1) { syslog (allow_severity, "connect from %s", inet_ntoa (from.sin_addr)); } #endif /* Close file descriptors we don't need */ close (fd); /* Get a socket. This has to be done before the chroot(), since some systems require access to /dev to create a socket. */ peer = socket (AF_INET, SOCK_DGRAM, 0); if (peer < 0) { syslog (LOG_ERR, "socket: %m"); exit (EX_IOERR); } /* Set up the supplementary group access list if possible */ /* /etc/group still need to be accessible at this point */ #ifdef HAVE_INITGROUPS setrv = initgroups (user, pw->pw_gid); if (setrv) { syslog (LOG_ERR, "cannot set groups for user %s", user); exit (EX_OSERR); } #else #ifdef HAVE_SETGROUPS if (setgroups (0, NULL)) { syslog (LOG_ERR, "cannot clear group list"); } #endif #endif /* Chroot and drop privileges */ if (secure) { if (chroot (".")) { syslog (LOG_ERR, "chroot: %m"); exit (EX_OSERR); } #ifdef __CYGWIN__ chdir ("/"); /* Cygwin chroot() bug workaround */ #endif } #ifdef HAVE_SETREGID setrv = setregid (pw->pw_gid, pw->pw_gid); #else setrv = setegid (pw->pw_gid) || setgid (pw->pw_gid); #endif #ifdef HAVE_SETREUID setrv = setrv || setreuid (pw->pw_uid, pw->pw_uid); #else /* Important: setuid() must come first */ setrv = setrv || setuid (pw->pw_uid) || (geteuid () != pw->pw_uid && seteuid (pw->pw_uid)); #endif if (setrv) { syslog (LOG_ERR, "cannot drop privileges: %m"); exit (EX_OSERR); } /* Other basic setup */ from.sin_family = AF_INET; /* Process the request... */ myaddr.sin_port = htons (0); /* We want a new local port */ if (bind (peer, (struct sockaddr *) &myaddr, sizeof myaddr) < 0) { syslog (LOG_ERR, "bind: %m"); exit (EX_IOERR); } if (connect (peer, (struct sockaddr *) &from, sizeof from) < 0) { syslog (LOG_ERR, "connect: %m"); exit (EX_IOERR); } tp = (struct tftphdr *) buf; tp->th_opcode = ntohs (tp->th_opcode); if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) tftp (tp, n); exit (0); }