1 /*
2  * Copyright 2017 CodiLime
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 #include "client/networkclient.h"
19 
20 #include <cstring>
21 #include <functional>
22 #include <memory>
23 #include <string>
24 
25 #include <QHostAddress>
26 #include <QSslConfiguration>
27 #include <QSslSocket>
28 
29 #include "client/node.h"
30 #include "client/nodetree.h"
31 #include "proto/exceptions.h"
32 
33 namespace veles {
34 namespace client {
35 
36 /*****************************************************************************/
37 /* NetworkClient */
38 /*****************************************************************************/
39 
connStatusStr(ConnectionStatus status)40 QString NetworkClient::connStatusStr(ConnectionStatus status) {
41   switch (status) {
42     case ConnectionStatus::Connected:
43       return QString("Connected");
44       break;
45     case ConnectionStatus::Connecting:
46       return QString("Connecting");
47       break;
48     case ConnectionStatus::NotConnected:
49       return QString("Not Connected");
50       break;
51     default:
52       return QString("Unknown status");
53       break;
54   }
55 }
56 
NetworkClient(QObject * parent)57 NetworkClient::NetworkClient(QObject* parent)
58     : QObject(parent),
59       node_tree_(nullptr),
60       status_(ConnectionStatus::NotConnected),
61       server_name_("127.0.0.1"),
62       server_port_(3135),
63       protocol_version_(1),
64       client_name_(""),
65       client_version_("[unspecified version]"),
66       client_description_(""),
67       client_type_(""),
68       authentication_key_(""),
69       fingerprint_(""),
70       quit_on_close_(false),
71       ssl_enabled_(true),
72       qid_(0) {
73   NetworkClient::NetworkClient::registerMessageHandlers();
74 }
75 
~NetworkClient()76 NetworkClient::~NetworkClient() {}
77 
connectionStatus()78 NetworkClient::ConnectionStatus NetworkClient::connectionStatus() {
79   return status_;
80 }
81 
connect(const QString & server_url,const QString & client_name,const QString & client_version,const QString & client_description,const QString & client_type,bool quit_on_close)82 void NetworkClient::connect(const QString& server_url,
83                             const QString& client_name,
84                             const QString& client_version,
85                             const QString& client_description,
86                             const QString& client_type, bool quit_on_close) {
87   QString scheme = server_url.section("://", 0, 0).toLower();
88   if (scheme == SCHEME_SSL) {
89     if (QSslSocket::supportsSsl()) {
90       ssl_enabled_ = true;
91     } else {
92       if (output() != nullptr) {
93         *output() << "NetworkClient:: SSL error - check if "
94                      "OpenSSL is available"
95                   << endl;
96       }
97       return;
98     }
99   } else if (scheme == SCHEME_TCP) {
100     ssl_enabled_ = false;
101   } else if (scheme == SCHEME_UNIX) {
102     if (output() != nullptr) {
103       *output() << "NetworkClient:: Unix sockets are "
104                    "currently unsupported"
105                 << endl;
106     }
107     return;
108   } else {
109     if (output() != nullptr) {
110       *output() << "NetworkClient:: ERROR: unknown scheme provided!" << endl;
111     }
112     return;
113   }
114   QString url = server_url.section("://", 1);
115   QString auth = url.section("@", 0, 0);
116   QString loc = url.section("@", 1);
117 
118   server_name_ = loc.section(":", 0, -2);
119   server_port_ = loc.section(":", -1, -1).toInt();
120   client_name_ = client_name;
121   client_version_ = client_version;
122   client_description_ = client_description;
123   client_type_ = client_type;
124   quit_on_close_ = quit_on_close;
125 
126   authentication_key_ = QByteArray::fromHex(auth.section(":", 0, 0).toUtf8());
127   fingerprint_ = auth.section(":", 1).replace(":", "").toLower();
128   const int target_size = 64;
129   int key_size = authentication_key_.size();
130   authentication_key_.resize(target_size);
131   for (int i = key_size; i < target_size; ++i) {
132     authentication_key_[i] = 0;
133   }
134 
135   if (status_ != ConnectionStatus::Connected &&
136       status_ != ConnectionStatus::Connecting) {
137     client_socket_ = new QSslSocket(this);
138     if (ssl_enabled_) {
139       QObject::connect(client_socket_, &QSslSocket::encrypted, this,
140                        &NetworkClient::socketConnected, Qt::QueuedConnection);
141       QObject::connect(
142           client_socket_,
143           static_cast<void (QSslSocket::*)(const QList<QSslError>&)>(
144               &QSslSocket::sslErrors),
145           this, &NetworkClient::checkFingerprint);
146     } else {
147       QObject::connect(client_socket_, &QAbstractSocket::connected, this,
148                        &NetworkClient::socketConnected, Qt::QueuedConnection);
149     }
150     QObject::connect(client_socket_, &QAbstractSocket::disconnected, this,
151                      &NetworkClient::socketDisconnected, Qt::QueuedConnection);
152     QObject::connect(client_socket_, &QIODevice::readyRead, this,
153                      &NetworkClient::newDataAvailable);
154     QObject::connect(
155         client_socket_,
156         static_cast<void (QAbstractSocket::*)(QAbstractSocket::SocketError)>(
157             &QAbstractSocket::error),
158         this, &NetworkClient::socketError);
159 
160     if (output() != nullptr) {
161       *output() << "Connecting to " << server_name_ << ":" << server_port_
162                 << "..." << endl;
163     }
164 
165     if (ssl_enabled_) {
166       client_socket_->connectToHostEncrypted(server_name_, server_port_);
167     } else {
168       client_socket_->connectToHost(server_name_, server_port_);
169     }
170     setConnectionStatus(ConnectionStatus::Connecting);
171   }
172 }
173 
disconnect()174 void NetworkClient::disconnect() {
175   if (output() != nullptr) {
176     *output() << "NetworkClient: Disconnect." << endl;
177   }
178 
179   setConnectionStatus(ConnectionStatus::NotConnected);
180 
181   if (client_socket_ != nullptr) {
182     client_socket_->disconnectFromHost();
183   }
184 }
185 
nodeTree()186 std::unique_ptr<NodeTree> const& NetworkClient::nodeTree() {
187   return node_tree_;
188 }
189 
nextQid()190 uint64_t NetworkClient::nextQid() { return ++qid_; }
191 
protocolVersion()192 unsigned int NetworkClient::protocolVersion() { return protocol_version_; }
193 
clientName()194 QString NetworkClient::clientName() { return client_name_; }
195 
clientVersion()196 QString NetworkClient::clientVersion() { return client_version_; }
197 
clientDescription()198 QString NetworkClient::clientDescription() { return client_description_; }
199 
clientType()200 QString NetworkClient::clientType() { return client_type_; }
201 
authenticationKey()202 QString NetworkClient::authenticationKey() { return authentication_key_; }
203 
output()204 QTextStream* NetworkClient::output() { return output_stream_; }
205 
setOutput(QTextStream * stream)206 void NetworkClient::setOutput(QTextStream* stream) { output_stream_ = stream; }
207 
sendMsgConnect()208 void NetworkClient::sendMsgConnect() {
209   std::shared_ptr<std::string> client_name_ptr(
210       new std::string(client_name_.toStdString()));
211   std::shared_ptr<std::string> client_version_ptr(
212       new std::string(client_version_.toStdString()));
213   std::shared_ptr<std::string> client_description_ptr(
214       new std::string(client_description_.toStdString()));
215   std::shared_ptr<std::string> client_type_ptr(
216       new std::string(client_type_.toStdString()));
217 
218   msg_ptr msg(new proto::MsgConnect(
219       1, pair_str(true, client_name_ptr), pair_str(true, client_version_ptr),
220       pair_str(true, client_description_ptr), pair_str(true, client_type_ptr),
221       quit_on_close_));
222 
223   sendMessage(msg);
224 }
225 
registerMessageHandlers()226 void NetworkClient::registerMessageHandlers() {
227   message_handlers_["subscription_cancelled"] =
228       &NetworkClient::handleNodeTreeRelatedMessage;
229   message_handlers_["get_reply"] = &NetworkClient::handleNodeTreeRelatedMessage;
230   message_handlers_["connected"] = &NetworkClient::handleConnectedMessage;
231   message_handlers_["proto_error"] = &NetworkClient::handleProtoErrorMessage;
232   message_handlers_["connections_reply"] =
233       &NetworkClient::handleConnectionsMessage;
234   message_handlers_["registry_reply"] =
235       &NetworkClient::handleRegistryReplyMessage;
236   message_handlers_["method_result"] = &NetworkClient::handleMthdResMessage;
237   message_handlers_["method_error"] = &NetworkClient::handleMthdResMessage;
238   message_handlers_["broadcast_result"] = &NetworkClient::handleMthdResMessage;
239   message_handlers_["plugin_trigger_run"] =
240       &NetworkClient::handlePluginTriggerRunMessage;
241   message_handlers_["request_error"] =
242       &NetworkClient::handleNodeTreeRelatedMessage;
243   message_handlers_["get_list_reply"] =
244       &NetworkClient::handleNodeTreeRelatedMessage;
245   message_handlers_["get_data_reply"] =
246       &NetworkClient::handleNodeTreeRelatedMessage;
247   message_handlers_["get_query_reply"] =
248       &NetworkClient::handleNodeTreeRelatedMessage;
249   message_handlers_["query_error"] =
250       &NetworkClient::handleNodeTreeRelatedMessage;
251   message_handlers_["request_ack"] =
252       &NetworkClient::handleNodeTreeRelatedMessage;
253   message_handlers_["get_bindata_reply"] =
254       &NetworkClient::handleNodeTreeRelatedMessage;
255   message_handlers_["connection_error"] =
256       &NetworkClient::handleConnErrorMessage;
257   message_handlers_["plugin_method_run"] =
258       &NetworkClient::handlePluginMethodRunMessage;
259   message_handlers_["plugin_query_get"] =
260       &NetworkClient::handlePluginQueryGetMessage;
261   message_handlers_["plugin_broadcast_run"] =
262       &NetworkClient::handleBroadcastRunMessage;
263   message_handlers_["plugin_handler_unregistered"] =
264       &NetworkClient::handlePluginHandlerUnregisteredMessage;
265 }
266 
handleNodeTreeRelatedMessage(const msg_ptr & msg)267 void NetworkClient::handleNodeTreeRelatedMessage(const msg_ptr& msg) {
268   nodeTree()->addRemoteNodeTreeRelatedMessage(msg);
269 }
270 
handleConnectedMessage(const msg_ptr &)271 void NetworkClient::handleConnectedMessage(const msg_ptr& /*msg*/) {
272   if (connectionStatus() != ConnectionStatus::Connecting) {
273     if (output() != nullptr) {
274       *output() << "NetworkClient: Very confusing... "
275                    "Received \"connected\" message while already connected."
276                 << endl;
277     }
278   } else {
279     if (output() != nullptr) {
280       *output() << "NetworkClient: Received \"connected\" message." << endl;
281     }
282 
283     setConnectionStatus(ConnectionStatus::Connected);
284   }
285 }
286 
handleProtoErrorMessage(const msg_ptr & msg)287 void NetworkClient::handleProtoErrorMessage(const msg_ptr& msg) {
288   auto* mpe = dynamic_cast<proto::MsgProtoError*>(msg.get());
289   if (mpe != nullptr) {
290     if (output() != nullptr) {
291       *output() << "Received protocol error message. Aborting connection..."
292                 << endl
293                 << "    code: " << mpe->err->code.c_str()
294                 << "  msg: " << mpe->err->msg.c_str() << endl;
295     }
296 
297     disconnect();
298   }
299 }
300 
handleConnectionsMessage(const msg_ptr & msg)301 void NetworkClient::handleConnectionsMessage(const msg_ptr& msg) {
302   if (output() != nullptr) {
303     *output() << "NetworkClient: Received \""
304               << QString::fromStdString(msg->object_type) << "\" message."
305               << endl;
306   }
307 
308   // TODO(altran01): Is this something that client should implement in a
309   // subclass?
310 }
311 
handleRegistryReplyMessage(const msg_ptr & msg)312 void NetworkClient::handleRegistryReplyMessage(const msg_ptr& msg) {
313   if (output() != nullptr) {
314     *output() << "NetworkClient: Received \""
315               << QString::fromStdString(msg->object_type) << "\" message."
316               << endl;
317   }
318 
319   // TODO(altran01): Is this something that client should implement in a
320   // subclass?
321 }
322 
handleMthdResMessage(const msg_ptr & msg)323 void NetworkClient::handleMthdResMessage(const msg_ptr& msg) {
324   if (output() != nullptr) {
325     *output() << "NetworkClient: Received \""
326               << QString::fromStdString(msg->object_type) << "\" message."
327               << endl;
328   }
329   // TODO(altran01): Is this something that client should implement in a
330   // subclass?
331 }
332 
handlePluginTriggerRunMessage(const msg_ptr & msg)333 void NetworkClient::handlePluginTriggerRunMessage(const msg_ptr& msg) {
334   if (output() != nullptr) {
335     *output() << "NetworkClient: Received \""
336               << QString::fromStdString(msg->object_type) << "\" message."
337               << endl;
338   }
339 
340   // TODO(altran01): Is this something that client should implement in a
341   // subclass?
342 }
343 
handleConnErrorMessage(const msg_ptr & msg)344 void NetworkClient::handleConnErrorMessage(const msg_ptr& msg) {
345   auto* cem = dynamic_cast<proto::MsgConnectionError*>(msg.get());
346   if (cem != nullptr) {
347     if (output() != nullptr) {
348       *output() << "Received connection error message. Aborting connection..."
349                 << endl
350                 << "    code: " << cem->err->code.c_str()
351                 << "  msg: " << cem->err->msg.c_str() << endl;
352     }
353 
354     disconnect();
355   }
356 }
357 
handlePluginMethodRunMessage(const msg_ptr & msg)358 void NetworkClient::handlePluginMethodRunMessage(const msg_ptr& msg) {
359   if (output() != nullptr) {
360     *output() << "NetworkClient: Received \""
361               << QString::fromStdString(msg->object_type) << "\" message."
362               << endl;
363   }
364 
365   // TODO(altran01): Is this something that client should implement in a
366   // subclass?
367 }
368 
handlePluginQueryGetMessage(const msg_ptr & msg)369 void NetworkClient::handlePluginQueryGetMessage(const msg_ptr& msg) {
370   if (output() != nullptr) {
371     *output() << "NetworkClient: Received \""
372               << QString::fromStdString(msg->object_type) << "\" message."
373               << endl;
374   }
375 
376   // TODO(altran01): Is this something that client should implement in a
377   // subclass?
378 }
379 
handleBroadcastRunMessage(const msg_ptr & msg)380 void NetworkClient::handleBroadcastRunMessage(const msg_ptr& msg) {
381   if (output() != nullptr) {
382     *output() << "NetworkClient: Received \""
383               << QString::fromStdString(msg->object_type) << "\" message."
384               << endl;
385   }
386 
387   // TODO(altran01): Is this something that client should implement in a
388   // subclass?
389 }
390 
handlePluginHandlerUnregisteredMessage(const msg_ptr & msg)391 void NetworkClient::handlePluginHandlerUnregisteredMessage(const msg_ptr& msg) {
392   if (output() != nullptr) {
393     *output() << "NetworkClient: Received \""
394               << QString::fromStdString(msg->object_type) << "\" message."
395               << endl;
396   }
397 
398   // TODO(altran01): Is this something that client should implement in a
399   // subclass?
400 }
401 
sendMessage(const msg_ptr & msg)402 void NetworkClient::sendMessage(const msg_ptr& msg) {
403   if (client_socket_ != nullptr && client_socket_->isValid()) {
404     msgpack::sbuffer buf;
405     msgpack::packer<msgpack::sbuffer> packer(buf);
406     messages::MsgpackWrapper::dumpObject(packer, msg);
407     client_socket_->write(buf.data(), buf.size());
408   }
409 }
410 
setConnectionStatus(ConnectionStatus connection_status)411 void NetworkClient::setConnectionStatus(ConnectionStatus connection_status) {
412   if (status_ != connection_status) {
413     status_ = connection_status;
414     if (output() != nullptr) {
415       *output() << "NetworkClient: New connection status: "
416                 << connStatusStr(connection_status) << "." << endl;
417     }
418     emit connectionStatusChanged(status_);
419   }
420 }
421 
socketConnected()422 void NetworkClient::socketConnected() {
423   if (ssl_enabled_) {
424     QSslCertificate cert = client_socket_->peerCertificate();
425     if (cert.isNull()) {
426       if (output() != nullptr) {
427         *output() << "NetworkClient: received null certificate!" << endl;
428       }
429       disconnect();
430       return;
431     }
432     QByteArray remote_fingerprint =
433         cert.digest(QCryptographicHash::Algorithm::Sha256).toHex();
434     if (fingerprint_ != remote_fingerprint) {
435       if (output() != nullptr) {
436         *output()
437             << "NetworkClient: Certificate fingerprint mismatch! Expected: "
438             << fingerprint_ << ", got: " << remote_fingerprint << endl;
439       }
440       disconnect();
441       return;
442     }
443   }
444 
445   if (output() != nullptr) {
446     *output() << "NetworkClient: TCP socket connected - sending an "
447                  "authentication key and \"connect\" message."
448               << endl;
449   }
450 
451   node_tree_ = std::make_unique<NodeTree>(this);
452   client_socket_->write(authentication_key_);
453   sendMsgConnect();
454 }
455 
socketDisconnected()456 void NetworkClient::socketDisconnected() {
457   setConnectionStatus(ConnectionStatus::NotConnected);
458   if (output() != nullptr) {
459     *output() << "NetworkClient: TCP socket disconnected." << endl;
460   }
461 
462   if (node_tree_) {
463     node_tree_.reset();
464   }
465 
466   if (client_socket_ != nullptr) {
467     client_socket_->deleteLater();
468     client_socket_ = nullptr;
469   }
470 }
471 
newDataAvailable()472 void NetworkClient::newDataAvailable() {
473   while (client_socket_ != nullptr) {
474     msg_ptr msg = nullptr;
475     try {
476       msg = msgpack_wrapper_.loadMessage(client_socket_);
477     } catch (proto::SchemaError& schema_error) {
478       if (output() != nullptr) {
479         *output() << "NetworkClient: SchemaError - "
480                   << QString::fromStdString(schema_error.msg) << endl;
481       }
482     }
483 
484     if (msg) {
485       auto handler_iter = message_handlers_.find(msg->object_type);
486       if (handler_iter != message_handlers_.end()) {
487         MessageHandler handler = handler_iter->second;
488         (this->*handler)(msg);
489       } else {
490         if (output() != nullptr) {
491           *output() << "NetworkClient: Received message of not handled "
492                        "type: \""
493                     << msg->object_type.c_str() << "\"." << endl;
494         }
495       }
496       emit messageReceived(msg);
497     } else {
498       break;
499     }
500   }
501 }
502 
socketError(QAbstractSocket::SocketError)503 void NetworkClient::socketError(QAbstractSocket::SocketError /*socketError*/) {
504   setConnectionStatus(ConnectionStatus::NotConnected);
505   if (output() != nullptr && client_socket_ != nullptr) {
506     *output() << "NetworkClient: Socket error - "
507               << client_socket_->errorString() << endl;
508   }
509 }
510 
checkFingerprint(const QList<QSslError> & errors)511 void NetworkClient::checkFingerprint(const QList<QSslError>& errors) {
512   for (const auto& err : errors) {
513     if (err.error() != QSslError::SelfSignedCertificate &&
514         err.error() != QSslError::HostNameMismatch &&
515         err.error() != QSslError::CertificateUntrusted) {
516       if (output() != nullptr) {
517         *output() << "NetworkClient: unexpected error: " << err.errorString()
518                   << endl;
519       }
520       return;
521     }
522   }
523   client_socket_->ignoreSslErrors(errors);
524 }
525 
526 }  // namespace client
527 }  // namespace veles
528