RDK Documentation (Open Sourced RDK Components)
main.cpp
1 /*
2  * If not stated otherwise in this file or this component's Licenses.txt file the
3  * following copyright and licenses apply:
4  *
5  * Copyright 2016 RDK Management
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18 */
19 
20 
21 /**
22 * @defgroup trm
23 * @{
24 * @defgroup wsproxy
25 * @{
26 **/
27 
28 
29 #include <arpa/inet.h>
30 #include <sys/socket.h>
31 #include <sys/un.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <pthread.h>
39 #include <map>
40 #include <QCoreApplication>
41 #include <QRegExp>
42 #include <iostream>
43 #include <QStringList>
44 #include <QString>
45 #include <QSettings>
46 #include <QNetworkInterface>
47 #include <QNetworkAddressEntry>
48 #include "rdk_debug.h"
49 
50 #include <QtWebSockets/QWebSocketServer>
51 #include <QtWebSockets/QWebSocket>
52 #include <QDateTime>
53 
54 #include "qt_websocketproxy.h"
55 #include "safec_lib.h"
56 
57 static const int header_length = 16;
58 
59 enum MessageType {
60  REQUEST = 0x1234,
61  RESPONSE = 0x1800,
62  NOTIFICATION = 0x1400,
63  UNKNOWN,
64 };
65 
66 static const char *trm_ip = 0;
67 static const char *trm_port = 0;
68 
69 static int trm_socket_fd = -1;
70 static pthread_mutex_t conn_mutex;
71 typedef std::map<void *, int> connection_ct_t;
72 static connection_ct_t connections;
73 const char* localHost="lo";
74 const char* devicePropertiesPath ="/etc/device.properties";
75 const char* mocaIntTagName = "MOCA_INTERFACE";
76 
77 class AutoLock_
78 {
79 public:
80  AutoLock_(void) {
81  pthread_mutex_lock(&conn_mutex);
82  }
83  ~AutoLock_(void) {
84  pthread_mutex_unlock(&conn_mutex);
85  }
86 };
87 
88 #define AutoLock() AutoLock_ a()
89 
90 static int get_connection_id(void)
91 {
92  static int connection_id = 0;
93  int connid = 0;
94  connection_id++;
95  connid = connection_id;
96  return connid;
97 }
98 
99 static int conn_to_id(void *conn)
100 {
101  int connection_id = -1;
102 
103  connection_ct_t::iterator it = connections.find(conn);
104  if (it != connections.end()) {
105  connection_id = it->second;
106  }
107  else {
108  __TIMESTAMP();
109  printf("Cannot find connection\r\n");
110  connection_id = -1;
111  }
112 
113  return connection_id;
114 }
115 
116 static void * id_to_conn(int connection_id)
117 {
118  void *conn = 0;
119 
120  if (connection_id >= 0) {
121  connection_ct_t::iterator it;
122  for (it = connections.begin(); it != connections.end(); it++) {
123  __TIMESTAMP();
124  printf("id_to_conn::Trying Connection Pair [%d] with [%p]\r\n", it->second, it->first);
125 
126  if (connection_id == (it->second)) {
127  conn = it->first;
128  break;
129  }
130  }
131  }
132 
133  return conn;
134 }
135 
136 static void add_connection(void *conn)
137 {
138  int connection_id = get_connection_id();
139  __TIMESTAMP();
140  printf("Adding Connection [%d] with [%p]\r\n", connection_id, conn);
141  connections.insert(connections.end(), std::pair<void*, int>(conn, connection_id));
142 }
143 
144 static void remove_connection(void *conn)
145 {
146  __TIMESTAMP();
147  printf("Removing Connection [%p]\r\n", conn);
148 
149  connection_ct_t::iterator it = connections.find(conn);
150  if (it != connections.end()) {
151  connections.erase(it);
152  }
153 }
154 
155 static void remove_all_connections(void)
156 {
157  connection_ct_t::iterator it;
158  for (it = connections.begin(); it != connections.end(); it++) {
159  __TIMESTAMP();
160  printf("Removing Connection [%p]\r\n", it->first);
161  extern int mg_websocket_close(void * conn);
162  mg_websocket_close(it->first);
163  }
164 
165  connections.clear();
166  extern int mg_websocket_close_all_ssl();
167  mg_websocket_close_all_ssl();
168 
169 }
170 
171 
172 static int connect_to_trm(const char *ip, int port, int *trm_fd)
173 {
174  int socket_fd = -1;
175  int socket_error = 0;
176  struct sockaddr_in trm_address = {0};
177  trm_address.sin_family = AF_INET;
178  trm_address.sin_addr.s_addr = inet_addr(ip);
179  trm_address.sin_port = htons(port);
180 
181  socket_fd = socket(AF_INET, SOCK_STREAM, 0);
182  __TIMESTAMP();
183  fprintf(_TRMPRX_OUT_, "Connecting to remote\r\n");
184  while(1) {
185  static int retry_count = 10;
186  socket_error = connect(socket_fd, (struct sockaddr *) &trm_address, sizeof(struct sockaddr_in));
187  if (socket_error == ECONNREFUSED && retry_count > 0) {
188  __TIMESTAMP();
189  fprintf(_TRMPRX_OUT_, "TRM Server is not started...retry to connect\r\n");
190  sleep(2);
191  retry_count--;
192  }
193  else {
194  break;
195  }
196  }
197 
198  if (socket_error == 0) {
199  __TIMESTAMP();
200  fprintf(_TRMPRX_OUT_, "Connected\r\n");
201 
202  int current_flags = fcntl(socket_fd, F_GETFL, 0);
203  current_flags &= (~O_NONBLOCK);
204  fcntl(socket_fd, F_SETFL, current_flags);
205  *trm_fd = socket_fd;
206  }
207  else {
208  close(socket_fd);
209  *trm_fd = -1;
210  }
211 
212  return socket_error;
213 }
214 
215 int begin_request_callback(void */*conn*/)
216 {
217  AutoLock();
218  __TIMESTAMP();
219  printf("[%s]\r\n", __func__);
220  return 0;
221 }
222 
223 void end_request_callback(void */*conn*/, int /*reply_status_code*/)
224 {
225  AutoLock();
226  __TIMESTAMP();
227  printf("[%s]\r\n", __func__);
228 }
229 
230 int websocket_connect_callback(void */*conn*/)
231 {
232  AutoLock();
233  __TIMESTAMP();
234  printf("[%s]\r\n", __func__);
235  return 0;
236 }
237 
238 int websocket_disconnect_callback(void *conn)
239 {
240 
241  QWebSocket *wssocket = (QWebSocket *)conn;
242 
243  /* Notify TRM */
244  __TIMESTAMP();
245  printf("[%s] - removing connection from TRM\r\n", __func__);
246  extern int websocket_data_callback(void *conn, int flags, char *trm_data, size_t trm_data_length);
247 
248 
249  /* Notify TRM with Device IP infomration.*/
250  if (wssocket)
251  {
252  char eventString[256];
253  QString ipString = wssocket->peerAddress().toString();
254  qint64 currentEpoch = QDateTime(QDate::currentDate(), QTime::currentTime()).toMSecsSinceEpoch();
255  std::string reason = "TRM_Event_Disconnect";
256 
257  int length = sprintf_s(eventString,sizeof(eventString),"{ \"notifyClientConnectionEvent\": { \"eventName\" :\"%s\",\"clientIP\" : \"%s\",\"timeStamp\": %lld } }",reason.c_str(),ipString.toUtf8().data(),currentEpoch);
258  if(length < EOK) {
259  ERR_CHK(length);
260  }
261  websocket_data_callback(conn, 0, &eventString[0],length);
262 
263  //__TIMESTAMP(); printf("Disconnected from IP -[%s] and Size [%d] \r\n",ipString.toUtf8().data(),ipString.size());
264  //__TIMESTAMP(); printf("Current Epoch Time -[%lld] \r\n",currentEpoch);
265  //__TIMESTAMP(); printf("Size of the Event string %d \r\n",length);
266 
267  }
268 
269  websocket_data_callback(conn, 8, NULL, 0);
270 
271  AutoLock();
272  __TIMESTAMP();
273  printf("[%s]\r\n", __func__);
274  remove_connection(conn);
275  return 0;
276 }
277 
278 void websocket_ready_callback(void *conn)
279 {
280  AutoLock();
281  __TIMESTAMP();
282  printf("[%s]\r\n", __func__);
283  add_connection(conn);
284 }
285 
286 int log_message_callback(void */*conn*/, const char *message)
287 {
288  AutoLock();
289  __TIMESTAMP();
290  printf("[wsLog][%s]\r\n", message);
291  return 0;
292 }
293 
294 int websocket_data_callback_with_clientId(int connection_id, int flags, char *trm_data, size_t trm_data_length){
295  int keep_connection = 1;
296  printf("[%s][%.*s]\r\n", __func__, trm_data_length, trm_data);
297  /* Packaget into TRM message. Prexif transport protocol */
298  size_t payload_length = trm_data_length;
299  if (connection_id >= 0 && trm_data_length >= 0) {
300  /* First prepend header */
301  unsigned char *buf = (unsigned char *) malloc(payload_length + header_length);
302  int idx = 0;
303  /* Magic Word */
304  buf[idx++] = 'T';
305  buf[idx++] = 'R';
306  buf[idx++] = 'M';
307  buf[idx++] = 'S';
308  /* Type, set to UNKNOWN, as it is not used right now*/
309  buf[idx++] = (UNKNOWN & 0xFF000000) >> 24;
310  buf[idx++] = (UNKNOWN & 0x00FF0000) >> 16;
311  buf[idx++] = (UNKNOWN & 0x0000FF00) >> 8;
312  buf[idx++] = (UNKNOWN & 0x000000FF) >> 0;
313  /* client id */
314  buf[idx++] = (connection_id & 0xFF000000) >> 24;
315  buf[idx++] = (connection_id & 0x00FF0000) >> 16;
316  buf[idx++] = (connection_id & 0x0000FF00) >> 8;
317  buf[idx++] = (connection_id & 0x000000FF) >> 0;
318  /* Payload length */
319  buf[idx++] = (payload_length & 0xFF000000) >> 24;
320  buf[idx++] = (payload_length & 0x00FF0000) >> 16;
321  buf[idx++] = (payload_length & 0x0000FF00) >> 8;
322  buf[idx++] = (payload_length & 0x000000FF) >> 0;
323  /* Read payload from ws*/
324 
325  if (payload_length > 0) {
326  errno_t safec_rc = memcpy_s((void *)&buf[idx], payload_length, trm_data, payload_length);
327  ERR_CHK(safec_rc);
328  }
329 
330  for (idx = 0; idx < (header_length); idx++) {
331  fprintf(_TRMPRX_OUT_, "%02x", buf[idx]);
332  }
333  for (; idx < (payload_length + header_length); idx++) {
334  fprintf(_TRMPRX_OUT_, "%c", buf[idx]);
335  }
336 
337  fprintf(_TRMPRX_OUT_, "]At %d\r\n==============================================================\r\n", idx);
338 
339  /* Write payload from fastcgi to TRM */
340  int write_trm_count = write(trm_socket_fd, buf, payload_length + header_length);
341  __TIMESTAMP();
342  fprintf(_TRMPRX_OUT_, "Send to TRM %d vs expected %d\r\n", write_trm_count, payload_length + header_length);
343  free(buf);
344  }
345  else {
346  __TIMESTAMP();
347  printf("invalid connection %d\r\n", connection_id);
348  }
349 
350  return keep_connection;
351 }
352 int websocket_data_callback(void *conn, int flags, char *trm_data, size_t trm_data_length)
353 {
354  flags = flags;
355  __TIMESTAMP();
356  int connection_id = -1;
357  { AutoLock();
358  connection_id = conn_to_id(conn);
359  }
360  return websocket_data_callback_with_clientId (connection_id, flags, trm_data, trm_data_length);
361 }
362 
363 static void * TRM_response_listener(void * /*args*/)
364 {
365  while (1) {
366  /* Retry connect OUTSIDE autoLock */
367  while (trm_socket_fd < 0) {
368  /* connection to TRM is lost, Reset all WS connections */
369  { AutoLock();
370  remove_all_connections();
371  }
372  sleep(10);
373  __TIMESTAMP();
374  fprintf(_TRMPRX_OUT_, "Retry Connecting to TRM\r\n");
375  connect_to_trm(trm_ip, atoi(trm_port), &trm_socket_fd);
376  }
377 
378  int idx = 0;
379  size_t payload_length = 0;
380  int connection_id = -1;
381  /*Always start reading from 16 header bytes */
382  char *buf = (char *) malloc(header_length);
383  /* Read Response from TRM, read header first, then payload */
384  int read_trm_count = read(trm_socket_fd, buf, header_length);
385  __TIMESTAMP();
386  fprintf(_TRMPRX_OUT_, "Read Header from TRM %d vs expected %d\r\n", read_trm_count, header_length);
387  __TIMESTAMP();
388  fprintf(_TRMPRX_OUT_, "\r\n=====RESPONSE HEADER===================================================\r\n[");
389 
390  for (idx = 0; idx < (header_length); idx++) {
391  fprintf(_TRMPRX_OUT_, "%02x", buf[idx]);
392  }
393  __TIMESTAMP();
394  fprintf(_TRMPRX_OUT_, "\r\n==============================================================\r\n[");
395 
396  if (read_trm_count == header_length) {
397  int magic_cookie_offset = 0;
398  int connection_id_offset = 8;
399  int payload_length_offset = 12;
400 
401  if ((buf[magic_cookie_offset+0] != 'T') ||
402  (buf[magic_cookie_offset+1] != 'R') ||
403  (buf[magic_cookie_offset+2] != 'M') ||
404  (buf[magic_cookie_offset+3] != 'S')) {
405  //TODO: close the non-complying connection!
406  fprintf(_TRMPRX_OUT_, "Mismatching Magic! Discard\r\n");
407  }
408 
409  connection_id =((((unsigned char)(buf[connection_id_offset+0])) << 24) |
410  (((unsigned char)(buf[connection_id_offset+1])) << 16) |
411  (((unsigned char)(buf[connection_id_offset+2])) << 8 ) |
412  (((unsigned char)(buf[connection_id_offset+3])) << 0 ));
413 
414  if (1) {
415 
416  payload_length =((((unsigned char)(buf[payload_length_offset+0])) << 24) |
417  (((unsigned char)(buf[payload_length_offset+1])) << 16) |
418  (((unsigned char)(buf[payload_length_offset+2])) << 8 ) |
419  (((unsigned char)(buf[payload_length_offset+3])) << 0 ));
420 
421  free(buf);
422  __TIMESTAMP();
423  fprintf(_TRMPRX_OUT_, " TRM Response payloads is %d and header %d\r\n", payload_length, header_length);
424  fflush(_TRMPRX_OUT_);
425 
426  buf = (char *) malloc(payload_length);
427  read_trm_count = read(trm_socket_fd, buf, payload_length);
428  __TIMESTAMP();
429  fprintf(_TRMPRX_OUT_, "Read Payload from TRM %d vs expected %d\r\n", read_trm_count, payload_length);
430 
431  if (read_trm_count == (int)payload_length) {
432  /* Write Response from TRM to fastcgi */
433  AutoLock();
434  printf("Content-type: text/html\r\n"
435  "Content-length:%d\r\n"
436  "Content-type:application/json\r\n"
437  "\r\n",
438  payload_length);
439 
440  void *conn = id_to_conn(connection_id);
441 
442  if (conn) {
443 #if 0
444  extern int mg_websocket_write(void * conn, const char *data, size_t data_len);
445  size_t write_ws_count = mg_websocket_write(conn, buf, read_trm_count);
446  fprintf(stderr, "Send to WS %d vs expected %d\r\n", write_ws_count, payload_length + 4/*WS overhead*/);
447  if (write_ws_count == 0 ) {
448  remove_connection(conn);
449  }
450 #else
451  //emit signal and let main loop handles it.
452  extern int mg_websocket_write(void * conn, const char *data, size_t data_len);
453  int write_ws_count = mg_websocket_write(conn, buf, read_trm_count);
454  write_ws_count = write_ws_count;
455 #endif
456  }
457  else {
458  // check id it is openssl tcp connection if so handle it or discard the buf.
459  extern int mg_websocket_write_ssl(int clientId, char *data, int data_len);
460  int write_ws_count = mg_websocket_write_ssl (connection_id, buf, read_trm_count);
461  write_ws_count = write_ws_count;
462  }
463  }
464  else {
465  if (read_trm_count <= 0) {
466  __TIMESTAMP();
467  fprintf(_TRMPRX_OUT_, "Remote connection is closed...Retry\r\n");
468  close(trm_socket_fd);
469  trm_socket_fd = -1;
470  }
471  }
472 
473  free(buf);
474  }
475  else {
476  __TIMESTAMP();
477  fprintf(_TRMPRX_OUT_, "Cannot find connection from id %d\r\n", connection_id);
478  }
479  }
480  else {
481  free(buf);
482  if (read_trm_count <= 0) {
483  __TIMESTAMP();
484  fprintf(_TRMPRX_OUT_, "Remote connection is closed...Retry\r\n");
485  close(trm_socket_fd);
486  trm_socket_fd = -1;
487  }
488  }
489  }
490 
491  return 0;
492 }
493 
494 static QString getIPFromInterface(QString interfaceName, bool isLoopback)
495 {
496  QString interfaceIp;
497  QNetworkInterface netInterface = QNetworkInterface::interfaceFromName(interfaceName);
498  if(netInterface.isValid())
499  {
500  foreach (const QNetworkAddressEntry &address, netInterface.addressEntries())
501  {
502  if(address.ip().protocol() != QAbstractSocket::IPv4Protocol)
503  {
504  continue;
505  }
506  if (address.ip().isLoopback() == isLoopback)
507  {
508  interfaceIp = address.ip().toString();
509  break;
510  }
511  }
512  }
513  else
514  {
515  qDebug()<<"TRM Invalid interface";
516  }
517  return interfaceIp;
518 }
519 
520 int main(int argc, char *argv[])
521 {
522  /* Start the main loop */
523  QCoreApplication app(argc, argv);
524  QStringList args = app.arguments();
525 
526  char *debugConfigFile = NULL;
527 
528  QRegExp optionDebugFile("--debugconfig");
529  QRegExp optionBound("-bound");
530  QStringList boundIPs;
531 
532  for (int i = 1; i < args.size(); ++i) {
533  if (optionBound.indexIn(args.at(i)) != -1 ) {
534  boundIPs << args.at(i+1);
535  ++i;
536  qDebug() << "bound to " << boundIPs;
537  }
538  else if (optionDebugFile.indexIn(args.at(i)) != -1 ) {
539  debugConfigFile = argv[i+1];
540  ++i;
541  qDebug() << "rdklogger debug file is " << debugConfigFile;
542  }
543  }
544 
545  if (argc < 3) {
546  printf("TRM-WS-Proxy <server ip addr> <port> --debugconfi <config file> [optionals]\r\n");
547  printf("optionals: -bound <bound ip addr1> <bound ip addr2>...\r\n");
548  return 0;
549  }
550 
551  rdk_logger_init(debugConfigFile);
552 
553  RDK_LOG(RDK_LOG_DEBUG, "LOG.RDK.TRM", "This is a test line");
554 
555 
556 
557 #define TRM_WS_PROXY_LISTENING_PORT 9989
558 
559  //Initiate utilities
560  pthread_mutex_init(&conn_mutex, NULL);
561  __TIMESTAMP();
562  printf("Starting TRM-WS-Proxy\r\n");
563 
564 
565  /* First create a persistent connection to TRM */
566  trm_ip = argv[1];
567  trm_port = argv[2];
568 
569  connect_to_trm(trm_ip, atoi(trm_port), &trm_socket_fd);
570  pthread_t trm_rsp_listener_thread;
571  pthread_create(&trm_rsp_listener_thread, NULL, TRM_response_listener, (void *)trm_socket_fd);
572 
573  if (boundIPs.empty())
574  {
575 
576  //Adding local host ip to the list
577  QString localHostIp = getIPFromInterface(localHost, true);
578  if(!localHostIp.isNull())
579  {
580  boundIPs.append(localHostIp);
581  }
582 
583  //Adding moca interface ip to the list
584  QSettings deviceSetting( devicePropertiesPath, QSettings::IniFormat );
585  QString mocaInterfaceName = deviceSetting.value( mocaIntTagName).toString();
586  if(!mocaInterfaceName.isNull())
587  {
588  int retryCount = 10;
589  QString mocaIp;
590  do
591  {
592  QString mocaIp = getIPFromInterface(mocaInterfaceName, false);
593  if(!mocaIp.isNull())
594  {
595  boundIPs.append(mocaIp);
596  qDebug()<<"TRM Moca ip present ...continue ";
597  break;
598  }
599  else
600  {
601  qDebug()<<"TRM Moca ip not present retry after 5 sleep";
602  sleep(5);
603  }
604  retryCount--;
605  } while( retryCount);
606  }
607  else
608  {
609  qDebug()<<"TRM Moca interface name not found";
610  }
611  }
612 
613  WebSocketProxy wsproxy(boundIPs, TRM_WS_PROXY_LISTENING_PORT);
614 
615  int app_return = app.exec();
616 
617  //Initiate utilities
618  pthread_mutex_destroy(&conn_mutex);
619 
620  __TIMESTAMP();
621  printf("TRM WS-Daemon Exit!!\r\n");
622  return app_return;
623 }
624 
625 /** @} */
626 /** @} */
AutoLock_
Definition: main.cpp:77
rdk_debug.h
RDK_LOG
#define RDK_LOG
Definition: rdk_debug.h:258
WebSocketProxy
Definition: qt_websocketproxy.h:56
rdk_logger_init
rdk_Error rdk_logger_init(const char *debugConfigFile)
Initialize the logger. Sets up the environment variable storage by parsing debug configuration file t...
Definition: rdk_logger_init.c:57