// file : bbot/agent/tftp.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #include #include // htonl() #include // sockaddr_in #include #include #include // memset() #include using namespace std; using namespace butl; namespace bbot { tftp_server:: tftp_server (const string& map, uint16_t port) { int fd (socket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)); if (fd == -1) throw_system_error (errno); fd_.reset (fd); // Bind to ephemeral port. // sockaddr_in addr; memset (&addr, 0, sizeof (addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl (INADDR_ANY); addr.sin_port = htons (port); // Not to confuse with std::bind(). // if (::bind (fd, reinterpret_cast (&addr), sizeof (sockaddr_in)) == -1) throw_system_error (errno); // Create the map file. // map_ = auto_rmfile (path::temp_path ("bbot-agent-tftp-map")); ofdstream ofs (map_.path); ofs << map << endl; ofs.close (); } uint16_t tftp_server:: port () const { sockaddr_in addr; socklen_t size (sizeof (addr)); if (getsockname (fd_.get (), reinterpret_cast (&addr), &size) == -1) throw_system_error (errno); assert (size == sizeof (addr)); return ntohs (addr.sin_port); } bool tftp_server:: serve (size_t& sec, size_t inc) { tracer trace ("tftp_server::serve"); if (inc == 0 || inc > sec) inc = sec; int fd (fd_.get ()); // Note: Linux updates the timeout value which we rely upon. // timeval timeout {static_cast (inc), 0}; fd_set rd; FD_ZERO (&rd); for (;;) { FD_SET (fd, &rd); int r (select (fd + 1, &rd, nullptr, nullptr, &timeout)); if (r == -1) { if (errno == EINTR) continue; throw_system_error (errno); } else if (r == 0) // Timeout. { sec -= inc; return false; } if (FD_ISSET (fd, &rd)) { // The inetd "protocol" is to pass the socket as stdin/stdout file // descriptors. // // Notes/issues: // // 1. Writes diagnostics to syslog. // run_io (trace, fddup (fd), fddup (fd), 2, "sudo", // Required for --secure (chroot). "/usr/sbin/in.tftpd", // Standard installation location. "--verbosity", 1, // Verbosity level. "--timeout", 1, // Wait for more requests. "--permissive", // Use inherited umask. "--create", // Allow creating new files (PUT). "--map-file", map_.path, // Path remapping rules. "--user", uname, // Run as our effective user. "--secure", // Chroot to data directory. ops.tftp ()); // This is not really accurate since tftpd will, for example, serve // an upload request until it is complete. But it's close enough for // our needs. // sec -= (inc - static_cast (timeout.tv_sec)); return true; } } } }