blob: 8a5c42cdb4e083c9f04bfab5278f3450733d3593 [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 "src/connectivity/network/mdns/util/commands.h"
#include <lib/syslog/cpp/macros.h>
#include <functional>
#include <iostream>
#include "src/lib/fxl/strings/split_string.h"
namespace mdns {
namespace {
// Concatenates argv[1]..argv[argc - 1] with space separators.
std::string Concatenate(int argc, const char** argv) {
if (argc == 1) {
return "";
}
std::string result;
size_t result_size = 0;
for (int i = 1; i < argc; ++i) {
result_size += strlen(argv[i]) + 1;
}
result_size -= 1;
result.reserve(result_size);
result.append(argv[1]);
for (int i = 2; i < argc; ++i) {
result.append(" ");
result.append(argv[i]);
}
return result;
}
const std::string kTimeoutOption = "timeout";
const std::string kMediaOption = "media";
const std::string kIpVersionsOption = "ip-versions";
const std::string kExcludeLocalOption = "exclude-local";
const std::string kExcludeLocalProxyOption = "exclude-local-proxies";
const std::string kProbeOption = "probe";
const std::string kSrvPriorityOption = "srv-priority";
const std::string kSrvWeightOption = "srv-weight";
const std::string kPtrTtlOption = "ptr-ttl";
const std::string kSrvTtlOption = "srv-ttl";
const std::string kTxtTtlOption = "txt-ttl";
const std::string kProxyHostOption = "proxy-host";
const std::string kBoolValueTrue = "true";
const std::string kBoolValueFalse = "false";
const std::string kMediaValueWired = "wired";
const std::string kMediaValueWireless = "wireless";
const std::string kIpVersionsValueV4 = "4";
const std::string kIpVersionsValueV6 = "6";
const std::string kLabelSeparator = ".";
const std::string kValueSeparator = ",";
const std::string kOptionPrefix = "--";
const std::string kOptionSeparator = "=";
const std::string kHostSuffix = "local.";
const std::string kTcpSuffix = "._tcp.";
const std::string kUdpSuffix = "._udp.";
constexpr size_t kMaxServiceNameLabelLength = 16;
constexpr size_t kMaxHostNameLength = 253 - 6; // 6 for local domain.
} // namespace
const std::string Command::kResolve = "resolve";
const std::string Command::kSubscribe = "subscribe";
const std::string Command::kPublish = "publish";
const std::string Command::kUnsubscribe = "unsubscribe";
const std::string Command::kUnpublish = "unpublish";
const std::string Command::kHelp = "help";
const std::string Command::kQuit = "quit";
const std::string Command::kAllServices = "all";
CommandParser::CommandParser(const std::string& command_line) : str_(command_line) {}
CommandParser::CommandParser(int argc, const char** argv)
: CommandParser(Concatenate(argc, argv)) {}
Command CommandParser::Parse() {
MatchWhitespace();
if (MatchEnd()) {
// Whitespace only.
return Command::Empty();
} else if (MatchLiteral(Command::kResolve)) {
if (!MatchWhitespace()) {
return Command::Malformed();
}
std::string host_name;
std::string service_name;
std::string instance_name;
if (MatchHostName(host_name)) {
auto command = Command::ResolveHost(host_name);
if (MatchOptions(command)) {
return command;
} else {
return Command::Help(CommandVerb::kResolveHost);
}
} else if (MatchInstanceName(instance_name, service_name)) {
auto command = Command::ResolveInstance(instance_name, service_name);
if (MatchOptions(command)) {
return command;
} else {
return Command::Help(CommandVerb::kResolveInstance);
}
} else {
return Command::Help(CommandVerb::kResolveHost);
}
} else if (MatchLiteral(Command::kSubscribe)) {
if (!MatchWhitespace()) {
return Command::Malformed();
}
std::string host_name;
std::string service_name;
if (MatchHostName(host_name)) {
auto command = Command::SubscribeHost(host_name);
if (MatchOptions(command)) {
return command;
} else {
return Command::Help(CommandVerb::kSubscribeHost);
}
} else if (MatchServiceName(service_name)) {
auto command = Command::SubscribeService(service_name);
if (MatchOptions(command)) {
return command;
} else {
return Command::Help(CommandVerb::kSubscribeService);
}
} else if (MatchLiteral(Command::kAllServices)) {
auto command = Command::SubscribeService(Command::kAllServices);
if (MatchOptions(command)) {
return command;
} else {
return Command::Help(CommandVerb::kSubscribeService);
}
} else {
return Command::Help(CommandVerb::kSubscribeHost);
}
} else if (MatchLiteral(Command::kPublish)) {
if (!MatchWhitespace()) {
return Command::Malformed();
}
std::string host_name;
std::string service_name;
std::string instance_name;
if (MatchHostName(host_name)) {
std::vector<inet::IpAddress> addresses;
if (!MatchWhitespace() || !(MatchAddresses(addresses))) {
return Command::Help(CommandVerb::kPublishHost);
}
auto command = Command::PublishHost(host_name, addresses);
if (MatchOptions(command)) {
return command;
} else {
return Command::Help(CommandVerb::kPublishHost);
}
} else if (MatchInstanceName(instance_name, service_name)) {
uint16_t port;
std::vector<std::string> text;
if (!MatchWhitespace() || !(MatchUint16(port)) || !MatchWhitespace() || !(MatchText(text))) {
return Command::Help(CommandVerb::kPublishInstance);
}
auto command = Command::PublishInstance(instance_name, service_name, port, text);
if (MatchOptions(command)) {
return command;
} else {
return Command::Help(CommandVerb::kPublishInstance);
}
} else {
return Command::Help(CommandVerb::kUnpublishHost);
}
} else if (MatchLiteral(Command::kUnsubscribe)) {
if (!MatchWhitespace()) {
return Command::Malformed();
}
std::string host_name;
std::string service_name;
if (MatchHostName(host_name)) {
auto command = Command::UnsubscribeHost(host_name);
if (MatchOptions(command)) {
return command;
} else {
return Command::Help(CommandVerb::kUnsubscribeHost);
}
} else if (MatchServiceName(service_name)) {
auto command = Command::UnsubscribeService(service_name);
if (MatchOptions(command)) {
return command;
} else {
return Command::Help(CommandVerb::kUnsubscribeService);
}
} else if (MatchLiteral(Command::kAllServices)) {
auto command = Command::UnsubscribeService(Command::kAllServices);
if (MatchOptions(command)) {
return command;
} else {
return Command::Help(CommandVerb::kSubscribeService);
}
} else {
return Command::Help(CommandVerb::kUnsubscribeHost);
}
} else if (MatchLiteral(Command::kUnpublish)) {
if (!MatchWhitespace()) {
return Command::Malformed();
}
std::string host_name;
std::string service_name;
std::string instance_name;
if (MatchHostName(host_name)) {
auto command = Command::UnpublishHost(host_name);
if (MatchOptions(command)) {
return command;
} else {
return Command::Help(CommandVerb::kUnpublishHost);
}
} else if (MatchInstanceName(instance_name, service_name)) {
auto command = Command::UnpublishInstance(instance_name, service_name);
if (MatchOptions(command)) {
return command;
} else {
return Command::Help(CommandVerb::kUnpublishHost);
}
} else {
return Command::Help(CommandVerb::kUnpublishHost);
}
} else if (MatchLiteral(Command::kHelp)) {
if (MatchWhitespaceEnd()) {
return Command::Help();
}
if (!MatchWhitespace()) {
return Command::Malformed();
}
// Here we are using e.g. |CommandVerb::kResolveHost| to specify help for both the 'resolve'
// commands (both kResolveHost or kResolveInstance).
if (MatchLiteral(Command::kResolve)) {
return MatchWhitespaceEnd() ? Command::Help(CommandVerb::kResolveHost) : Command::Malformed();
} else if (MatchLiteral(Command::kSubscribe)) {
return MatchWhitespaceEnd() ? Command::Help(CommandVerb::kSubscribeHost)
: Command::Malformed();
} else if (MatchLiteral(Command::kPublish)) {
return MatchWhitespaceEnd() ? Command::Help(CommandVerb::kPublishHost) : Command::Malformed();
} else if (MatchLiteral(Command::kUnsubscribe)) {
return MatchWhitespaceEnd() ? Command::Help(CommandVerb::kUnsubscribeHost)
: Command::Malformed();
return Command::Help(CommandVerb::kUnsubscribeHost);
} else if (MatchLiteral(Command::kUnpublish)) {
return MatchWhitespaceEnd() ? Command::Help(CommandVerb::kUnpublishHost)
: Command::Malformed();
} else {
return Command::Malformed();
}
} else if (MatchLiteral(Command::kQuit)) {
if (MatchWhitespaceEnd()) {
return Command::Quit();
} else {
return Command::Malformed();
}
} else {
return Command::Malformed();
}
}
bool CommandParser::MatchOptions(Command& command) {
size_t saved_chars_remaining = chars_remaining();
while (true) {
if (MatchWhitespaceEnd()) {
return true;
}
if (!MatchWhitespace()) {
break;
}
if (!MatchLiteral(kOptionPrefix)) {
break;
}
if (MatchLiteral(kTimeoutOption)) {
switch (command.verb()) {
case CommandVerb::kResolveHost:
case CommandVerb::kResolveInstance:
break;
default:
SetCharsRemaining(saved_chars_remaining);
return false;
}
if (!MatchLiteral(kOptionSeparator) || !MatchSeconds(command.timeout_)) {
break;
}
} else if (MatchLiteral(kMediaOption)) {
switch (command.verb()) {
case CommandVerb::kResolveHost:
case CommandVerb::kResolveInstance:
case CommandVerb::kSubscribeHost:
case CommandVerb::kSubscribeService:
case CommandVerb::kPublishHost:
case CommandVerb::kPublishInstance:
break;
default:
SetCharsRemaining(saved_chars_remaining);
return false;
}
if (!MatchLiteral(kOptionSeparator) || !MatchMedia(command.media_)) {
break;
}
} else if (MatchLiteral(kIpVersionsOption)) {
switch (command.verb()) {
case CommandVerb::kResolveHost:
case CommandVerb::kResolveInstance:
case CommandVerb::kSubscribeHost:
case CommandVerb::kSubscribeService:
case CommandVerb::kPublishHost:
case CommandVerb::kPublishInstance:
break;
default:
SetCharsRemaining(saved_chars_remaining);
return false;
}
if (!MatchLiteral(kOptionSeparator) || !MatchIpVersions(command.ip_versions_)) {
break;
}
} else if (MatchLiteral(kExcludeLocalOption)) {
switch (command.verb()) {
case CommandVerb::kResolveHost:
case CommandVerb::kResolveInstance:
case CommandVerb::kSubscribeHost:
case CommandVerb::kSubscribeService:
break;
default:
SetCharsRemaining(saved_chars_remaining);
return false;
}
if (!MatchLiteral(kOptionSeparator) || !MatchBool(command.exclude_local_)) {
break;
}
} else if (MatchLiteral(kExcludeLocalProxyOption)) {
switch (command.verb()) {
case CommandVerb::kResolveHost:
case CommandVerb::kResolveInstance:
case CommandVerb::kSubscribeHost:
case CommandVerb::kSubscribeService:
break;
default:
SetCharsRemaining(saved_chars_remaining);
return false;
}
if (!MatchLiteral(kOptionSeparator) || !MatchBool(command.exclude_local_proxies_)) {
break;
}
} else if (MatchLiteral(kProbeOption)) {
switch (command.verb()) {
case CommandVerb::kPublishHost:
case CommandVerb::kPublishInstance:
break;
default:
SetCharsRemaining(saved_chars_remaining);
return false;
}
if (!MatchLiteral(kOptionSeparator) || !MatchBool(command.probe_)) {
break;
}
} else if (MatchLiteral(kSrvPriorityOption)) {
if (command.verb() != CommandVerb::kPublishInstance) {
break;
}
if (!MatchLiteral(kOptionSeparator) || !MatchUint16(command.srv_priority_)) {
break;
}
} else if (MatchLiteral(kSrvWeightOption)) {
if (command.verb() != CommandVerb::kPublishInstance) {
break;
}
if (!MatchLiteral(kOptionSeparator) || !MatchUint16(command.srv_weight_)) {
break;
}
} else if (MatchLiteral(kPtrTtlOption)) {
if (command.verb() != CommandVerb::kPublishInstance) {
break;
}
if (!MatchLiteral(kOptionSeparator) || !MatchSeconds(command.ptr_ttl_)) {
break;
}
} else if (MatchLiteral(kSrvTtlOption)) {
if (command.verb() != CommandVerb::kPublishInstance) {
break;
}
if (!MatchLiteral(kOptionSeparator) || !MatchSeconds(command.srv_ttl_)) {
break;
}
} else if (MatchLiteral(kTxtTtlOption)) {
if (command.verb() != CommandVerb::kPublishInstance) {
break;
}
if (!MatchLiteral(kOptionSeparator) || !MatchSeconds(command.txt_ttl_)) {
break;
}
} else if (MatchLiteral(kProxyHostOption)) {
switch (command.verb()) {
case CommandVerb::kPublishInstance:
case CommandVerb::kUnpublishInstance:
break;
default:
SetCharsRemaining(saved_chars_remaining);
return false;
}
if (!MatchLiteral(kOptionSeparator) || !MatchHostName(command.proxy_host_name_)) {
break;
}
}
}
SetCharsRemaining(saved_chars_remaining);
return false;
}
bool CommandParser::MatchHostName(std::string& value_out) {
size_t saved_chars_remaining = chars_remaining();
std::string label;
if (!MatchDnsLabel(label)) {
return false;
}
std::string value = std::move(label);
while (!MatchEnd() && value.size() <= kMaxHostNameLength) {
if (!MatchLiteral(kLabelSeparator)) {
break;
}
if (MatchLiteral(kHostSuffix)) {
value_out = value;
return true;
}
value.append(kLabelSeparator);
if (!MatchDnsLabel(label)) {
break;
}
value.append(label);
}
SetCharsRemaining(saved_chars_remaining);
return false;
}
bool CommandParser::MatchInstanceName(std::string& instance_name_out,
std::string& service_name_out) {
size_t saved_chars_remaining = chars_remaining();
std::string instance_name;
if (MatchDnsLabel(instance_name) && MatchLiteral(kLabelSeparator) &&
MatchServiceName(service_name_out)) {
instance_name_out = std::move(instance_name);
return true;
}
SetCharsRemaining(saved_chars_remaining);
return false;
}
bool CommandParser::MatchServiceName(std::string& value_out) {
size_t saved_chars_remaining = chars_remaining();
if (!MatchLiteral("_")) {
return false;
}
std::string label;
if (MatchDnsLabel(label, kMaxServiceNameLabelLength - 1)) {
if (MatchLiteral(kTcpSuffix)) {
value_out = "_";
value_out.append(label);
value_out.append(kTcpSuffix);
return true;
} else if (MatchLiteral(kUdpSuffix)) {
value_out = "_";
value_out.append(label);
value_out.append(kUdpSuffix);
return true;
}
}
SetCharsRemaining(saved_chars_remaining);
return false;
}
bool CommandParser::MatchUint16(uint16_t& value_out) {
size_t saved_chars_remaining = chars_remaining();
uint8_t digit;
if (!MatchDigit(digit)) {
return false;
}
uint16_t value = digit;
while (MatchDigit(digit)) {
if (value > (std::numeric_limits<uint16_t>::max() - digit) / 10) {
// Too large.
SetCharsRemaining(saved_chars_remaining);
return false;
}
value = value * 10 + digit;
}
value_out = value;
return true;
}
bool CommandParser::MatchText(std::vector<std::string>& value_out) {
std::vector<std::string> value;
while (true) {
std::string text_string;
if (!MatchTextString(text_string)) {
break;
}
value.push_back(text_string);
MatchWhitespace();
}
value_out = std::move(value);
return true;
}
bool CommandParser::MatchTextString(std::string& value_out) {
size_t saved_chars_remaining = chars_remaining();
std::string terminator = "\"";
if (!MatchLiteral(terminator) && (terminator = "'", !MatchLiteral(terminator))) {
return false;
}
auto start = remaining_chars();
while (!MatchLiteral(terminator)) {
if (MatchEnd()) {
SetCharsRemaining(saved_chars_remaining);
return false;
}
ConsumeChars(1);
}
value_out = std::string(start, (remaining_chars() - start) - 1);
return true;
}
bool CommandParser::MatchAddresses(std::vector<inet::IpAddress>& value_out) {
size_t saved_chars_remaining = chars_remaining();
inet::IpAddress address;
if (!MatchAddress(address)) {
return false;
}
std::vector<inet::IpAddress> value;
value.push_back(address);
while (MatchLiteral(",")) {
if (!MatchAddress(address)) {
SetCharsRemaining(saved_chars_remaining);
return false;
}
value.push_back(address);
}
value_out = std::move(value);
return true;
}
bool CommandParser::MatchAddress(inet::IpAddress& value_out) {
auto pair =
inet::IpAddress::FromStringView(std::string_view(remaining_chars(), chars_remaining()));
if (!pair.first.is_valid()) {
return false;
}
value_out = pair.first;
ConsumeChars(pair.second);
return true;
}
bool CommandParser::MatchDnsLabel(std::string& value_out, size_t max_length) {
size_t saved_chars_remaining = chars_remaining();
std::string value;
while (!MatchEnd() && *remaining_chars() != '.' && !isspace(*remaining_chars())) {
if (value.size() == max_length) {
// Too long.
value.clear();
break;
}
value.push_back(*remaining_chars());
ConsumeChars(1);
}
if (value.empty()) {
SetCharsRemaining(saved_chars_remaining);
return false;
}
value_out = value;
return true;
}
bool CommandParser::MatchSeconds(zx::duration& value_out) {
size_t saved_chars_remaining = chars_remaining();
uint8_t digit;
if (!MatchDigit(digit)) {
return false;
}
uint32_t value = digit;
while (MatchDigit(digit)) {
if (value > (std::numeric_limits<uint32_t>::max() - digit) / 10) {
// Too large.
SetCharsRemaining(saved_chars_remaining);
return false;
}
value = value * 10 + digit;
}
value_out = zx::sec(value);
return true;
}
bool CommandParser::MatchMedia(fuchsia::net::mdns::Media& value_out) {
size_t saved_chars_remaining = chars_remaining();
fuchsia::net::mdns::Media value;
if (!MatchMedium(value)) {
return false;
}
while (MatchLiteral(kValueSeparator)) {
fuchsia::net::mdns::Media v;
if (!MatchMedium(v)) {
SetCharsRemaining(saved_chars_remaining);
return false;
}
value |= v;
}
value_out = value;
return true;
}
bool CommandParser::MatchMedium(fuchsia::net::mdns::Media& value_out) {
if (MatchLiteral(kMediaValueWired)) {
value_out = fuchsia::net::mdns::Media::WIRED;
return true;
}
if (MatchLiteral(kMediaValueWireless)) {
value_out = fuchsia::net::mdns::Media::WIRELESS;
return true;
}
return false;
}
bool CommandParser::MatchIpVersions(fuchsia::net::mdns::IpVersions& value_out) {
size_t saved_chars_remaining = chars_remaining();
fuchsia::net::mdns::IpVersions value;
if (!MatchIpVersion(value)) {
return false;
}
while (MatchLiteral(kValueSeparator)) {
fuchsia::net::mdns::IpVersions v;
if (!MatchIpVersion(v)) {
SetCharsRemaining(saved_chars_remaining);
return false;
}
value |= v;
}
value_out = value;
return true;
}
bool CommandParser::MatchIpVersion(fuchsia::net::mdns::IpVersions& value_out) {
if (MatchLiteral(kIpVersionsValueV4)) {
value_out = fuchsia::net::mdns::IpVersions::V4;
return true;
}
if (MatchLiteral(kIpVersionsValueV6)) {
value_out = fuchsia::net::mdns::IpVersions::V6;
return true;
}
return false;
}
bool CommandParser::MatchBool(bool& value_out) {
if (MatchLiteral(kBoolValueTrue)) {
value_out = true;
return true;
}
if (MatchLiteral(kBoolValueFalse)) {
value_out = false;
return true;
}
return false;
}
bool CommandParser::MatchLiteral(const std::string& literal) {
if (chars_remaining() < literal.size()) {
return false;
}
if (strncmp(literal.data(), remaining_chars(), literal.size()) != 0) {
return false;
}
ConsumeChars(literal.size());
return true;
}
bool CommandParser::MatchDigit(uint8_t& value_out) {
if (MatchEnd() || !isdigit(*remaining_chars())) {
return false;
}
value_out = *remaining_chars() - '0';
ConsumeChars(1);
return true;
}
bool CommandParser::MatchWhitespace() {
if (MatchEnd()) {
return false;
}
if (!isspace(*remaining_chars())) {
return false;
}
for (ConsumeChars(1); !MatchEnd() && isspace(*remaining_chars()); ConsumeChars(1)) {
}
return true;
}
bool CommandParser::MatchWhitespaceEnd() {
size_t saved_chars_remaining = chars_remaining();
MatchWhitespace();
if (MatchEnd()) {
return true;
}
SetCharsRemaining(saved_chars_remaining);
return false;
}
// static
void Command::ShowHelp(CommandVerb command_verb) {
switch (command_verb) {
case CommandVerb::kResolveHost:
case CommandVerb::kResolveInstance:
std::cout << "resolve <host name>\n";
std::cout << " resolves a host\n";
std::cout << " <host name>: name of host including `.local.` suffix\n";
std::cout << " --timeout=<seconds> default: '3'\n";
std::cout << " --media=<media> default: `wired,wireless`\n";
std::cout << " --ip-versions=<versions> default: `4,6`\n";
std::cout << " --exclude-local=<true|false> default: `false`\n";
std::cout << " --exclude-local-proxies=<true|false> default: `false`\n";
std::cout << "\n";
std::cout << "resolve <instance name>\n";
std::cout << " resolves a service instance\n";
std::cout << " <instance name>: instance.service including `._tcp.` or `._udp.` suffix\n";
std::cout << " --timeout=<seconds> default: '3'\n";
std::cout << " --media=<media> default: `wired,wireless`\n";
std::cout << " --ip-versions=<versions> default: `4,6`\n";
std::cout << " --exclude-local=<true|false> default: `false`\n";
std::cout << " --exclude-local-proxies=<true|false> default: `false`\n";
break;
case CommandVerb::kSubscribeHost:
case CommandVerb::kSubscribeService:
std::cout << "subscribe <host name>\n";
std::cout << " subscribes to a host\n";
std::cout << " <host name>: name of host including `.local.` suffix\n";
std::cout << " --media=<media> default: `wired,wireless`\n";
std::cout << " --ip-versions=<versions> default: `4,6`\n";
std::cout << " --exclude-local=<true|false> default: `false`\n";
std::cout << " --exclude-local-proxies=<true|false> default: `false`\n";
std::cout << "\n";
std::cout << "subscribe <service name>\n";
std::cout << " subscribes to a service\n";
std::cout << " <service name>: name of service including `._tcp.` or `._udp.` suffix\n";
std::cout << " --media=<media> default: `wired,wireless`\n";
std::cout << " --ip-versions=<versions> default: `4,6`\n";
std::cout << " --exclude-local=<true|false> default: `false`\n";
std::cout << " --exclude-local-proxies=<true|false> default: `false`\n";
std::cout << "\n";
std::cout << "subscribe all\n";
std::cout << " subscribes to all services\n";
std::cout << " --media=<media> default: `wired,wireless`\n";
std::cout << " --ip-versions=<versions> default: `4,6`\n";
std::cout << " --exclude-local=<true|false> default: `false`\n";
std::cout << " --exclude-local-proxies=<true|false> default: `false`\n";
break;
case CommandVerb::kPublishHost:
case CommandVerb::kPublishInstance:
std::cout << "publish <host name> <addresses>\n";
std::cout << " publishes a proxy host\n";
std::cout << " <host name>: name of proxy host including `.local.` suffix\n";
std::cout << " <addresses>: IP addresses of proxy host\n";
std::cout << " --probe=<true|false> default: `true`\n";
std::cout << " --media=<media> default: `wired,wireless`\n";
std::cout << " --ip-versions=<versions> default: `4,6`\n";
std::cout << "\n";
std::cout << "publish <instance name> <port> <text>*\n";
std::cout << " publishes to a service instance\n";
std::cout << " <instance name>: instance.service including `._tcp.` or `._udp.` suffix\n";
std::cout << " <port>: port number (decimal)\n";
std::cout << " <text>: text string (in single or double quotes)\n";
std::cout << " --probe=<true|false> default: `true`\n";
std::cout << " --media=<media> default: `wired,wireless`\n";
std::cout << " --ip-versions=<versions> default: `4,6`\n";
std::cout << " --srv-priority=<uint16> default: `0`\n";
std::cout << " --srv-weight=<uint16> default: `0`\n";
std::cout << " --ptr-ttl=<seconds> default: `4500`\n";
std::cout << " --srv-ttl=<seconds> default: `120`\n";
std::cout << " --txt-ttl=<seconds> default: `4500`\n";
std::cout << " --proxy-host=<host name> default: none (local)\n";
break;
case CommandVerb::kUnsubscribeHost:
case CommandVerb::kUnsubscribeService:
std::cout << "unsubscribe <host name>\n";
std::cout << " cancels 'subscribe <host name>'\n";
std::cout << " <host name>: name of host including `.local.` suffix\n";
std::cout << "\n";
std::cout << "unsubscribe <service name>\n";
std::cout << " cancels 'subscribe <service name>'\n";
std::cout << " <service name>: name of service including `._tcp.` or `._udp.` suffix\n";
std::cout << "\n";
std::cout << "unsubscribe all\n";
std::cout << " cancels 'subscribe all'\n";
break;
case CommandVerb::kUnpublishHost:
case CommandVerb::kUnpublishInstance:
std::cout << "unpublish <host name>\n";
std::cout << " cancels `publish <host name> ...`\n";
std::cout << " <host name>: name of proxy host including `.local.` suffix\n";
std::cout << "\n";
std::cout << "unpublish <instance name>\n";
std::cout << " cancels `publish <instance name> ...`\n";
std::cout << " <instance name>: instance.service including `._tcp.` or `._udp.` suffix\n";
std::cout << " --proxy-host=<host name> default: none (local)\n";
break;
case CommandVerb::kHelp:
case CommandVerb::kQuit:
case CommandVerb::kEmpty:
case CommandVerb::kMalformed:
std::cout << "commands:\n";
std::cout << " resolve <host name> | <instance name>\n";
std::cout << " subscribe <host name> | <service name>\n";
std::cout << " publish ( <host name> <addresses>) | ( <instance name> <port> <text>) \n";
std::cout << " unsubscribe <host name> | <service_name>\n";
std::cout << " unpublish <host name> | <instance name>\n";
std::cout << " quit\n";
std::cout << " help\n";
std::cout << " help <command>\n";
break;
}
}
void Command::ShowHelp() const { ShowHelp(help_verb()); }
} // namespace mdns