aboutsummaryrefslogtreecommitdiff
path: root/bbot/tftp.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'bbot/tftp.cxx')
-rw-r--r--bbot/tftp.cxx127
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;
+ }
+ }
+ }
+}