1 #include "MessageServer.hpp"
2 
3 #include <stdexcept>
4 #include <limits>
5 
6 #include <QNetworkInterface>
7 #include <QUdpSocket>
8 #include <QTimer>
9 #include <QHash>
10 
11 #include "Radio.hpp"
12 #include "Network/NetworkMessage.hpp"
13 #include "qt_helpers.hpp"
14 
15 #include "pimpl_impl.hpp"
16 
17 #include "moc_MessageServer.cpp"
18 
19 namespace
20 {
21   auto quint32_max = std::numeric_limits<quint32>::max ();
22 }
23 
24 class MessageServer::impl
25   : public QUdpSocket
26 {
27   Q_OBJECT;
28 
29 public:
impl(MessageServer * self,QString const & version,QString const & revision)30   impl (MessageServer * self, QString const& version, QString const& revision)
31     : self_ {self}
32     , version_ {version}
33     , revision_ {revision}
34     , clock_ {new QTimer {this}}
35   {
36     // register the required types with Qt
37     Radio::register_types ();
38 
39     connect (this, &QIODevice::readyRead, this, &MessageServer::impl::pending_datagrams);
40 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
41     connect (this, static_cast<void (impl::*) (SocketError)> (&impl::error)
42              , [this] (SocketError /* e */)
__anonc82af2fc0202(SocketError ) 43              {
44                Q_EMIT self_->error (errorString ());
45              });
46 #else
47     connect (this, &impl::errorOccurred, [this] (SocketError /* e */)
__anonc82af2fc0302(SocketError ) 48                                  {
49                                    Q_EMIT self_->error (errorString ());
50                                  });
51 #endif
52     connect (clock_, &QTimer::timeout, this, &impl::tick);
53     clock_->start (NetworkMessage::pulse * 1000);
54   }
55 
56   enum StreamStatus {Fail, Short, OK};
57 
58   void leave_multicast_group ();
59   void join_multicast_group ();
60   void parse_message (QHostAddress const& sender, port_type sender_port, QByteArray const& msg);
61   void tick ();
62   void pending_datagrams ();
63   StreamStatus check_status (QDataStream const&) const;
send_message(QDataStream const & out,QByteArray const & message,QHostAddress const & address,port_type port)64   void send_message (QDataStream const& out, QByteArray const& message, QHostAddress const& address, port_type port)
65   {
66       if (OK == check_status (out))
67         {
68           writeDatagram (message, address, port);
69         }
70       else
71         {
72           Q_EMIT self_->error ("Error creating UDP message");
73         }
74   }
75 
76   MessageServer * self_;
77   QString version_;
78   QString revision_;
79   QHostAddress multicast_group_address_;
80   QSet<QString> network_interfaces_;
81   static BindMode constexpr bind_mode_ = ShareAddress | ReuseAddressHint;
82   struct Client
83   {
84     Client () = default;
ClientMessageServer::impl::Client85     Client (port_type const& sender_port)
86       : sender_port_ {sender_port}
87       , negotiated_schema_number_ {2} // not 1 because it's broken
88       , last_activity_ {QDateTime::currentDateTime ()}
89     {
90     }
91     Client (Client const&) = default;
92     Client& operator= (Client const&) = default;
93 
94     port_type sender_port_;
95     quint32 negotiated_schema_number_;
96     QDateTime last_activity_;
97   };
98   QHash<ClientKey, Client> clients_; // maps id to Client
99   QTimer * clock_;
100 };
101 
102 MessageServer::impl::BindMode constexpr MessageServer::impl::bind_mode_;
103 
104 #include "MessageServer.moc"
105 
leave_multicast_group()106 void MessageServer::impl::leave_multicast_group ()
107 {
108   if (BoundState == state () && is_multicast_address (multicast_group_address_))
109     {
110       for (auto const& if_name : network_interfaces_)
111         {
112           leaveMulticastGroup (multicast_group_address_, QNetworkInterface::interfaceFromName (if_name));
113         }
114     }
115 }
116 
join_multicast_group()117 void MessageServer::impl::join_multicast_group ()
118 {
119   if (BoundState == state () && is_multicast_address (multicast_group_address_))
120     {
121       if (network_interfaces_.size ())
122         {
123           for (auto const& if_name : network_interfaces_)
124             {
125               joinMulticastGroup (multicast_group_address_, QNetworkInterface::interfaceFromName (if_name));
126             }
127         }
128       else
129         {
130           // find the loop-back interface and join on that
131           for (auto const& net_if : QNetworkInterface::allInterfaces ())
132             {
133               auto flags = QNetworkInterface::IsUp | QNetworkInterface::IsLoopBack | QNetworkInterface::CanMulticast;
134               if ((net_if.flags () & flags) == flags)
135                 {
136                   joinMulticastGroup (multicast_group_address_, net_if);
137                   break;
138                 }
139             }
140         }
141     }
142 }
143 
pending_datagrams()144 void MessageServer::impl::pending_datagrams ()
145 {
146   while (hasPendingDatagrams ())
147     {
148       QByteArray datagram;
149       datagram.resize (pendingDatagramSize ());
150       QHostAddress sender_address;
151       port_type sender_port;
152       if (0 <= readDatagram (datagram.data (), datagram.size (), &sender_address, &sender_port))
153         {
154           parse_message (sender_address, sender_port, datagram);
155         }
156     }
157 }
158 
parse_message(QHostAddress const & sender,port_type sender_port,QByteArray const & msg)159 void MessageServer::impl::parse_message (QHostAddress const& sender, port_type sender_port, QByteArray const& msg)
160 {
161   try
162     {
163       //
164       // message format is described in NetworkMessage.hpp
165       //
166       NetworkMessage::Reader in {msg};
167 
168       auto id = in.id ();
169       if (OK == check_status (in))
170         {
171           auto client_key = ClientKey {sender, id};
172           if (!clients_.contains (client_key))
173             {
174               auto& client = (clients_[client_key] = {sender_port});
175               QByteArray client_version;
176               QByteArray client_revision;
177 
178               if (NetworkMessage::Heartbeat == in.type ())
179                 {
180                   // negotiate a working schema number
181                   in >> client.negotiated_schema_number_;
182                   if (OK == check_status (in))
183                     {
184                       auto sn = NetworkMessage::Builder::schema_number;
185                       client.negotiated_schema_number_ = std::min (sn, client.negotiated_schema_number_);
186 
187                       // reply to the new client informing it of the
188                       // negotiated schema number
189                       QByteArray message;
190                       NetworkMessage::Builder hb {&message, NetworkMessage::Heartbeat, id, client.negotiated_schema_number_};
191                       hb << NetworkMessage::Builder::schema_number // maximum schema number accepted
192                          << version_.toUtf8 () << revision_.toUtf8 ();
193                       if (impl::OK == check_status (hb))
194                         {
195                           writeDatagram (message, client_key.first, sender_port);
196                         }
197                       else
198                         {
199                           Q_EMIT self_->error ("Error creating UDP message");
200                         }
201                     }
202                   // we don't care if this fails to read
203                   in >> client_version >> client_revision;
204                 }
205               Q_EMIT self_->client_opened (client_key, QString::fromUtf8 (client_version),
206                                            QString::fromUtf8 (client_revision));
207             }
208           clients_[client_key].last_activity_ = QDateTime::currentDateTime ();
209 
210           //
211           // message format is described in NetworkMessage.hpp
212           //
213           switch (in.type ())
214             {
215             case NetworkMessage::Heartbeat:
216               //nothing to do here as time out handling deals with lifetime
217               break;
218 
219             case NetworkMessage::Clear:
220               Q_EMIT self_->decodes_cleared (client_key);
221               break;
222 
223             case NetworkMessage::Status:
224               {
225                 // unpack message
226                 Frequency f;
227                 QByteArray mode;
228                 QByteArray dx_call;
229                 QByteArray report;
230                 QByteArray tx_mode;
231                 bool tx_enabled {false};
232                 bool transmitting {false};
233                 bool decoding {false};
234                 quint32 rx_df {quint32_max};
235                 quint32 tx_df {quint32_max};
236                 QByteArray de_call;
237                 QByteArray de_grid;
238                 QByteArray dx_grid;
239                 bool watchdog_timeout {false};
240                 QByteArray sub_mode;
241                 bool fast_mode {false};
242                 quint8 special_op_mode {0};
243                 quint32 frequency_tolerance {quint32_max};
244                 quint32 tr_period {quint32_max};
245                 QByteArray configuration_name;
246                 QByteArray tx_message;
247                 in >> f >> mode >> dx_call >> report >> tx_mode >> tx_enabled >> transmitting >> decoding
248                    >> rx_df >> tx_df >> de_call >> de_grid >> dx_grid >> watchdog_timeout >> sub_mode
249                    >> fast_mode >> special_op_mode >> frequency_tolerance >> tr_period >> configuration_name
250                    >> tx_message;
251                 if (check_status (in) != Fail)
252                   {
253                     Q_EMIT self_->status_update (client_key, f, QString::fromUtf8 (mode)
254                                                  , QString::fromUtf8 (dx_call)
255                                                  , QString::fromUtf8 (report), QString::fromUtf8 (tx_mode)
256                                                  , tx_enabled, transmitting, decoding, rx_df, tx_df
257                                                  , QString::fromUtf8 (de_call), QString::fromUtf8 (de_grid)
258                                                  , QString::fromUtf8 (dx_grid), watchdog_timeout
259                                                  , QString::fromUtf8 (sub_mode), fast_mode
260                                                  , special_op_mode, frequency_tolerance, tr_period
261                                                  , QString::fromUtf8 (configuration_name)
262                                                  , QString::fromUtf8 (tx_message));
263                   }
264               }
265               break;
266 
267             case NetworkMessage::Decode:
268               {
269                 // unpack message
270                 bool is_new {true};
271                 QTime time;
272                 qint32 snr;
273                 float delta_time;
274                 quint32 delta_frequency;
275                 QByteArray mode;
276                 QByteArray message;
277                 bool low_confidence {false};
278                 bool off_air {false};
279                 in >> is_new >> time >> snr >> delta_time >> delta_frequency >> mode
280                    >> message >> low_confidence >> off_air;
281                 if (check_status (in) != Fail)
282                   {
283                     Q_EMIT self_->decode (is_new, client_key, time, snr, delta_time, delta_frequency
284                                           , QString::fromUtf8 (mode), QString::fromUtf8 (message)
285                                           , low_confidence, off_air);
286                   }
287               }
288               break;
289 
290             case NetworkMessage::WSPRDecode:
291               {
292                 // unpack message
293                 bool is_new {true};
294                 QTime time;
295                 qint32 snr;
296                 float delta_time;
297                 Frequency frequency;
298                 qint32 drift;
299                 QByteArray callsign;
300                 QByteArray grid;
301                 qint32 power;
302                 bool off_air {false};
303                 in >> is_new >> time >> snr >> delta_time >> frequency >> drift >> callsign >> grid >> power
304                    >> off_air;
305                 if (check_status (in) != Fail)
306                   {
307                     Q_EMIT self_->WSPR_decode (is_new, client_key, time, snr, delta_time, frequency, drift
308                                                , QString::fromUtf8 (callsign), QString::fromUtf8 (grid)
309                                                , power, off_air);
310                   }
311               }
312               break;
313 
314             case NetworkMessage::QSOLogged:
315               {
316                 QDateTime time_off;
317                 QByteArray dx_call;
318                 QByteArray dx_grid;
319                 Frequency dial_frequency;
320                 QByteArray mode;
321                 QByteArray report_sent;
322                 QByteArray report_received;
323                 QByteArray tx_power;
324                 QByteArray comments;
325                 QByteArray name;
326                 QDateTime time_on; // Note: LOTW uses TIME_ON for their +/- 30-minute time window
327                 QByteArray operator_call;
328                 QByteArray my_call;
329                 QByteArray my_grid;
330                 QByteArray exchange_sent;
331                 QByteArray exchange_rcvd;
332                 QByteArray prop_mode;
333                 in >> time_off >> dx_call >> dx_grid >> dial_frequency >> mode >> report_sent >> report_received
334                    >> tx_power >> comments >> name >> time_on >> operator_call >> my_call >> my_grid
335                    >> exchange_sent >> exchange_rcvd >> prop_mode;
336                 if (check_status (in) != Fail)
337                   {
338                     Q_EMIT self_->qso_logged (client_key, time_off, QString::fromUtf8 (dx_call)
339                                               , QString::fromUtf8 (dx_grid)
340                                               , dial_frequency, QString::fromUtf8 (mode)
341                                               , QString::fromUtf8 (report_sent)
342                                               , QString::fromUtf8 (report_received), QString::fromUtf8 (tx_power)
343                                               , QString::fromUtf8 (comments), QString::fromUtf8 (name), time_on
344                                               , QString::fromUtf8 (operator_call), QString::fromUtf8 (my_call)
345                                               , QString::fromUtf8 (my_grid), QString::fromUtf8 (exchange_sent)
346                                               , QString::fromUtf8 (exchange_rcvd), QString::fromUtf8 (prop_mode));
347                   }
348               }
349               break;
350 
351             case NetworkMessage::Close:
352               Q_EMIT self_->client_closed (client_key);
353               clients_.remove (client_key);
354               break;
355 
356             case NetworkMessage::LoggedADIF:
357               {
358                 QByteArray ADIF;
359                 in >> ADIF;
360                 if (check_status (in) != Fail)
361                   {
362                     Q_EMIT self_->logged_ADIF (client_key, ADIF);
363                   }
364               }
365               break;
366 
367             default:
368               // Ignore
369               break;
370             }
371         }
372       else
373         {
374           Q_EMIT self_->error ("MessageServer warning: invalid UDP message received");
375         }
376     }
377   catch (std::exception const& e)
378     {
379       Q_EMIT self_->error (QString {"MessageServer exception: %1"}.arg (e.what ()));
380     }
381   catch (...)
382     {
383       Q_EMIT self_->error ("Unexpected exception in MessageServer");
384     }
385 }
386 
tick()387 void MessageServer::impl::tick ()
388 {
389   auto now = QDateTime::currentDateTime ();
390   auto iter = std::begin (clients_);
391   while (iter != std::end (clients_))
392     {
393       if (now > (*iter).last_activity_.addSecs (NetworkMessage::pulse))
394         {
395           Q_EMIT self_->decodes_cleared (iter.key ());
396           Q_EMIT self_->client_closed (iter.key ());
397           iter = clients_.erase (iter); // safe while iterating as doesn't rehash
398         }
399       else
400         {
401           ++iter;
402         }
403     }
404 }
405 
check_status(QDataStream const & stream) const406 auto MessageServer::impl::check_status (QDataStream const& stream) const -> StreamStatus
407 {
408   auto stat = stream.status ();
409   StreamStatus result {Fail};
410   switch (stat)
411     {
412     case QDataStream::ReadPastEnd:
413       result = Short;
414       break;
415 
416     case QDataStream::ReadCorruptData:
417       Q_EMIT self_->error ("Message serialization error: read corrupt data");
418       break;
419 
420     case QDataStream::WriteFailed:
421       Q_EMIT self_->error ("Message serialization error: write error");
422       break;
423 
424     default:
425       result = OK;
426       break;
427     }
428   return result;
429 }
430 
MessageServer(QObject * parent,QString const & version,QString const & revision)431 MessageServer::MessageServer (QObject * parent, QString const& version, QString const& revision)
432   : QObject {parent}
433   , m_ {this, version, revision}
434 {
435 }
436 
start(port_type port,QHostAddress const & multicast_group_address,QSet<QString> const & network_interface_names)437 void MessageServer::start (port_type port, QHostAddress const& multicast_group_address
438                            , QSet<QString> const& network_interface_names)
439 {
440   // qDebug () << "MessageServer::start port:" << port << "multicast addr:" << multicast_group_address.toString () << "network interfaces:" << network_interface_names;
441   if (port != m_->localPort ()
442       || multicast_group_address != m_->multicast_group_address_
443       || network_interface_names != m_->network_interfaces_)
444     {
445       m_->leave_multicast_group ();
446       if (impl::UnconnectedState != m_->state ())
447         {
448           m_->close ();
449         }
450       if (!(multicast_group_address.isNull () || is_multicast_address (multicast_group_address)))
451         {
452           Q_EMIT error ("Invalid multicast group address");
453         }
454       else if (is_MAC_ambiguous_multicast_address (multicast_group_address))
455         {
456           Q_EMIT error ("MAC-ambiguous IPv4 multicast group address not supported");
457         }
458       else
459         {
460           m_->multicast_group_address_ = multicast_group_address;
461           m_->network_interfaces_ = network_interface_names;
462           QHostAddress local_addr {is_multicast_address (multicast_group_address)
463                                    && impl::IPv4Protocol == multicast_group_address.protocol () ? QHostAddress::AnyIPv4 : QHostAddress::Any};
464           if (port && m_->bind (local_addr, port, m_->bind_mode_))
465             {
466               m_->join_multicast_group ();
467             }
468         }
469     }
470 }
471 
clear_decodes(ClientKey const & key,quint8 window)472 void MessageServer::clear_decodes (ClientKey const& key, quint8 window)
473 {
474   auto iter = m_->clients_.find (key);
475   if (iter != std::end (m_->clients_))
476     {
477       QByteArray message;
478       NetworkMessage::Builder out {&message, NetworkMessage::Clear, key.second, (*iter).negotiated_schema_number_};
479       out << window;
480       m_->send_message (out, message, key.first, (*iter).sender_port_);
481     }
482 }
483 
reply(ClientKey const & key,QTime time,qint32 snr,float delta_time,quint32 delta_frequency,QString const & mode,QString const & message_text,bool low_confidence,quint8 modifiers)484 void MessageServer::reply (ClientKey const& key, QTime time, qint32 snr, float delta_time
485                            , quint32 delta_frequency, QString const& mode
486                            , QString const& message_text, bool low_confidence, quint8 modifiers)
487 {
488   auto iter = m_->clients_.find (key);
489   if (iter != std::end (m_->clients_))
490     {
491       QByteArray message;
492       NetworkMessage::Builder out {&message, NetworkMessage::Reply, key.second, (*iter).negotiated_schema_number_};
493       out << time << snr << delta_time << delta_frequency << mode.toUtf8 ()
494           << message_text.toUtf8 () << low_confidence << modifiers;
495       m_->send_message (out, message, key.first, (*iter).sender_port_);
496     }
497 }
498 
replay(ClientKey const & key)499 void MessageServer::replay (ClientKey const& key)
500 {
501   auto iter = m_->clients_.find (key);
502   if (iter != std::end (m_->clients_))
503     {
504       QByteArray message;
505       NetworkMessage::Builder out {&message, NetworkMessage::Replay, key.second, (*iter).negotiated_schema_number_};
506       m_->send_message (out, message, key.first, (*iter).sender_port_);
507     }
508 }
509 
close(ClientKey const & key)510 void MessageServer::close (ClientKey const& key)
511 {
512   auto iter = m_->clients_.find (key);
513   if (iter != std::end (m_->clients_))
514     {
515       QByteArray message;
516       NetworkMessage::Builder out {&message, NetworkMessage::Close, key.second, (*iter).negotiated_schema_number_};
517       m_->send_message (out, message, key.first, (*iter).sender_port_);
518     }
519 }
520 
halt_tx(ClientKey const & key,bool auto_only)521 void MessageServer::halt_tx (ClientKey const& key, bool auto_only)
522 {
523   auto iter = m_->clients_.find (key);
524   if (iter != std::end (m_->clients_))
525     {
526       QByteArray message;
527       NetworkMessage::Builder out {&message, NetworkMessage::HaltTx, key.second, (*iter).negotiated_schema_number_};
528       out << auto_only;
529       m_->send_message (out, message, key.first, (*iter).sender_port_);
530     }
531 }
532 
free_text(ClientKey const & key,QString const & text,bool send)533 void MessageServer::free_text (ClientKey const& key, QString const& text, bool send)
534 {
535   auto iter = m_->clients_.find (key);
536   if (iter != std::end (m_->clients_))
537     {
538       QByteArray message;
539       NetworkMessage::Builder out {&message, NetworkMessage::FreeText, key.second, (*iter).negotiated_schema_number_};
540       out << text.toUtf8 () << send;
541       m_->send_message (out, message, key.first, (*iter).sender_port_);
542     }
543 }
544 
location(ClientKey const & key,QString const & loc)545 void MessageServer::location (ClientKey const& key, QString const& loc)
546 {
547   auto iter = m_->clients_.find (key);
548   if (iter != std::end (m_->clients_))
549   {
550     QByteArray message;
551     NetworkMessage::Builder out {&message, NetworkMessage::Location, key.second, (*iter).negotiated_schema_number_};
552     out << loc.toUtf8 ();
553     m_->send_message (out, message, key.first, (*iter).sender_port_);
554   }
555 }
556 
highlight_callsign(ClientKey const & key,QString const & callsign,QColor const & bg,QColor const & fg,bool last_only)557 void MessageServer::highlight_callsign (ClientKey const& key, QString const& callsign
558                                         , QColor const& bg, QColor const& fg, bool last_only)
559 {
560   auto iter = m_->clients_.find (key);
561   if (iter != std::end (m_->clients_))
562   {
563     QByteArray message;
564     NetworkMessage::Builder out {&message, NetworkMessage::HighlightCallsign, key.second, (*iter).negotiated_schema_number_};
565     out << callsign.toUtf8 () << bg << fg << last_only;
566     m_->send_message (out, message, key.first, (*iter).sender_port_);
567   }
568 }
569 
switch_configuration(ClientKey const & key,QString const & configuration_name)570 void MessageServer::switch_configuration (ClientKey const& key, QString const& configuration_name)
571 {
572   auto iter = m_->clients_.find (key);
573   if (iter != std::end (m_->clients_))
574   {
575     QByteArray message;
576     NetworkMessage::Builder out {&message, NetworkMessage::SwitchConfiguration, key.second, (*iter).negotiated_schema_number_};
577     out << configuration_name.toUtf8 ();
578     m_->send_message (out, message, key.first, (*iter).sender_port_);
579   }
580 }
581 
configure(ClientKey const & key,QString const & mode,quint32 frequency_tolerance,QString const & submode,bool fast_mode,quint32 tr_period,quint32 rx_df,QString const & dx_call,QString const & dx_grid,bool generate_messages)582 void MessageServer::configure (ClientKey const& key, QString const& mode, quint32 frequency_tolerance
583                                , QString const& submode, bool fast_mode, quint32 tr_period, quint32 rx_df
584                                , QString const& dx_call, QString const& dx_grid, bool generate_messages)
585 {
586   auto iter = m_->clients_.find (key);
587   if (iter != std::end (m_->clients_))
588   {
589     QByteArray message;
590     NetworkMessage::Builder out {&message, NetworkMessage::Configure, key.second, (*iter).negotiated_schema_number_};
591     out << mode.toUtf8 () << frequency_tolerance << submode.toUtf8 () << fast_mode << tr_period << rx_df
592         << dx_call.toUtf8 () << dx_grid.toUtf8 () << generate_messages;
593     m_->send_message (out, message, key.first, (*iter).sender_port_);
594   }
595 }
596