1
2
3
4
5
6 #include <cmajor/rt/Debug.hpp>
7 #include <sngxml/dom/Element.hpp>
8 #include <sngxml/dom/Parser.hpp>
9 #include <sngxml/xpath/XPathEvaluate.hpp>
10 #include <soulng/util/Path.hpp>
11 #include <soulng/util/TextUtils.hpp>
12 #include <soulng/util/Sha1.hpp>
13 #include <soulng/util/Socket.hpp>
14 #include <soulng/util/System.hpp>
15 #include <soulng/util/Unicode.hpp>
16 #include <boost/lexical_cast.hpp>
17 #include <iostream>
18 #include <thread>
19 #include <time.h>
20
21 namespace cmajor { namespace rt {
22
23 using namespace soulng::util;
24 using namespace soulng::unicode;
25
26 const std::int64_t sessionTimeoutSecs = 5 * 60;
27
28 class CmdbSessionServer
29 {
30 public:
31 static void Init();
32 static void Done();
33 static CmdbSessionServer& Instance() { return *instance; }
34 void Start(const std::string& skey_, const std::string& rkey_, int port_);
35 void OpenSession(TcpSocket& socket);
36 void CloseSession(TcpSocket& socket);
37 bool IsSessionOpen() const { return sessionOpen; }
38 void SendOutput(int fileHandle, const std::string& bytes);
39 int64_t ReceiveInput(uint8_t* buffer, int64_t bufferSize);
40 private:
41 static std::unique_ptr<CmdbSessionServer> instance;
42 CmdbSessionServer();
43 std::string skey;
44 std::string rkey;
45 int port;
46 bool sessionOpen;
47 std::string inputHexByteBuffer;
48 };
49
50 std::unique_ptr<CmdbSessionServer> CmdbSessionServer::instance;
51
52 CmdbSessionServer::CmdbSessionServer() : skey(), rkey(), port(), sessionOpen(false)
53 {
54 }
55
56 void CmdbSessionServer::Init()
57 {
58 instance.reset(new CmdbSessionServer());
59 }
60
61 void CmdbSessionServer::Done()
62 {
63 instance.reset();
64 }
65
66 void CmdbSessionServer::Start(const std::string& skey_, const std::string& rkey_, int port_)
67 {
68 skey = skey_;
69 rkey = rkey_;
70 port = port_;
71 sessionOpen = true;
72 }
73
74 void CmdbSessionServer::OpenSession(TcpSocket& socket)
75 {
76 sngxml::dom::Document openSessionRequest;
77 sngxml::dom::Element* cmdbOpenMessage = new sngxml::dom::Element(U"cmdbMessage");
78 openSessionRequest.AppendChild(std::unique_ptr<sngxml::dom::Node>(cmdbOpenMessage));
79 cmdbOpenMessage->SetAttribute(U"kind", U"openSessionRequest");
80 cmdbOpenMessage->SetAttribute(U"key", ToUtf32(GetSha1MessageDigest(this->skey)));
81 sngxml::dom::SendDocument(socket, openSessionRequest);
82 bool validOpenResponseReceived = false;
83 std::unique_ptr<sngxml::dom::Document> openSessionResponse = sngxml::dom::ReceiveDocument(socket);
84 if (openSessionResponse)
85 {
86 std::unique_ptr<sngxml::xpath::XPathObject> responseObject(sngxml::xpath::Evaluate(U"/cmdbMessage", openSessionResponse.get()));
87 if (responseObject)
88 {
89 if (responseObject->Type() == sngxml::xpath::XPathObjectType::nodeSet)
90 {
91 sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(responseObject.get());
92 if (nodeSet->Length() == 1)
93 {
94 sngxml::dom::Node* node = (*nodeSet)[0];
95 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
96 {
97 sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
98 std::u32string kind = element->GetAttribute(U"kind");
99 if (kind == U"openSessionResponse")
100 {
101 std::string key = ToUtf8(element->GetAttribute(U"key"));
102 if (key == GetSha1MessageDigest(this->rkey))
103 {
104 validOpenResponseReceived = true;
105 }
106 else
107 {
108 throw std::runtime_error("invalid CMDB session key");
109 }
110 }
111 }
112 }
113 }
114 }
115 }
116 if (!validOpenResponseReceived)
117 {
118 throw std::runtime_error("no valid open session response received");
119 }
120 }
121
122 void CmdbSessionServer::CloseSession(TcpSocket& socket)
123 {
124 sngxml::dom::Document closeSessionRequest;
125 sngxml::dom::Element* cmdbCloseMessage = new sngxml::dom::Element(U"cmdbMessage");
126 closeSessionRequest.AppendChild(std::unique_ptr<sngxml::dom::Node>(cmdbCloseMessage));
127 cmdbCloseMessage->SetAttribute(U"kind", U"closeSessionRequest");
128 sngxml::dom::SendDocument(socket, closeSessionRequest);
129 }
130
131 void CmdbSessionServer::SendOutput(int fileHandle, const std::string& bytes)
132 {
133 try
134 {
135 if (!sessionOpen)
136 {
137 throw std::runtime_error("no CMDB session open");
138 }
139 TcpSocket socket("localhost", std::to_string(port));
140 OpenSession(socket);
141 sngxml::dom::Document outputRequest;
142 sngxml::dom::Element* cmdbOutputMessage = new sngxml::dom::Element(U"cmdbMessage");
143 outputRequest.AppendChild(std::unique_ptr<sngxml::dom::Node>(cmdbOutputMessage));
144 cmdbOutputMessage->SetAttribute(U"kind", U"outputRequest");
145 cmdbOutputMessage->SetAttribute(U"handle", ToUtf32(std::to_string(fileHandle)));
146 cmdbOutputMessage->SetAttribute(U"bytes", ToUtf32(bytes));
147 sngxml::dom::SendDocument(socket, outputRequest);
148 bool outputResponseReceived = false;
149 std::unique_ptr<sngxml::dom::Document> outputResponse = sngxml::dom::ReceiveDocument(socket);
150 if (outputResponse)
151 {
152 std::unique_ptr<sngxml::xpath::XPathObject> responseObject(sngxml::xpath::Evaluate(U"/cmdbMessage", outputResponse.get()));
153 if (responseObject)
154 {
155 if (responseObject->Type() == sngxml::xpath::XPathObjectType::nodeSet)
156 {
157 sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(responseObject.get());
158 if (nodeSet->Length() == 1)
159 {
160 sngxml::dom::Node* node = (*nodeSet)[0];
161 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
162 {
163 sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
164 std::u32string kind = element->GetAttribute(U"kind");
165 if (kind == U"outputResponse")
166 {
167 outputResponseReceived = true;
168 }
169 }
170 }
171 }
172 }
173 CloseSession(socket);
174 socket.Shutdown(ShutdownMode::both);
175 socket.Close();
176 }
177 if (!outputResponseReceived)
178 {
179 throw std::runtime_error("no valid output response received");
180 }
181 }
182 catch (const std::exception& ex;)
183 {
184 std::cerr << "CMDB session output request failed: " << ex.what() << std::endl;
185 }
186 }
187
188 int64_t CmdbSessionServer::ReceiveInput(uint8_t* buffer, int64_t bufferSize)
189 {
190 try
191 {
192 if (inputHexByteBuffer.empty())
193 {
194 if (!sessionOpen)
195 {
196 throw std::runtime_error("no CMDB session open");
197 }
198 TcpSocket socket("localhost", std::to_string(port));
199 OpenSession(socket);
200 sngxml::dom::Document inputRequest;
201 sngxml::dom::Element* cmdbInputMessage = new sngxml::dom::Element(U"cmdbMessage");
202 inputRequest.AppendChild(std::unique_ptr<sngxml::dom::Node>(cmdbInputMessage));
203 cmdbInputMessage->SetAttribute(U"kind", U"inputRequest");
204 sngxml::dom::SendDocument(socket, inputRequest);
205 bool inputResponseReceived = false;
206 std::unique_ptr<sngxml::dom::Document> inputResponse = sngxml::dom::ReceiveDocument(socket);
207 if (inputResponse)
208 {
209 std::unique_ptr<sngxml::xpath::XPathObject> responseObject(sngxml::xpath::Evaluate(U"/cmdbMessage", inputResponse.get()));
210 if (responseObject)
211 {
212 if (responseObject->Type() == sngxml::xpath::XPathObjectType::nodeSet)
213 {
214 sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(responseObject.get());
215 if (nodeSet->Length() == 1)
216 {
217 sngxml::dom::Node* node = (*nodeSet)[0];
218 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
219 {
220 sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
221 std::u32string kind = element->GetAttribute(U"kind");
222 if (kind == U"inputResponse")
223 {
224 std::u32string value = element->GetAttribute(U"bytes");
225 inputHexByteBuffer = ToUtf8(value);
226 inputResponseReceived = true;
227 }
228 }
229 }
230 }
231 }
232 }
233 CloseSession(socket);
234 socket.Shutdown(ShutdownMode::both);
235 socket.Close();
236 if (!inputResponseReceived)
237 {
238 throw std::runtime_error("no valid input response received");
239 }
240 }
241 int64_t bytesReceived = 0;
242 uint8_t* p = buffer;
243 int64_t bytesToReceive = bufferSize;
244 while (inputHexByteBuffer.size() >= 2 && bytesToReceive > 0)
245 {
246 std::string hex;
247 hex.append(1, inputHexByteBuffer[0]);
248 hex.append(1, inputHexByteBuffer[1]);
249 inputHexByteBuffer.erase(inputHexByteBuffer.begin());
250 inputHexByteBuffer.erase(inputHexByteBuffer.begin());
251 uint8_t byte = ParseHexByte(hex);
252 *p++ = byte;
253 --bytesToReceive;
254 ++bytesReceived;
255 }
256 return bytesReceived;
257 }
258 catch (const std::exception& ex;)
259 {
260 std::cerr << "CMDB session input request failed: " << ex.what() << std::endl;
261 }
262 return 0;
263 }
264
265 void StartCmdbSession()
266 {
267 try
268 {
269 std::string cmdbSessionFilePath;
270 std::string exePath = GetFullPath(GetPathToExecutable());
271 if (EndsWith(exePath, ".exe"))
272 {
273 cmdbSessionFilePath = Path::ChangeExtension(exePath, ".cmdbs");
274 }
275 else
276 {
277 cmdbSessionFilePath = exePath + ".cmdbs";
278 }
279 if (FileExists(cmdbSessionFilePath))
280 {
281 std::unique_ptr<sngxml::dom::Document> sessionDoc = sngxml::dom::ReadDocument(cmdbSessionFilePath);
282 std::unique_ptr<sngxml::xpath::XPathObject> timestampObject = sngxml::xpath::Evaluate(U"/cmdbSession/timestamp", sessionDoc.get());
283 if (timestampObject)
284 {
285 if (timestampObject->Type() == sngxml::xpath::XPathObjectType::nodeSet)
286 {
287 sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(timestampObject.get());
288 if (nodeSet->Length() == 1)
289 {
290 sngxml::dom::Node* timestampNode = (*nodeSet)[0];
291 if (timestampNode->GetNodeType() == sngxml::dom::NodeType::elementNode)
292 {
293 sngxml::dom::Element* timestampElement = static_cast<sngxml::dom::Element*>(timestampNode);
294 std::string timestampStr = ToUtf8(timestampElement->GetAttribute(U"value"));
295 if (!timestampStr.empty())
296 {
297 time_t timestamp = boost::lexical_cast<time_t>(timestampStr);
298 time_t now;
299 time(&now);
300 if (now - timestamp >= 0 && now - timestamp < sessionTimeoutSecs)
301 {
302 std::unique_ptr<sngxml::xpath::XPathObject> skeyObject = sngxml::xpath::Evaluate(U"/cmdbSession/skey", sessionDoc.get());
303 if (skeyObject)
304 {
305 if (skeyObject->Type() == sngxml::xpath::XPathObjectType::nodeSet)
306 {
307 sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(skeyObject.get());
308 if (nodeSet->Length() == 1)
309 {
310 sngxml::dom::Node* keyNode = (*nodeSet)[0];
311 if (keyNode->GetNodeType() == sngxml::dom::NodeType::elementNode)
312 {
313 sngxml::dom::Element* keyElement = static_cast<sngxml::dom::Element*>(keyNode);
314 std::string skeyStr = ToUtf8(keyElement->GetAttribute(U"value"));
315 if (!skeyStr.empty())
316 {
317 std::unique_ptr<sngxml::xpath::XPathObject> rkeyObject = sngxml::xpath::Evaluate(U"/cmdbSession/rkey", sessionDoc.get());
318 if (rkeyObject)
319 {
320 sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(rkeyObject.get());
321 if (nodeSet->Length() == 1)
322 {
323 sngxml::dom::Node* keyNode = (*nodeSet)[0];
324 if (keyNode->GetNodeType() == sngxml::dom::NodeType::elementNode)
325 {
326 sngxml::dom::Element* keyElement = static_cast<sngxml::dom::Element*>(keyNode);
327 std::string rkeyStr = ToUtf8(keyElement->GetAttribute(U"value"));
328 if (!rkeyStr.empty())
329 {
330 std::unique_ptr<sngxml::xpath::XPathObject> portObject = sngxml::xpath::Evaluate(U"/cmdbSession/port", sessionDoc.get());
331 if (portObject)
332 {
333 if (portObject->Type() == sngxml::xpath::XPathObjectType::nodeSet)
334 {
335 sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(portObject.get());
336 if (nodeSet->Length() == 1)
337 {
338 sngxml::dom::Node* portNode = (*nodeSet)[0];
339 if (portNode->GetNodeType() == sngxml::dom::NodeType::elementNode)
340 {
341 sngxml::dom::Element* portElement = static_cast<sngxml::dom::Element*>(portNode);
342 std::string portStr = ToUtf8(portElement->GetAttribute(U"value"));
343 if (!portStr.empty())
344 {
345 int port = boost::lexical_cast<int>(portStr);
346 CmdbSessionServer::Instance().Start(skeyStr, rkeyStr, port);
347 }
348 else
349 {
350 throw std::runtime_error("port is empty");
351 }
352 }
353 }
354 }
355 }
356 }
357 else
358 {
359 throw std::runtime_error("key is empty");
360 }
361 }
362 }
363 }
364 }
365 else
366 {
367 throw std::runtime_error("key is empty");
368 }
369 }
370 }
371 }
372 }
373 }
374 }
375 else
376 {
377 throw std::runtime_error("timestamp is empty");
378 }
379 }
380 }
381 }
382 }
383 }
384 }
385 catch (const std::exception& ex;)
386 {
387 std::cerr << "unable to start CMDB session: " << ex.what() << std::endl;
388 }
389 }
390
391 bool IsCmdbSessionOpen()
392 {
393 return CmdbSessionServer::Instance().IsSessionOpen();
394 }
395
396 void WriteBytesToCmdbSession(int fileHandle, const uint8_t* buffer, int64_t count)
397 {
398 std::string output;
399 for (int64_t i = 0; i < count; ++i)
400 {
401 output.append(ToHexString(buffer[i]));
402 }
403 CmdbSessionServer::Instance().SendOutput(fileHandle, output);
404 }
405
406 int64_t ReadBytesFromCmdbSession(uint8_t* buffer, int64_t bufferSize)
407 {
408 return CmdbSessionServer::Instance().ReceiveInput(buffer, bufferSize);
409 }
410
411 void InitCmdbSession()
412 {
413 CmdbSessionServer::Init();
414 }
415
416 void DoneCmdbSession()
417 {
418 CmdbSessionServer::Done();
419 }
420
421 } }