CCF
Loading...
Searching...
No Matches
cli_helper.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
5#include "ccf/crypto/san.h"
6#include "ccf/ds/nonstd.h"
8
9#include <CLI11/CLI11.hpp>
10#include <charconv>
11#include <optional>
12
13#define FMT_HEADER_ONLY
14#include <fmt/format.h>
15
16namespace cli
17{
19
20 static std::pair<std::string, std::string> validate_address(
21 const ParsedAddress& addr, const std::string& default_port = "0")
22 {
23 auto found = addr.find_last_of(":");
24 auto hostname = addr.substr(0, found);
25
26 const auto port =
27 found == std::string::npos ? default_port : addr.substr(found + 1);
28
29 // Check if port is in valid range
30 uint16_t port_n = 0;
31 const auto [_, ec] =
32 std::from_chars(port.data(), port.data() + port.size(), port_n);
33 if (ec == std::errc::invalid_argument)
34 {
35 throw std::logic_error(fmt::format("Port '{}' is not a number", port));
36 }
37 else if (ec == std::errc::result_out_of_range)
38 {
39 throw std::logic_error(
40 fmt::format("Port '{}' is not in range 0-65535", port));
41 }
42 else if (ec != std::errc())
43 {
44 throw std::logic_error(fmt::format("Error parsing port '{}'", port));
45 }
46
47 return std::make_pair(hostname, port);
48 }
49
50 static bool parse_address(
51 const std::string& addr,
52 ParsedAddress& parsed,
53 const std::string& option_name,
54 const std::string& default_port = "0")
55 {
56 try
57 {
58 validate_address(addr, default_port);
59 }
60 catch (const std::exception& e)
61 {
62 throw CLI::ValidationError(option_name, e.what());
63 }
64
65 parsed = addr;
66
67 return true;
68 }
69
70 static CLI::Option* add_address_option(
71 CLI::App& app,
72 ParsedAddress& parsed,
73 const std::string& option_name,
74 const std::string& option_desc,
75 const std::string& default_port = "0")
76 {
77 CLI::callback_t fun = [&parsed, option_name, default_port](
78 CLI::results_t results) {
79 if (results.size() != 1)
80 {
81 throw CLI::ValidationError(option_name, "Address could not be parsed");
82 }
83
84 auto addr = results[0];
85 return parse_address(addr, parsed, option_name, default_port);
86 };
87
88 auto* option = app.add_option(option_name, fun, option_desc, true);
89 option->type_name("HOST:PORT");
90
91 return option;
92 }
93
94 static const std::string IP_ADDRESS_PREFIX("iPAddress:");
95 static const std::string DNS_NAME_PREFIX("dNSName:");
96
97 static CLI::Option* add_subject_alternative_name_option(
98 CLI::App& app,
99 std::vector<ccf::crypto::SubjectAltName>& parsed,
100 const std::string& option_name,
101 const std::string& option_desc)
102 {
103 CLI::callback_t fun = [&parsed, option_name](CLI::results_t results) {
104 for (auto& result : results)
105 {
106 if (result.starts_with(IP_ADDRESS_PREFIX))
107 {
108 parsed.push_back({result.substr(IP_ADDRESS_PREFIX.size()), true});
109 }
110 else if (result.starts_with(DNS_NAME_PREFIX))
111 {
112 parsed.push_back({result.substr(DNS_NAME_PREFIX.size()), false});
113 }
114 else
115 {
116 throw CLI::ValidationError(
117 option_name,
118 fmt::format(
119 "SAN could not be parsed: {}, must be (iPAddress|dNSName):VALUE",
120 result));
121 }
122 }
123
124 return true;
125 };
126
127 auto* option = app.add_option(option_name, fun, option_desc, true);
128 option->type_name("(iPAddress|dNSName):VALUE")->type_size(-1);
129
130 return option;
131 }
132}
Definition cli_helper.h:17
ccf::NodeInfoNetwork::NetAddress ParsedAddress
Definition cli_helper.h:18
std::string NetAddress
Definition node_info_network.h:86