1 #include "server-conf.hpp"
2
3 #include "utils.hpp"
4 #include "xyz/openbmc_project/Common/error.hpp"
5
6 #include <phosphor-logging/elog.hpp>
7
8 #include <fstream>
9 #if __has_include("../../usr/include/phosphor-logging/elog-errors.hpp")
10 #include "../../usr/include/phosphor-logging/elog-errors.hpp"
11 #else
12 #include <phosphor-logging/elog-errors.hpp>
13 #endif
14 #include <arpa/inet.h>
15 #include <netdb.h>
16
17 #include <optional>
18 #include <string>
19
20 namespace phosphor
21 {
22 namespace rsyslog_config
23 {
24
25 namespace utils = phosphor::rsyslog_utils;
26 using namespace phosphor::logging;
27 using namespace sdbusplus::error::xyz::openbmc_project::common;
28
29 namespace internal
30 {
31
isIPv6Address(const std::string & addr)32 bool isIPv6Address(const std::string& addr)
33 {
34 struct in6_addr result;
35 return inet_pton(AF_INET6, addr.c_str(), &result) == 1;
36 }
37
38 std::optional<
39 std::tuple<std::string, uint32_t, NetworkClient::TransportProtocol>>
parseConfig(std::istream & ss)40 parseConfig(std::istream& ss)
41 {
42 std::string line;
43 std::getline(ss, line);
44
45 std::string serverAddress;
46 std::string serverPort;
47 NetworkClient::TransportProtocol serverTransportProtocol =
48 NetworkClient::TransportProtocol::TCP;
49
50 // Ignore if line is commented
51 if (!line.empty() && '#' != line.at(0))
52 {
53 //"*.* @@<address>:<port>" or
54 //"*.* @@[<ipv6-address>:<port>"
55 auto start = line.find('@');
56 if (start == std::string::npos)
57 return {};
58
59 // Skip "*.* @@" or "*.* @"
60 if (line.at(start + 1) == '@')
61 {
62 serverTransportProtocol = NetworkClient::TransportProtocol::TCP;
63 start += 2;
64 }
65 else
66 {
67 serverTransportProtocol = NetworkClient::TransportProtocol::UDP;
68 start++;
69 }
70
71 // Check if there is "[]", and make IPv6 address from it
72 auto posColonLeft = line.find('[');
73 auto posColonRight = line.find(']');
74 if (posColonLeft != std::string::npos ||
75 posColonRight != std::string::npos)
76 {
77 // It contains [ or ], so it should be an IPv6 address
78 if (posColonLeft == std::string::npos ||
79 posColonRight == std::string::npos)
80 {
81 // There either '[' or ']', invalid config
82 return {};
83 }
84 if (line.size() < posColonRight + 2 ||
85 line.at(posColonRight + 1) != ':')
86 {
87 // There is no ':', or no more content after ':', invalid config
88 return {};
89 }
90 serverAddress =
91 line.substr(posColonLeft + 1, posColonRight - posColonLeft - 1);
92 serverPort = line.substr(posColonRight + 2);
93 }
94 else
95 {
96 auto pos = line.find(':');
97 if (pos == std::string::npos)
98 {
99 // There is no ':', invalid config
100 return {};
101 }
102 serverAddress = line.substr(start, pos - start);
103 serverPort = line.substr(pos + 1);
104 }
105 }
106 if (serverAddress.empty() || serverPort.empty())
107 {
108 return {};
109 }
110 try
111 {
112 return std::make_tuple(std::move(serverAddress), std::stoul(serverPort),
113 serverTransportProtocol);
114 }
115 catch (const std::exception& ex)
116 {
117 log<level::ERR>("Invalid config", entry("ERR=%s", ex.what()));
118 return {};
119 }
120 }
121
122 } // namespace internal
123
address(std::string value)124 std::string Server::address(std::string value)
125 {
126 using Argument = xyz::openbmc_project::common::InvalidArgument;
127 std::string result{};
128
129 try
130 {
131 auto serverAddress = address();
132 if (serverAddress == value)
133 {
134 return serverAddress;
135 }
136
137 if (!value.empty() && !addressValid(value))
138 {
139 elog<InvalidArgument>(Argument::ARGUMENT_NAME("Address"),
140 Argument::ARGUMENT_VALUE(value.c_str()));
141 }
142
143 writeConfig(value, port(), transportProtocol(), configFilePath.c_str());
144 result = NetworkClient::address(value);
145 }
146 catch (const InvalidArgument& e)
147 {
148 throw;
149 }
150 catch (const InternalFailure& e)
151 {
152 throw;
153 }
154 catch (const std::exception& e)
155 {
156 log<level::ERR>(e.what());
157 elog<InternalFailure>();
158 }
159
160 return result;
161 }
162
port(uint16_t value)163 uint16_t Server::port(uint16_t value)
164 {
165 uint16_t result{};
166
167 try
168 {
169 auto serverPort = port();
170 if (serverPort == value)
171 {
172 return serverPort;
173 }
174
175 writeConfig(address(), value, transportProtocol(),
176 configFilePath.c_str());
177 result = NetworkClient::port(value);
178 }
179 catch (const InternalFailure& e)
180 {
181 throw;
182 }
183 catch (const std::exception& e)
184 {
185 log<level::ERR>(e.what());
186 elog<InternalFailure>();
187 }
188
189 return result;
190 }
191
192 NetworkClient::TransportProtocol
transportProtocol(NetworkClient::TransportProtocol value)193 Server::transportProtocol(NetworkClient::TransportProtocol value)
194 {
195 TransportProtocol result{};
196
197 try
198 {
199 auto serverTransportProtocol = transportProtocol();
200 if (serverTransportProtocol == value)
201 {
202 return serverTransportProtocol;
203 }
204
205 writeConfig(address(), port(), value, configFilePath.c_str());
206 result = NetworkClient::transportProtocol(value);
207 }
208 catch (const InternalFailure& e)
209 {
210 throw;
211 }
212 catch (const std::exception& e)
213 {
214 log<level::ERR>(e.what());
215 elog<InternalFailure>();
216 }
217
218 return result;
219 }
220
writeConfig(const std::string & serverAddress,uint16_t serverPort,NetworkClient::TransportProtocol serverTransportProtocol,const char * filePath)221 void Server::writeConfig(
222 const std::string& serverAddress, uint16_t serverPort,
223 NetworkClient::TransportProtocol serverTransportProtocol,
224 const char* filePath)
225 {
226 std::fstream stream(filePath, std::fstream::out);
227
228 if (serverPort && !serverAddress.empty())
229 {
230 std::string type =
231 (serverTransportProtocol == NetworkClient::TransportProtocol::UDP)
232 ? "@"
233 : "@@";
234 // write '*.* @@<remote-host>:<port>' or '*.* @<remote-host>:<port>'
235 if (internal::isIPv6Address(serverAddress))
236 {
237 stream << "*.* " << type << "[" << serverAddress
238 << "]:" << serverPort;
239 }
240 else
241 {
242 stream << "*.* " << type << serverAddress << ":" << serverPort;
243 }
244 }
245 else // this is a disable request
246 {
247 // dummy action to avoid error 2103 on startup
248 stream << "*.* /dev/null";
249 }
250
251 stream << std::endl;
252
253 restart();
254 }
255
addressValid(const std::string & address)256 bool Server::addressValid(const std::string& address)
257 {
258 addrinfo hints{};
259 addrinfo* res = nullptr;
260 hints.ai_family = AF_UNSPEC;
261 hints.ai_socktype = SOCK_STREAM;
262 hints.ai_flags |= AI_CANONNAME;
263
264 auto result = getaddrinfo(address.c_str(), nullptr, &hints, &res);
265 if (result)
266 {
267 log<level::ERR>("bad address", entry("ADDRESS=%s", address.c_str()),
268 entry("ERRNO=%d", result));
269 return false;
270 }
271
272 freeaddrinfo(res);
273 return true;
274 }
275
restore(const char * filePath)276 void Server::restore(const char* filePath)
277 {
278 std::fstream stream(filePath, std::fstream::in);
279
280 auto ret = internal::parseConfig(stream);
281 if (ret)
282 {
283 NetworkClient::address(std::get<0>(*ret));
284 NetworkClient::port(std::get<1>(*ret));
285 NetworkClient::transportProtocol(std::get<2>(*ret));
286 }
287 }
288
restart()289 void Server::restart()
290 {
291 utils::restart();
292 }
293
294 } // namespace rsyslog_config
295 } // namespace phosphor
296