1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <getopt.h>
6
7 #include <algorithm>
8 #include <iostream>
9 #include <memory>
10 #include <string>
11 #include <utility>
12 #include <vector>
13
14 #include "absl/strings/str_cat.h"
15 #include "cast/receiver/channel/static_credentials.h"
16 #include "cast/standalone_receiver/cast_service.h"
17 #include "platform/api/time.h"
18 #include "platform/base/error.h"
19 #include "platform/base/ip_address.h"
20 #include "platform/impl/logging.h"
21 #include "platform/impl/network_interface.h"
22 #include "platform/impl/platform_client_posix.h"
23 #include "platform/impl/task_runner.h"
24 #include "platform/impl/text_trace_logging_platform.h"
25 #include "util/chrono_helpers.h"
26 #include "util/stringprintf.h"
27 #include "util/trace_logging.h"
28
29 namespace openscreen {
30 namespace cast {
31 namespace {
32
LogUsage(const char * argv0)33 void LogUsage(const char* argv0) {
34 constexpr char kTemplate[] = R"(
35 usage: %s <options> <interface>
36
37 interface
38 Specifies the network interface to bind to. The interface is
39 looked up from the system interface registry.
40 Mandatory, as it must be known for publishing discovery.
41
42 options:
43 -p, --private-key=path-to-key: Path to OpenSSL-generated private key to be
44 used for TLS authentication. If a private key is not
45 provided, a randomly generated one will be used for this
46 session.
47
48 -d, --developer-certificate=path-to-cert: Path to PEM file containing a
49 developer generated server root TLS certificate.
50 If a root server certificate is not provided, one
51 will be generated using a randomly generated
52 private key. Note that if a certificate path is
53 passed, the private key path is a mandatory field.
54
55 -g, --generate-credentials: Instructs the binary to generate a private key
56 and self-signed root certificate with the CA
57 bit set to true, and then exit. The resulting
58 private key and certificate can then be used as
59 values for the -p and -s flags.
60
61 -f, --friendly-name: Friendly name to be used for receiver discovery.
62
63 -m, --model-name: Model name to be used for receiver discovery.
64
65 -t, --tracing: Enable performance tracing logging.
66
67 -v, --verbose: Enable verbose logging.
68
69 -h, --help: Show this help message.
70
71 -x, --disable-discovery: Disable discovery, useful for platforms like Mac OS
72 where our implementation is incompatible with
73 the native Bonjour service.
74
75 )";
76
77 std::cerr << StringPrintf(kTemplate, argv0);
78 }
79
GetInterfaceInfoFromName(const char * name)80 InterfaceInfo GetInterfaceInfoFromName(const char* name) {
81 OSP_CHECK(name != nullptr) << "Missing mandatory argument: interface.";
82 InterfaceInfo interface_info;
83 std::vector<InterfaceInfo> network_interfaces = GetNetworkInterfaces();
84 for (auto& interface : network_interfaces) {
85 if (interface.name == name) {
86 interface_info = std::move(interface);
87 break;
88 }
89 }
90
91 if (interface_info.name.empty()) {
92 auto error_or_info = GetLoopbackInterfaceForTesting();
93 if (error_or_info.has_value()) {
94 if (error_or_info.value().name == name) {
95 interface_info = std::move(error_or_info.value());
96 }
97 }
98 }
99 OSP_CHECK(!interface_info.name.empty()) << "Invalid interface specified.";
100 return interface_info;
101 }
102
RunCastService(TaskRunnerImpl * runner,CastService::Configuration config)103 void RunCastService(TaskRunnerImpl* runner, CastService::Configuration config) {
104 std::unique_ptr<CastService> service;
105 runner->PostTask(
106 [&] { service = std::make_unique<CastService>(std::move(config)); });
107
108 OSP_LOG_INFO << "CastService is running. CTRL-C (SIGINT), or send a "
109 "SIGTERM to exit.";
110 runner->RunUntilSignaled();
111
112 // Spin the TaskRunner to execute destruction/shutdown tasks.
113 OSP_LOG_INFO << "Shutting down...";
114 runner->PostTask([&] {
115 service.reset();
116 runner->RequestStopSoon();
117 });
118 runner->RunUntilStopped();
119 OSP_LOG_INFO << "Bye!";
120 }
121
RunStandaloneReceiver(int argc,char * argv[])122 int RunStandaloneReceiver(int argc, char* argv[]) {
123 #if !defined(CAST_ALLOW_DEVELOPER_CERTIFICATE)
124 OSP_LOG_FATAL
125 << "It compiled! However cast_receiver currently only supports using a "
126 "passed-in certificate and private key, and must be built with "
127 "cast_allow_developer_certificate=true set in the GN args to "
128 "actually do anything interesting.";
129 return 1;
130 #endif
131
132 #if !defined(CAST_STANDALONE_RECEIVER_HAVE_EXTERNAL_LIBS)
133 OSP_LOG_INFO
134 << "Note: compiled without external libs. The dummy player will "
135 "be linked and no video decoding will occur. If this is not desired, "
136 "install the required external libraries. For more information, see: "
137 "[external_libraries.md](../../build/config/external_libraries.md).";
138 #endif
139
140 // A note about modifying command line arguments: consider uniformity
141 // between all Open Screen executables. If it is a platform feature
142 // being exposed, consider if it applies to the standalone receiver,
143 // standalone sender, osp demo, and test_main argument options.
144 const struct option kArgumentOptions[] = {
145 {"private-key", required_argument, nullptr, 'p'},
146 {"developer-certificate", required_argument, nullptr, 'd'},
147 {"generate-credentials", no_argument, nullptr, 'g'},
148 {"friendly-name", required_argument, nullptr, 'f'},
149 {"model-name", required_argument, nullptr, 'm'},
150 {"tracing", no_argument, nullptr, 't'},
151 {"verbose", no_argument, nullptr, 'v'},
152 {"help", no_argument, nullptr, 'h'},
153
154 // Discovery is enabled by default, however there are cases where it
155 // needs to be disabled, such as on Mac OS X.
156 {"disable-discovery", no_argument, nullptr, 'x'},
157 {nullptr, 0, nullptr, 0}};
158
159 bool is_verbose = false;
160 bool enable_discovery = true;
161 std::string private_key_path;
162 std::string developer_certificate_path;
163 std::string friendly_name = "Cast Standalone Receiver";
164 std::string model_name = "cast_standalone_receiver";
165 bool should_generate_credentials = false;
166 std::unique_ptr<TextTraceLoggingPlatform> trace_logger;
167 int ch = -1;
168 while ((ch = getopt_long(argc, argv, "p:d:f:m:grtvhx", kArgumentOptions,
169 nullptr)) != -1) {
170 switch (ch) {
171 case 'p':
172 private_key_path = optarg;
173 break;
174 case 'd':
175 developer_certificate_path = optarg;
176 break;
177 case 'f':
178 friendly_name = optarg;
179 break;
180 case 'm':
181 model_name = optarg;
182 break;
183 case 'g':
184 should_generate_credentials = true;
185 break;
186 case 't':
187 trace_logger = std::make_unique<TextTraceLoggingPlatform>();
188 break;
189 case 'v':
190 is_verbose = true;
191 break;
192 case 'x':
193 enable_discovery = false;
194 break;
195 case 'h':
196 LogUsage(argv[0]);
197 return 1;
198 }
199 }
200
201 SetLogLevel(is_verbose ? LogLevel::kVerbose : LogLevel::kInfo);
202
203 // Either -g is required, or both -p and -d.
204 if (should_generate_credentials) {
205 GenerateDeveloperCredentialsToFile();
206 return 0;
207 }
208 if (private_key_path.empty() || developer_certificate_path.empty()) {
209 OSP_LOG_FATAL << "You must either invoke with -g to generate credentials, "
210 "or provide both a private key path and root certificate "
211 "using -p and -d";
212 return 1;
213 }
214
215 const char* interface_name = argv[optind];
216 OSP_CHECK(interface_name && strlen(interface_name) > 0)
217 << "No interface name provided.";
218
219 std::string receiver_id =
220 absl::StrCat("Standalone Receiver on ", interface_name);
221 ErrorOr<GeneratedCredentials> creds = GenerateCredentials(
222 receiver_id, private_key_path, developer_certificate_path);
223 OSP_CHECK(creds.is_value()) << creds.error();
224
225 const InterfaceInfo interface = GetInterfaceInfoFromName(interface_name);
226 OSP_CHECK(interface.GetIpAddressV4() || interface.GetIpAddressV6());
227
228 auto* const task_runner = new TaskRunnerImpl(&Clock::now);
229 PlatformClientPosix::Create(milliseconds(50),
230 std::unique_ptr<TaskRunnerImpl>(task_runner));
231 RunCastService(task_runner,
232 CastService::Configuration{
233 task_runner, interface, std::move(creds.value()),
234 friendly_name, model_name, enable_discovery});
235 PlatformClientPosix::ShutDown();
236
237 return 0;
238 }
239
240 } // namespace
241 } // namespace cast
242 } // namespace openscreen
243
main(int argc,char * argv[])244 int main(int argc, char* argv[]) {
245 return openscreen::cast::RunStandaloneReceiver(argc, argv);
246 }
247