30 #include <libconfig.h++>
31 #include <boost/asio.hpp>
32 #include <openssl/sha.h>
34 #include "spdlog/async.h"
35 #include "spdlog/spdlog.h"
36 #include "spdlog/sinks/syslog_sink.h"
39 #include "../utils/base64.h"
43 using libconfig::Config;
44 using libconfig::FileIOException;
45 using libconfig::ParseException;
47 using namespace std::literals::chrono_literals;
49 static void print_version(FILE *stream,
struct argp_state *state);
52 static char doc[] =
"FLUTE/ALC transmitter demo";
54 static struct argp_option
options[] = {
55 {
"target",
'm',
"IP", 0,
"Target multicast address (default: 238.1.1.95)", 0},
56 {
"port",
'p',
"PORT", 0,
"Target port (default: 40085)", 0},
57 {
"mtu",
't',
"BYTES", 0,
"Path MTU to size ALC packets for (default: 1500)", 0},
58 {
"rate-limit",
'r',
"KBPS", 0,
"Transmit rate limit (kbps), 0 = no limit, default: 1000 (1 Mbps)", 0},
59 {
"ipsec-key",
'k',
"KEY", 0,
"To enable IPSec/ESP encryption of packets, provide a hex-encoded AES key here", 0},
60 {
"log-level",
'l',
"LEVEL", 0,
61 "Log verbosity: 0 = trace, 1 = debug, 2 = info, 3 = warn, 4 = error, 5 = "
62 "critical, 6 = none (default: 2)",
64 {
"gzip",
'g',
nullptr, 0,
"Use gzip to compress the contents, implies -n option", 0},
65 {
"tsi",
'T',
"ID", 0,
"The TSI to use for the FLUTE session (default: 16)", 0},
66 {
"new-api",
'n',
nullptr, 0,
"Use the new FileDescription API", 0},
67 {
"retransmit",
'R',
"COUNT", 0,
"Number of times to repeatedly transmit a file, implies -n option (default: 1)", 0},
68 {
"etags",
'e',
nullptr, 0,
"Enable generation of ETag values for each file, implies -n option (default: no ETags)", 0},
69 {
nullptr, 0,
nullptr, 0,
nullptr, 0}};
75 const char *mcast_target = {};
76 bool enable_ipsec =
false;
77 bool use_gzip =
false;
79 bool gen_etags =
false;
80 const char *aes_key = {};
81 unsigned short mcast_port = 40085;
82 unsigned short mtu = 1500;
83 uint32_t rate_limit = 1000;
85 size_t retransmit_count = 1;
86 unsigned log_level = 2;
93 static auto parse_opt(
int key,
char *arg,
struct argp_state *state) -> error_t {
94 auto arguments =
static_cast<struct
ft_arguments *
>(state->input);
98 arguments->new_api =
true;
101 arguments->mcast_target = arg;
104 arguments->aes_key = arg;
105 arguments->enable_ipsec =
true;
108 arguments->mcast_port =
static_cast<unsigned short>(strtoul(arg,
nullptr, 10));
111 arguments->mtu =
static_cast<unsigned short>(strtoul(arg,
nullptr, 10));
114 arguments->rate_limit =
static_cast<uint32_t
>(strtoul(arg,
nullptr, 10));
117 arguments->log_level =
static_cast<unsigned>(strtoul(arg,
nullptr, 10));
120 arguments->use_gzip =
true;
121 arguments->new_api =
true;
124 arguments->tsi =
static_cast<uint64_t
>(strtoul(arg,
nullptr, 10));
127 arguments->new_api =
true;
130 arguments->retransmit_count =
static_cast<size_t>(strtoul(arg,
nullptr, 10));
131 arguments->new_api =
true;
133 case ARGP_KEY_NO_ARGS:
136 arguments->files = &state->argv[state->next-1];
137 state->next = state->argc;
140 return ARGP_ERR_UNKNOWN;
147 nullptr,
nullptr,
nullptr};
153 fprintf(stream,
"%s.%s.%s\n", std::to_string(VERSION_MAJOR).c_str(),
154 std::to_string(VERSION_MINOR).c_str(),
155 std::to_string(VERSION_PATCH).c_str());
163 std::shared_ptr<LibFlute::Transmitter::FileDescription> file;
164 size_t transmitted_count;
167 std::list<fileEntry> files;
169 for (
int j = 0; arguments.
files[j]; j++) {
171 fd->set_content_type(
"application/octet-stream");
172 fd->set_expiry_time(std::chrono::system_clock::now() + 60s);
177 std::array<unsigned char, SHA_DIGEST_LENGTH> digest;
178 SHA1(
reinterpret_cast<const unsigned char*
>(fd->data()), fd->data_length(), digest.data());
179 fd->set_etag(base64_encode(digest.data(), SHA_DIGEST_LENGTH));
181 files.emplace_back(fd);
185 boost::asio::io_context io;
204 [&files, &arguments, &transmitter](uint32_t toi) ->
void {
205 for (
auto& f : files) {
206 if (f.file->toi() == toi) {
207 spdlog::info(
"{} (TOI {}) has been transmitted", f.file->file_entry().content_location, f.file->toi());
208 f.transmitted_count++;
209 if (f.transmitted_count < arguments.retransmit_count) {
210 transmitter.send(f.file);
217 for (
const auto& file : files) {
218 auto toi = transmitter.send( file.file );
219 const auto &file_entry = file.file->file_entry();
220 spdlog::info(
"Queued {} ({} bytes ({} bytes transmitted)) for transmission, TOI is {}",
221 file_entry.content_location, file_entry.content_length, file_entry.fec_oti.transfer_length, toi);
233 std::string location;
238 std::vector<FsFile> files;
241 for (
int j = 0; arguments.
files[j]; j++) {
242 const std::string &location = arguments.
files[j];
243 std::ifstream file(location, std::ios::binary | std::ios::ate);
244 std::streamsize size = file.tellg();
245 file.seekg(0, std::ios::beg);
247 char* buffer = (
char*)malloc(size);
248 file.read(buffer, size);
249 files.push_back(FsFile{ location, buffer, (size_t)size});
253 boost::asio::io_context io;
272 [&files](uint32_t toi) ->
void {
273 for (
auto& file : files) {
274 if (file.toi == toi) {
275 spdlog::info(
"{} (TOI {}) has been transmitted", file.location, file.toi);
282 for (
auto& file : files) {
283 file.toi = transmitter.
send( file.location,
284 "application/octet-stream",
289 spdlog::info(
"Queued {} ({} bytes) for transmission, TOI is {}",
290 file.location, file.len, file.toi);
304 auto main(
int argc,
char **argv) ->
int {
309 argp_parse(&
argp, argc, argv, 0,
nullptr, &arguments);
312 std::string ident =
"flute-transmitter";
313 auto syslog_logger = spdlog::syslog_logger_mt(
"syslog", ident, LOG_PID | LOG_PERROR | LOG_CONS );
316 static_cast<spdlog::level::level_enum
>(arguments.
log_level));
317 spdlog::set_pattern(
"[%H:%M:%S.%f %z] [%^%l%$] [thr %t] %v");
319 spdlog::set_default_logger(syslog_logger);
320 spdlog::info(
"FLUTE transmitter demo starting up");
328 }
catch (std::exception ex ) {
329 spdlog::error(
"Exiting on unhandled exception: %s", ex.what());
uint64_t seconds_since_epoch()
Convenience function to get the current timestamp for expiry calculation.
uint16_t send(const std::string &content_location, const std::string &content_type, uint32_t expires, char *data, size_t length)
Transmit a file (deprecated).
void enable_ipsec(uint32_t spi, const std::string &aes_key)
Enable IPSEC ESP encryption of FLUTE payloads.
void register_completion_callback(completion_callback_t cb)
Register a callback for file transmission completion notifications.
static void send_with_new_api(struct ft_arguments &arguments)
static void print_version(FILE *stream, struct argp_state *state)
Print the program version in MAJOR.MINOR.PATCH format.
auto main(int argc, char **argv) -> int
Main entry point for the program.
const char * argp_program_bug_address
static struct argp_option options[]
void(* argp_program_version_hook)(FILE *, struct argp_state *)
static auto parse_opt(int key, char *arg, struct argp_state *state) -> error_t
Parses the command line options into the arguments struct.
static void send_with_old_api(struct ft_arguments &arguments)
Holds all options passed on the command line.
unsigned log_level
log level
const char * mcast_target
unsigned short mcast_port