You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
249 lines
8.7 KiB
249 lines
8.7 KiB
// Copyright 2019 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "absl/strings/str_cat.h"
|
|
#include "cast/receiver/channel/static_credentials.h"
|
|
#include "cast/standalone_receiver/cast_service.h"
|
|
#include "platform/api/time.h"
|
|
#include "platform/base/error.h"
|
|
#include "platform/base/ip_address.h"
|
|
#include "platform/impl/logging.h"
|
|
#include "platform/impl/network_interface.h"
|
|
#include "platform/impl/platform_client_posix.h"
|
|
#include "platform/impl/task_runner.h"
|
|
#include "platform/impl/text_trace_logging_platform.h"
|
|
#include "util/chrono_helpers.h"
|
|
#include "util/stringprintf.h"
|
|
#include "util/trace_logging.h"
|
|
|
|
namespace openscreen {
|
|
namespace cast {
|
|
namespace {
|
|
|
|
void LogUsage(const char* argv0) {
|
|
constexpr char kTemplate[] = R"(
|
|
usage: %s <options> <interface>
|
|
|
|
interface
|
|
Specifies the network interface to bind to. The interface is
|
|
looked up from the system interface registry.
|
|
Mandatory, as it must be known for publishing discovery.
|
|
|
|
options:
|
|
-p, --private-key=path-to-key: Path to OpenSSL-generated private key to be
|
|
used for TLS authentication. If a private key is not
|
|
provided, a randomly generated one will be used for this
|
|
session.
|
|
|
|
-d, --developer-certificate=path-to-cert: Path to PEM file containing a
|
|
developer generated server root TLS certificate.
|
|
If a root server certificate is not provided, one
|
|
will be generated using a randomly generated
|
|
private key. Note that if a certificate path is
|
|
passed, the private key path is a mandatory field.
|
|
|
|
-g, --generate-credentials: Instructs the binary to generate a private key
|
|
and self-signed root certificate with the CA
|
|
bit set to true, and then exit. The resulting
|
|
private key and certificate can then be used as
|
|
values for the -p and -s flags.
|
|
|
|
-f, --friendly-name: Friendly name to be used for device discovery.
|
|
|
|
-m, --model-name: Model name to be used for device discovery.
|
|
|
|
-t, --tracing: Enable performance tracing logging.
|
|
|
|
-v, --verbose: Enable verbose logging.
|
|
|
|
-h, --help: Show this help message.
|
|
)";
|
|
|
|
std::cerr << StringPrintf(kTemplate, argv0);
|
|
}
|
|
|
|
InterfaceInfo GetInterfaceInfoFromName(const char* name) {
|
|
OSP_CHECK(name != nullptr) << "Missing mandatory argument: interface.";
|
|
InterfaceInfo interface_info;
|
|
std::vector<InterfaceInfo> network_interfaces = GetNetworkInterfaces();
|
|
for (auto& interface : network_interfaces) {
|
|
if (interface.name == name) {
|
|
interface_info = std::move(interface);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (interface_info.name.empty()) {
|
|
auto error_or_info = GetLoopbackInterfaceForTesting();
|
|
if (error_or_info.has_value()) {
|
|
if (error_or_info.value().name == name) {
|
|
interface_info = std::move(error_or_info.value());
|
|
}
|
|
}
|
|
}
|
|
OSP_CHECK(!interface_info.name.empty()) << "Invalid interface specified.";
|
|
return interface_info;
|
|
}
|
|
|
|
void RunCastService(TaskRunnerImpl* task_runner,
|
|
const InterfaceInfo& interface,
|
|
GeneratedCredentials creds,
|
|
const std::string& friendly_name,
|
|
const std::string& model_name,
|
|
bool discovery_enabled) {
|
|
std::unique_ptr<CastService> service;
|
|
task_runner->PostTask([&] {
|
|
service = std::make_unique<CastService>(task_runner, interface,
|
|
std::move(creds), friendly_name,
|
|
model_name, discovery_enabled);
|
|
});
|
|
|
|
OSP_LOG_INFO << "CastService is running. CTRL-C (SIGINT), or send a "
|
|
"SIGTERM to exit.";
|
|
task_runner->RunUntilSignaled();
|
|
|
|
// Spin the TaskRunner to execute destruction/shutdown tasks.
|
|
OSP_LOG_INFO << "Shutting down...";
|
|
task_runner->PostTask([&] {
|
|
service.reset();
|
|
task_runner->RequestStopSoon();
|
|
});
|
|
task_runner->RunUntilStopped();
|
|
OSP_LOG_INFO << "Bye!";
|
|
}
|
|
|
|
int RunStandaloneReceiver(int argc, char* argv[]) {
|
|
#if !defined(CAST_ALLOW_DEVELOPER_CERTIFICATE)
|
|
OSP_LOG_FATAL
|
|
<< "It compiled! However cast_receiver currently only supports using a "
|
|
"passed-in certificate and private key, and must be built with "
|
|
"cast_allow_developer_certificate=true set in the GN args to "
|
|
"actually do anything interesting.";
|
|
return 1;
|
|
#endif
|
|
|
|
// A note about modifying command line arguments: consider uniformity
|
|
// between all Open Screen executables. If it is a platform feature
|
|
// being exposed, consider if it applies to the standalone receiver,
|
|
// standalone sender, osp demo, and test_main argument options.
|
|
const struct option kArgumentOptions[] = {
|
|
{"private-key", required_argument, nullptr, 'p'},
|
|
{"developer-certificate", required_argument, nullptr, 'd'},
|
|
{"generate-credentials", no_argument, nullptr, 'g'},
|
|
{"friendly-name", required_argument, nullptr, 'f'},
|
|
{"model-name", required_argument, nullptr, 'm'},
|
|
{"tracing", no_argument, nullptr, 't'},
|
|
{"verbose", no_argument, nullptr, 'v'},
|
|
{"help", no_argument, nullptr, 'h'},
|
|
|
|
// Discovery is enabled by default, however there are cases where it
|
|
// needs to be disabled, such as on Mac OS X.
|
|
{"disable-discovery", no_argument, nullptr, 'x'},
|
|
{nullptr, 0, nullptr, 0}};
|
|
|
|
bool is_verbose = false;
|
|
bool discovery_enabled = true;
|
|
std::string private_key_path;
|
|
std::string developer_certificate_path;
|
|
std::string friendly_name = "Cast Standalone Receiver";
|
|
std::string model_name = "cast_standalone_receiver";
|
|
bool should_generate_credentials = false;
|
|
std::unique_ptr<TextTraceLoggingPlatform> trace_logger;
|
|
int ch = -1;
|
|
while ((ch = getopt_long(argc, argv, "p:d:f:m:gtvhx", kArgumentOptions,
|
|
nullptr)) != -1) {
|
|
switch (ch) {
|
|
case 'p':
|
|
private_key_path = optarg;
|
|
break;
|
|
case 'd':
|
|
developer_certificate_path = optarg;
|
|
break;
|
|
case 'f':
|
|
friendly_name = optarg;
|
|
break;
|
|
case 'm':
|
|
model_name = optarg;
|
|
break;
|
|
case 'g':
|
|
should_generate_credentials = true;
|
|
break;
|
|
case 't':
|
|
trace_logger = std::make_unique<TextTraceLoggingPlatform>();
|
|
break;
|
|
case 'v':
|
|
is_verbose = true;
|
|
break;
|
|
case 'x':
|
|
discovery_enabled = false;
|
|
break;
|
|
case 'h':
|
|
LogUsage(argv[0]);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
SetLogLevel(is_verbose ? LogLevel::kVerbose : LogLevel::kInfo);
|
|
|
|
// Either -g is required, or both -p and -d.
|
|
if (should_generate_credentials) {
|
|
GenerateDeveloperCredentialsToFile();
|
|
return 0;
|
|
}
|
|
if (private_key_path.empty() || developer_certificate_path.empty()) {
|
|
OSP_LOG_FATAL << "You must either invoke with -g to generate credentials, "
|
|
"or provide both a private key path and root certificate "
|
|
"using -p and -d";
|
|
return 1;
|
|
}
|
|
|
|
const char* interface_name = argv[optind];
|
|
OSP_CHECK(interface_name && strlen(interface_name) > 0)
|
|
<< "No interface name provided.";
|
|
|
|
std::string device_id =
|
|
absl::StrCat("Standalone Receiver on ", interface_name);
|
|
ErrorOr<GeneratedCredentials> creds = GenerateCredentials(
|
|
device_id, private_key_path, developer_certificate_path);
|
|
OSP_CHECK(creds.is_value()) << creds.error();
|
|
|
|
const InterfaceInfo interface = GetInterfaceInfoFromName(interface_name);
|
|
OSP_CHECK(interface.GetIpAddressV4() || interface.GetIpAddressV6());
|
|
if (std::all_of(interface.hardware_address.begin(),
|
|
interface.hardware_address.end(),
|
|
[](int e) { return e == 0; })) {
|
|
OSP_LOG_WARN
|
|
<< "Hardware address is empty. Either you are on a loopback device "
|
|
"or getting the network interface information failed somehow. "
|
|
"Discovery publishing will be disabled.";
|
|
discovery_enabled = false;
|
|
}
|
|
|
|
auto* const task_runner = new TaskRunnerImpl(&Clock::now);
|
|
PlatformClientPosix::Create(milliseconds(50),
|
|
std::unique_ptr<TaskRunnerImpl>(task_runner));
|
|
RunCastService(task_runner, interface, std::move(creds.value()),
|
|
friendly_name, model_name, discovery_enabled);
|
|
PlatformClientPosix::ShutDown();
|
|
|
|
return 0;
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace cast
|
|
} // namespace openscreen
|
|
|
|
int main(int argc, char* argv[]) {
|
|
return openscreen::cast::RunStandaloneReceiver(argc, argv);
|
|
}
|