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 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>> 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 = line.substr(posColonLeft + 1, 91 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 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 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 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 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 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 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 289 void Server::restart() 290 { 291 utils::restart(); 292 } 293 294 } // namespace rsyslog_config 295 } // namespace phosphor 296