1 // Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <netconf/netconf_log.h>
10 #include <netconf/netconf_cfg_mgr.h>
11 #include <exceptions/exceptions.h>
12 #include <asiolink/io_error.h>
13 
14 #include <boost/foreach.hpp>
15 #include <boost/scoped_ptr.hpp>
16 #include <boost/algorithm/string/predicate.hpp>
17 
18 #include <sstream>
19 #include <string>
20 
21 using namespace std;
22 using namespace isc::process;
23 using namespace isc::data;
24 using namespace isc::http;
25 
26 namespace isc {
27 namespace netconf {
28 
29 // *********************** CfgControlSocket  *************************
30 
CfgControlSocket(Type type,const string & name,const Url & url)31 CfgControlSocket::CfgControlSocket(Type type, const string& name,
32                                    const Url& url)
33     : type_(type), name_(name), url_(url) {
34 }
35 
36 CfgControlSocket::Type
stringToType(const string & type)37 CfgControlSocket::stringToType(const string& type) {
38     if (type == "unix") {
39         return (CfgControlSocket::Type::UNIX);
40     } else if (type == "http") {
41         return (CfgControlSocket::Type::HTTP);
42     } else if (type == "stdout") {
43         return (CfgControlSocket::Type::STDOUT);
44     }
45 
46     isc_throw(BadValue, "Unknown control socket type: " << type);
47 }
48 
49 const string
typeToString(CfgControlSocket::Type type)50 CfgControlSocket::typeToString(CfgControlSocket::Type type) {
51     switch (type) {
52     case CfgControlSocket::Type::UNIX:
53         return ("unix");
54     case CfgControlSocket::Type::HTTP:
55         return ("http");
56     case CfgControlSocket::Type::STDOUT:
57         return ("stdout");
58     default:
59         isc_throw(BadValue, "Unknown control socket type: " << type);
60     }
61 }
62 
63 ElementPtr
toElement() const64 CfgControlSocket::toElement() const {
65     ElementPtr result = Element::createMap();
66     // Set user-context
67     contextToElement(result);
68     // Set type
69     result->set("socket-type", Element::create(typeToString(type_)));
70     // Set name
71     result->set("socket-name", Element::create(name_));
72     // Set url
73     result->set("socket-url", Element::create(url_.toText()));
74     return (result);
75 }
76 
77 // *********************** CfgServer  *************************
CfgServer(const string & model,CfgControlSocketPtr ctrl_sock)78 CfgServer::CfgServer(const string& model, CfgControlSocketPtr ctrl_sock)
79     : model_(model), boot_update_(true), subscribe_changes_(true),
80       subscribe_notifications_(true), validate_changes_(true),
81       control_socket_(ctrl_sock) {
82 }
83 
84 string
toText() const85 CfgServer::toText() const {
86     ostringstream s;
87     s << "model: " << model_ << ", control socker: ";
88     if (!control_socket_) {
89         s << "none";
90     } else {
91         switch (control_socket_->getType()) {
92         case CfgControlSocket::Type::UNIX:
93             s << "UNIX:'" << control_socket_->getName() << "'";
94             break;
95         case CfgControlSocket::Type::HTTP:
96           s << "HTTP:'" << control_socket_->getUrl().toText() << "'";
97             break;
98         case CfgControlSocket::Type::STDOUT:
99             s << "STDOUT";
100             break;
101         }
102     }
103     return (s.str());
104 }
105 
106 ElementPtr
toElement() const107 CfgServer::toElement() const {
108     ElementPtr result = Element::createMap();
109     // Set user-context
110     contextToElement(result);
111     // Set model
112     result->set("model", Element::create(model_));
113     // Set boot-update
114     result->set("boot-update", Element::create(boot_update_));
115     // Set subscribe-changes
116     result->set("subscribe-changes", Element::create(subscribe_changes_));
117     // Set validate-changes
118     result->set("validate-changes", Element::create(validate_changes_));
119     // Set control-socket
120     if (control_socket_) {
121         result->set("control-socket", control_socket_->toElement());
122     }
123     return (result);
124 }
125 
126 ostream&
operator <<(ostream & os,const CfgServer & server)127 operator<<(ostream& os, const CfgServer& server) {
128     os << server.toText();
129     return (os);
130 }
131 
132 // *************************** PARSERS ***********************************
133 
134 // *********************** ControlSocketConfigParser  *************************
135 
136 CfgControlSocketPtr
parse(ConstElementPtr ctrl_sock_config)137 ControlSocketConfigParser::parse(ConstElementPtr ctrl_sock_config) {
138     CfgControlSocketPtr result;
139     string type_str = getString(ctrl_sock_config, "socket-type");
140     string name = getString(ctrl_sock_config, "socket-name");
141     string url_str = getString(ctrl_sock_config, "socket-url");
142     ConstElementPtr user_context = ctrl_sock_config->get("user-context");
143 
144     // Type must be valid.
145     CfgControlSocket::Type type;
146     try {
147         type = CfgControlSocket::stringToType(type_str);
148     } catch (const std::exception& ex) {
149         isc_throw(ConfigError, ex.what() << " '" << type_str << "' ("
150                   << getPosition("socket-type", ctrl_sock_config)  << ")");
151     }
152 
153     // Url must be valid.
154     Url url(url_str);
155     if (!url.isValid()) {
156         isc_throw(ConfigError, "invalid control socket url: "
157                   << url.getErrorMessage() << " '" << url_str << "' ("
158                   << getPosition("socket-url", ctrl_sock_config)  << ")");
159     }
160 
161     // Create the control socket.
162     try {
163         result.reset(new CfgControlSocket(type, name, url));
164     } catch (const std::exception& ex) {
165         isc_throw(ConfigError, ex.what() << " ("
166                   << ctrl_sock_config->getPosition() << ")");
167     }
168 
169     // Add user-context.
170     if (user_context) {
171         result->setContext(user_context);
172     }
173 
174     return (result);
175 }
176 
177 // *********************** ServerConfigParser  *************************
178 
179 CfgServerPtr
parse(ConstElementPtr server_config)180 ServerConfigParser::parse(ConstElementPtr server_config) {
181     CfgServerPtr result;
182     string model = getString(server_config, "model");
183     ConstElementPtr user_context = server_config->get("user-context");
184     ConstElementPtr ctrl_sock_config = server_config->get("control-socket");
185     CfgControlSocketPtr ctrl_sock;
186     if (ctrl_sock_config) {
187         ControlSocketConfigParser parser;
188         ctrl_sock = parser.parse(ctrl_sock_config);
189     }
190     try {
191         result.reset(new CfgServer(model, ctrl_sock));
192     } catch (const std::exception& ex) {
193         isc_throw(ConfigError, ex.what() << " ("
194                   << server_config->getPosition() << ")");
195     }
196 
197     // Add flags.
198     result->setBootUpdate(getBoolean(server_config, "boot-update"));
199     result->setSubscribeChanges(getBoolean(server_config, "subscribe-changes"));
200     result->setValidateChanges(getBoolean(server_config, "validate-changes"));
201 
202     // Add user-context.
203     if (user_context) {
204         result->setContext(user_context);
205     }
206 
207     return (result);
208 }
209 
210 }  // namespace netconf
211 }  // namespace isc
212