diff options
Diffstat (limited to 'bbot/tftp.cxx')
-rw-r--r-- | bbot/tftp.cxx | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/bbot/tftp.cxx b/bbot/tftp.cxx new file mode 100644 index 0000000..a7398be --- /dev/null +++ b/bbot/tftp.cxx @@ -0,0 +1,127 @@ +// file : bbot/tftp.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <bbot/tftp> + +#include <arpa/inet.h> // htonl() +#include <netinet/in.h> // sockaddr_in +#include <sys/socket.h> +#include <sys/select.h> + +#include <cstring> // memset() + +#include <bbot/agent> + +using namespace std; +using namespace butl; + +namespace bbot +{ + tftp_server:: + tftp_server (const string& map) + { + int fd (socket (AF_INET, SOCK_DGRAM, 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 (0); + + if (bind (fd, + reinterpret_cast<sockaddr*> (&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<sockaddr*> (&addr), + &size) == -1) + throw_system_error (errno); + + assert (size == sizeof (addr)); + return ntohs (addr.sin_port); + } + + bool tftp_server:: + serve (size_t& sec) + { + tracer trace ("tftp_server::serve"); + + int fd (fd_.get ()); + + // Note: Linux updates the timeout value which we rely upon. + // + timeval timeout {static_cast<long> (sec), 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. + return false; + + if (FD_ISSET (fd, &rd)) + { + text << "connection"; + + // 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. + "--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. + "/build/tftp/"); + + sec = static_cast<size_t> (timeout.tv_sec); + return true; + } + } + } +} |