botsense  3.2.0
RoadNarrows Client-Server Proxied Services Framework
bsProxyClient.c
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: BotSense
4 //
5 // Program: bsProxy
6 //
7 // File: bsProxyClient.c
8 //
9 /*! \file
10  *
11  * $LastChangedDate: 2010-08-20 11:36:38 -0600 (Fri, 20 Aug 2010) $
12  * $Rev: 568 $
13  *
14  * \brief \h_botsense bsProxy client routines.
15  *
16  * \author Robin Knight (robin.knight@roadnarrows.com)
17  *
18  * \copyright
19  * \h_copy 2007-2017. RoadNarrows LLC.\n
20  * http://www.roadnarrows.com\n
21  * All Rights Reserved
22  */
23 // Permission is hereby granted, without written agreement and without
24 // license or royalty fees, to use, copy, modify, and distribute this
25 // software and its documentation for any purpose, provided that
26 // (1) The above copyright notice and the following two paragraphs
27 // appear in all copies of the source code and (2) redistributions
28 // including binaries reproduces these notices in the supporting
29 // documentation. Substantial modifications to this software may be
30 // copyrighted by their authors and need not follow the licensing terms
31 // described here, provided that the new terms are clearly indicated in
32 // all files where they apply.
33 //
34 // IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY MEMBERS/EMPLOYEES
35 // OF ROADNARROW LLC OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
36 // PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
37 // DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
38 // EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
39 // THE POSSIBILITY OF SUCH DAMAGE.
40 //
41 // THE AUTHOR AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
42 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
43 // FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
44 // "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
45 // PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
46 //
47 ////////////////////////////////////////////////////////////////////////////////
48 
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <stdarg.h>
53 
54 #include "rnr/rnrconfig.h"
55 #include "rnr/log.h"
56 #include "rnr/new.h"
57 #include "rnr/sock.h"
58 #include "rnr/netmsgs.h"
59 
60 #include "botsense/BotSense.h"
61 #include "botsense/bsProxyMsgs.h"
62 
63 #include "bsProxy.h"
64 
65 
66 // ---------------------------------------------------------------------------
67 // Private Interface
68 // ---------------------------------------------------------------------------
69 
70 //
71 // Local data
72 //
73 static pthread_mutex_t BsClientBusyMutex; ///< busy mutex
74 static pthread_cond_t BsClientBusyCond; ///< busy condition
75 
76 /*!
77  * \brief Convert pointer to client to client handle.
78  *
79  * \param pClient \h_botsense client.
80  */
81 #define CLIENT_HND(pClient) \
82  ServerClientSd2Hnd(SocketAttrGetSd(pClient->m_pClientSock))
83 
84 //.............................................................................
85 // Mutual Exclusion Functions
86 //.............................................................................
87 
88 /*!
89  * \brief Lock client's global busy mutual exclusion.
90  */
91 static inline void ClientLockBusy()
92 {
93  int rc;
94 
95  if( (rc = pthread_mutex_lock(&BsClientBusyMutex)) != 0 )
96  {
97  errno = rc;
98  LOGSYSERROR("pthread_mutex_lock()");
99  }
100 }
101 
102 /*!
103  * \brief Unlock client's global busy mutual exclusion.
104  */
105 static inline void ClientUnlockBusy()
106 {
107  int rc;
108 
109  if( (rc = pthread_mutex_unlock(&BsClientBusyMutex)) != 0 )
110  {
111  errno = rc;
112  LOGSYSERROR("pthread_mutex_unlock()");
113  }
114 }
115 
116 /*!
117  * \brief Try to lock client's global busy mutual exclusion.
118  *
119  * \return
120  * Returns true if lock is acquired. Otherwise returns false if mutex already
121  * locked.
122  */
123 static inline bool_t ClientBusyTryLock()
124 {
125  return pthread_mutex_trylock(&BsClientBusyMutex) == 0? true: false;
126 }
127 
128 /*!
129  * \brief Broadcast that a client has been freed or deleted.
130  *
131  * A broadcast will unblock all threads currently blocked on the busy condition
132  * variable. Only those threads waiting on the client whose condition has
133  * changed will run. If multiple threads are waiting on the same client, then
134  * only one is schedule to run.
135  */
136 static inline void ClientBroadcastNotBusy()
137 {
138  int rc;
139 
140  if( (rc = pthread_cond_broadcast(&BsClientBusyCond)) != 0 )
141  {
142  errno = rc;
143  LOGSYSERROR("pthread_cond_broadcast()");
144  }
145 }
146 
147 /*!
148  * \brief Wait on a client to become free.
149  */
150 static inline void ClientWaitNotBusy()
151 {
152  int rc;
153 
154  if( (rc = pthread_cond_wait(&BsClientBusyCond, &BsClientBusyMutex)) != 0 )
155  {
156  errno = rc;
157  LOGSYSERROR("pthread_cond_wait()");
158  }
159 }
160 
161 
162 //.............................................................................
163 // Basic I/O Functions
164 //.............................................................................
165 
166 PRAGMA_IGNORED(sign-conversion)
167 /*!
168  * \brief FD_SET() wrapper with no annoying warnings.
169  * \param fd File descriptor to add to set.
170  * \param pset Pointer to fd set.
171  */
172 static inline void fdset_nowarn(int fd, fd_set *pset)
173 {
174  FD_SET(fd, pset);
175 }
176 PRAGMA_WARNING(sign-conversion)
177 
178 /*!
179  * \brief Read count bytes from socket.
180  *
181  * \param pClient \h_botsense client.
182  * \param [out] buf Destination buffer.
183  * \param uCount Number of bytes to read.
184  *
185  * \return Returns <0 on error, 0 on time out, or \h_ge 0 for the number of
186  * bytes read.
187  */
188 static ssize_t ClientRead(BsProxyClientCtl_T *pClient,
189  byte_t buf[],
190  size_t uCount)
191 {
192  uint_t usec = BSPROXY_TUNE_T_RECV;
193  struct timeval tstart;
194  uint_t tdelta;
195  struct timeval timeout;
196  fd_set rset;
197  int sd;
198  int nFd;
199  ssize_t n;
200  ssize_t nBytes = 0;
201 
202  LOGDIAG4CALL(_TSTR(ClientThisHasName(pClient)), _TPTR(buf), _TUINT(uCount));
203 
204  // client is disconnected or in an unrecoverable state
205  if( pClient->m_eClientState == BsProxyClientStateZombie )
206  {
208  }
209 
210  // socket descriptor
211  sd = SocketAttrGetSd(pClient->m_pClientSock);
212 
213  //
214  // Read the data until either 1) count bytes are read, 2) a time out occurs,
215  // or 3) an error occurs.
216  //
217  while( nBytes < uCount )
218  {
219  FD_ZERO(&rset);
220  fdset_nowarn(sd, &rset);
221 
222  // mark now
223  timer_mark(&tstart);
224 
225  // (re)load timeout (gets munged after each select())
226  timeout.tv_sec = (time_t)(usec / 1000000);
227  timeout.tv_usec = (time_t)(usec % 1000000);
228 
229  // wait to bytes to be available to be read
230  nFd = select(sd+1, &rset, NULL, NULL, &timeout);
231 
232  // system error occurred on select - interpret
233  if( nFd < 0 )
234  {
235  switch(errno)
236  {
237  case EAGAIN: // non-blocking timeout
238  case EINTR: // read was interrupted
239  break;
240  default: // non-recoverable error
241  BSPROXY_LOG_SYSERROR(ServerClientSd2Hnd(sd), "select(%d,...)", sd);
242  return -BS_ECODE_BAD_RECV;
243  }
244  }
245 
246  // select() timeout occurred
247  else if( nFd == 0 )
248  {
249  LOGDIAG4("select() on read timed out.");
250  break;
251  }
252 
253  // read the available data from the socket
254  n = read(sd, buf+nBytes, uCount-(size_t)nBytes);
255 
256  // system error occurred on read - interpret
257  if( n < 0 )
258  {
259  switch(errno)
260  {
261  case EAGAIN: // non-blocking timeout
262  case EINTR: // read was interrupted
263  n = 0;
264  break;
265  default: // non-recoverable error
266  BSPROXY_LOG_SYSERROR(ServerClientSd2Hnd(sd), "select(%d,...)", sd);
267  return -BS_ECODE_BAD_RECV;
268  }
269  }
270 
271  // got some data
272  nBytes += n;
273 
274  // all of the requested bytes have been read
275  if( nBytes == uCount )
276  {
277  break;
278  }
279 
280  // determine time left for the non-blocking read timeout
281  if( nBytes < uCount )
282  {
283  tdelta = timer_elapsed(&tstart);
284  if( tdelta >= usec )
285  {
286  LOGDIAG4("%s() timed out.", LOGFUNCNAME);
287  break;
288  }
289  else
290  {
291  usec -= tdelta;
292  }
293  }
294  }
295 
296  LOGDIAG4("%s(): %zd bytes read.", LOGFUNCNAME, nBytes);
297 
298  return nBytes;
299 }
300 
301 /*!
302  * \brief Write count bytes to socket.
303  *
304  * \param pClient \h_botsense client.
305  * \param [in] buf Source buffer.
306  * \param uCount Number of bytes to write.
307  *
308  * \return Returns <0 on error, 0 on time out, or \h_ge 0 for the number of
309  * bytes written.
310  */
311 static ssize_t ClientWrite(BsProxyClientCtl_T *pClient,
312  byte_t buf[],
313  size_t uCount)
314 {
315  uint_t usec = BSPROXY_TUNE_T_SEND;
316  struct timeval tstart;
317  uint_t tdelta;
318  struct timeval timeout;
319  fd_set wset;
320  int sd;
321  int nFd;
322  ssize_t n;
323  ssize_t nBytes = 0;
324 
325  LOGDIAG4CALL(_TSTR(ClientThisHasName(pClient)), _TPTR(buf), _TUINT(uCount));
326 
327  // client is disconnected or in an unrecoverable state
328  if( pClient->m_eClientState == BsProxyClientStateZombie )
329  {
331  }
332 
333  // socket descriptor
334  sd = SocketAttrGetSd(pClient->m_pClientSock);
335 
336  //
337  // Write the data until either 1) count bytes are written, 2) a time out
338  // occurs, or 3) an error occurs.
339  //
340  while( nBytes < uCount )
341  {
342  FD_ZERO(&wset);
343  fdset_nowarn(sd, &wset);
344 
345  // mark now
346  timer_mark(&tstart);
347 
348  // (re)load timeout (gets munged after each select())
349  timeout.tv_sec = (time_t)(usec / 1000000);
350  timeout.tv_usec = (time_t)(usec % 1000000);
351 
352  nFd = select(sd+1, NULL, &wset, NULL, &timeout);
353 
354  // system error occurred on select - interpret
355  if( nFd < 0 )
356  {
357  switch(errno)
358  {
359  case EAGAIN: // non-blocking timeout
360  case EINTR: // write was interrupted
361  break;
362  default: // non-recoverable error
363  BSPROXY_LOG_SYSERROR(ServerClientSd2Hnd(sd), "select(%d,...)", sd);
364  return -BS_ECODE_BAD_SEND;
365  }
366  }
367 
368  // select() timeout occurred
369  else if( nFd == 0 )
370  {
371  LOGDIAG4("select() on write timed out.");
372  break;
373  }
374 
375  // socket is available for writing
376  n = write(sd, buf+nBytes, uCount-(size_t)nBytes);
377 
378  // system error occurred on write - interpret
379  if( n < 0 )
380  {
381  switch(errno)
382  {
383  case EAGAIN: // non-blocking timeout
384  case EINTR: // write was interrupted
385  n = 0;
386  break;
387  default: // non-recoverable error
388  BSPROXY_LOG_SYSERROR(ServerClientSd2Hnd(sd), "select(%d,...)", sd);
389  return -BS_ECODE_BAD_SEND;
390  }
391  }
392 
393  // wrote some data
394  nBytes += n;
395 
396  // all of the requested bytes have been written
397  if( nBytes == uCount )
398  {
399  break;
400  }
401 
402  // determine time left for non-blocking write timeout
403  if( nBytes < uCount )
404  {
405  tdelta = timer_elapsed(&tstart);
406  if( tdelta >= usec )
407  {
408  LOGDIAG4("%s() timed out.", LOGFUNCNAME);
409  break;
410  }
411  else
412  {
413  usec -= tdelta;
414  }
415  }
416  }
417 
418  LOGDIAG4("%s(): %zd bytes written.", LOGFUNCNAME, nBytes);
419 
420  return nBytes;
421 }
422 
423 /*!
424  * \brief Resync to client message stream.
425  *
426  * If a client receieve stream becomes unsynchronized, the message headers
427  * are aliased to some offset into the stream. To resync, bsProxy searches
428  * the stream for the magic bytes in the message header. Once found, the
429  * reset of the header is read.
430  *
431  * \param pClient \h_botsense client.
432  * \param [out] bufHdr Destination header buffer.
433  *
434  * \return Returns <0 on error or \h_ge 0 number of bytes for successful read.
435  */
436 static ssize_t ClientResync(BsProxyClientCtl_T *pClient, byte_t bufHdr[])
437 {
438  enum ReSyncState_T {GetMagicHi, GetMagicLo, GetRoH};
439  byte_t byMagicHi = (byte_t)((BSPROXY_MSG_MAGIC >> 8) & 0xff);
440  byte_t byMagicLo = (byte_t)(BSPROXY_MSG_MAGIC & 0xff);
441  size_t uCount = BSPROXY_MSG_HDR_LEN;
442  size_t n = 0;
443  enum ReSyncState_T eCurState = GetMagicHi;
444 
445  byte_t buf[BSPROXY_MSG_HDR_LEN];
446  ssize_t nBytes;
447  int i;
448 
449  //
450  // Search stream for message header.
451  //
452  while( (nBytes = ClientRead(pClient, buf, uCount)) > 0 )
453  {
454  for(i=0; i<nBytes; ++i)
455  {
456  switch( eCurState )
457  {
458  case GetMagicHi: // get some high magic
459  if( buf[i] == byMagicHi )
460  {
461  bufHdr[n++] = buf[i];
462  eCurState = GetMagicLo;
463  }
464  break;
465  case GetMagicLo: // now get some low magic
466  if( buf[i] == byMagicLo )
467  {
468  bufHdr[n++] = buf[i];
469  eCurState = GetRoH;
470  }
471  else // false magic
472  {
473  n = 0;
474  eCurState = GetMagicHi;
475  }
476  break;
477  case GetRoH: // get rest of header
478  bufHdr[n++] = buf[i];
479  break;
480  }
481  }
482 
483  uCount = BSPROXY_MSG_HDR_LEN - n;
484 
485  if( uCount == 0 )
486  {
487  nBytes = BSPROXY_MSG_HDR_LEN;
488  break;
489  }
490  }
491 
492  return nBytes;
493 }
494 
495 /*!
496  * \brief Flush receive stream.
497  *
498  * \param pClient \h_botsense client.
499  * \param uCount Number of bytes to flush.
500  */
501 static void ClientFlushInput(BsProxyClientCtl_T *pClient, size_t uCount)
502 {
503  byte_t buf[256];
504  ssize_t k = 0;
505  size_t n;
506 
507  do
508  {
509  uCount -= (size_t)k;
510  n = sizeof(buf) < uCount? sizeof(buf): uCount;
511  } while( (n > 0) && ((k = ClientRead(pClient, buf, n)) > 0) );
512 }
513 
514 
515 // ---------------------------------------------------------------------------
516 // Public Interface
517 // ---------------------------------------------------------------------------
518 
519 // ...........................................................................
520 // Client Global Initialization and Concurrency Control
521 // ...........................................................................
522 
523 /*!
524  * \brief The \h_botsense bsProxy server one-time client control initialization.
525  */
527 {
528  // create mutex
529  pthread_mutex_init(&BsClientBusyMutex, NULL);
530 
531  // create condition
532  pthread_cond_init(&BsClientBusyCond, NULL);
533 }
534 
535 /*!
536  * \brief Acquire client, locking it from other threads.
537  *
538  * The calling thread is blocked until the client is availble or has been
539  * deleted.
540  *
541  * \param hndClient Client handle.
542  *
543  * \return On success, returns pointer to locked client control block.
544  * On failure, NULL is returned.
545  */
547 {
548  BsProxyClientCtl_T *pClient;
549 
550  ClientLockBusy();
551 
552  while( ((pClient = ServerGetClient(hndClient)) != NULL) && pClient->m_bBusy )
553  {
555  }
556 
557  if( pClient )
558  {
559  pClient->m_bBusy = true;
560  }
561 
563 
564  return pClient;
565 }
566 
567 /*!
568  * \brief Release the locked client.
569  *
570  * A broadcast is sent to all blocking threads on the freed event.
571  *
572  * \param hndClient Client handle.
573  */
575 {
576  BsProxyClientCtl_T *pClient;
577 
578  ClientLockBusy();
579 
580  if( (pClient = ServerGetClient(hndClient)) != NULL )
581  {
582  pClient->m_bBusy = false;
583  }
584 
586 
588 }
589 
590 
591 // ...........................................................................
592 // Client Create and Destroy
593 // ...........................................................................
594 
595 /*!
596  * \brief Create new client control structure.
597  *
598  * \param pSockClient Accecpted client socket.
599  *
600  * \return Returns a newly allocated and intialized client control block.
601  */
602 BsProxyClientCtl_T *ClientNew(Socket_T *pSockClient)
603 {
604  BsProxyClientCtl_T *pClient;
605 
606  pClient = NEW(BsProxyClientCtl_T);
607  pClient->m_sClientName = new_strdup(SocketAttrGetRemoteName(pSockClient));
608  pClient->m_pClientSock = pSockClient;
610  pClient->m_uRefCnt = 0;
611  pClient->m_bServerTrace = false;
612 
613  return pClient;
614 }
615 
616 /*!
617  * \brief Delete a client with the server.
618  *
619  * \param pClient \h_botsense client to delete.
620  */
622 {
623  if( pClient != NULL )
624  {
625  SocketClose(pClient->m_pClientSock);
626  SocketDelete(pClient->m_pClientSock);
627  delete((char *)pClient->m_sClientName);
628  delete(pClient);
629  }
630 }
631 
632 
633 // ...........................................................................
634 // Client Attribute Sets and Gets
635 // ...........................................................................
636 
637 /*!
638  * \brief Set client's state.
639  *
640  * \param pClient \h_botsense client.
641  * \param eNewState New client state.
642  */
644 {
645  static const char *sZombie = "(zombie)";
646 
647  const char *sState;
648  char *sName;
649  size_t n;
650 
651  if( pClient == NULL )
652  {
653  return;
654  }
655 
656  else if( pClient->m_eClientState == eNewState )
657  {
658  return;
659  }
660 
661  switch( eNewState )
662  {
663  case BsProxyClientStateZombie: ///< disconnected but not deleted
664  sState = "disconnected";
665  n = strlen(pClient->m_sClientName) + strlen(sZombie) + 2;
666  sName = NEWSTR(n);
667  sprintf(sName, "%s %s", pClient->m_sClientName, sZombie);
668  delete((char *)pClient->m_sClientName);
669  pClient->m_sClientName = sName;
670  break;
671  case BsProxyClientStateNominal: ///< normal operation
672  sState = "nominal";
673  break;
674  case BsProxyClientStateReSync: ///< resyncing server with client
675  sState = "resyncing";
676  break;
677  default:
678  return;
679  }
680 
681  pClient->m_eClientState = eNewState;
682 
683  LOGDIAG2("%s: %s: %s.", ServerHasName(), ClientThisHasName(pClient), sState);
684 }
685 
686 
687 // ...........................................................................
688 // Client Receive Request Functions
689 // ...........................................................................
690 
691 /*!
692  * \brief Receive client request header.
693  *
694  * Sanity checks are performed on the header.
695  *
696  * \param pClient \h_botsense client.
697  * \param [out] pMsgHdr Received, unpacked message header.
698  *
699  * \return Returns \h_lt 0 on error or \h_ge 0 number of bytes for successful
700  * received.
701  */
702 static int ClientRecvHdr(BsProxyClientCtl_T *pClient, BsProxyMsgHdr_T *pMsgHdr)
703 {
704  byte_t bufHdr[BSPROXY_MSG_HDR_LEN]; // header buffer
705  int nBytes; // bytes read
706  size_t uBodyLen; // length of message body
707 
708  //
709  // Read the message header. The method depends on the client's state.
710  // Nominal Normal operation
711  // Resync Need to find the start of a header in stream, then read header
712  // Zombie Client is disconnecting, simply return error.
713  //
714  switch( pClient->m_eClientState )
715  {
716  case BsProxyClientStateNominal: ///< normal operation
717  nBytes = (int)ClientRead(pClient, bufHdr, BSPROXY_MSG_HDR_LEN);
718  break;
719  case BsProxyClientStateReSync: ///< resyncing server with client
720  nBytes = (int)ClientResync(pClient, bufHdr);
721  break;
722  case BsProxyClientStateZombie: ///< disconnected/bad state, but not deleted
723  default:
725  }
726 
727  // receive error
728  if( nBytes < 0 )
729  {
730  return -BS_ECODE_BAD_RECV;
731  }
732 
733  // nothing to receive
734  else if( nBytes == 0 )
735  {
736  return 0;
737  }
738 
739  // received header fragment, enter resync state
740  else if( nBytes < BSPROXY_MSG_HDR_LEN )
741  {
743  "Received %d bytes, expected %d byte header.",
744  nBytes, BSPROXY_MSG_HDR_LEN);
746  return 0;
747  }
748 
749  // unpack message header
750  else if( bsUnpackMsgHdr(bufHdr, (size_t)nBytes, pMsgHdr) < 0 )
751  {
753  "Received bad message header.");
755  return 0;
756  }
757 
758  // validate magic
759  else if( pMsgHdr->m_hdrMagic != BSPROXY_MSG_MAGIC )
760  {
762  "Received bad magic 0x%04x in message header.",
763  pMsgHdr->m_hdrMagic);
765  return 0;
766  }
767 
768  //
769  // The message body is too long.
770  //
771  else if( pMsgHdr->m_hdrBodyLen > BSPROXY_MSG_BODY_MAX )
772  {
773  uBodyLen = (size_t)pMsgHdr->m_hdrBodyLen;
775  "Received body length=%zu, flushing.", uBodyLen);
776  ClientFlushInput(pClient, uBodyLen);
777  return 0;
778  }
779 
780  else
781  {
782  return nBytes;
783  }
784 }
785 
786 /*!
787  * \brief Receive client request message body.
788  *
789  * Sanity checks are performed on the message.
790  *
791  * \param pClient \h_botsense client.
792  * \param [out] buf Received, packed message body.
793  * \param uBodyLen Message body length.
794  *
795  * \return Returns \h_lt 0 on error or \h_ge 0 number of bytes for successful
796  * received.
797  */
798 static int ClientRecvBody(BsProxyClientCtl_T *pClient,
799  byte_t buf[],
800  size_t uBodyLen)
801 {
802  int nBytes; // bytes read
803 
804  // read failed
805  if( (nBytes = (int)ClientRead(pClient, buf, uBodyLen)) < 0 )
806  {
808  "Failed to receive client message body.");
809  return -BS_ECODE_BAD_RECV;
810  }
811 
812  // read a message fragment
813  else if( nBytes < (ssize_t)uBodyLen )
814  {
816  "Received length=%zd, expected length=%zu.", nBytes, uBodyLen);
818  nBytes = 0;
819  }
820 
821  return nBytes;
822 }
823 
824 /*!
825  * \brief Receive a request message from client.
826  *
827  * \param hndClient \h_botsense client handle.
828  * \param [out] pMsgHdr Request message header.
829  * \param [out] addrBuf Pointer to allocated input receive message body buffer.
830  *
831  * \return
832  * Returns \h_lt 0 on client non-recoverable error,\n
833  * 0 for discarded message,\n
834  * \h_gt 0 number of bytes for successful read.
835  */
837  BsProxyMsgHdr_T *pMsgHdr,
838  byte_t **addrBuf)
839 {
840  BsProxyClientCtl_T *pClient; // BotSense client
841  size_t uBodyLen; // expected message body length
842  byte_t *pBuf = NULL; // allocated request packed message body
843  int nBytesHdr; // read message header bytes/return code
844  int nBytesBody; // read message body bytes/return code
845  int nMsgLen; // total message length/return code
846 
847  *addrBuf = NULL;
848 
849  // lock client
850  if( (pClient = ClientAcquire(hndClient)) == NULL )
851  {
853  }
854 
855  // receive message header
856  if( (nBytesHdr = ClientRecvHdr(pClient, pMsgHdr)) <= 0 )
857  {
858  nMsgLen = nBytesHdr; // return code
859  }
860 
861  // received a good header, now receive any message body
862  else
863  {
864  // (reenter) nominal state
865  if( pClient->m_eClientState != BsProxyClientStateNominal )
866  {
868  }
869 
870  BSPROXY_LOG_REQ(hndClient, pMsgHdr);
871 
872  // specified message body length
873  uBodyLen = (size_t)pMsgHdr->m_hdrBodyLen;
874 
875  //
876  // Get the message body.
877  //
878  if( uBodyLen > 0 )
879  {
880  // allocate buffer
881  pBuf = (byte_t *)new(uBodyLen);
882 
883  // receive
884  if( (nBytesBody = ClientRecvBody(pClient, pBuf, uBodyLen)) <= 0 )
885  {
886  delete(pBuf);
887  pBuf = NULL;
888  nMsgLen = nBytesBody; // return code
889  }
890 
891  // success
892  else
893  {
894  nMsgLen = nBytesHdr + nBytesBody;
895  }
896  }
897 
898  //
899  // No message body
900  //
901  else
902  {
903  pBuf = NULL;
904  nMsgLen = nBytesHdr;
905  }
906  }
907 
908  // set allocated buffer
909  *addrBuf = pBuf;
910 
911  if( nMsgLen > 0 )
912  {
913  LOGDIAG3("%s: %s: received message length=%d",
914  ServerHasName(), ClientThisHasName(pClient), nMsgLen);
915  }
916 
917  // unlock client
918  ClientRelease(hndClient);
919 
920  return nMsgLen;
921 }
922 
923 
924 // ...........................................................................
925 // Client Send Reponse Functions
926 // ...........................................................................
927 
928 /*!
929  * \brief Send an ok response to the client.
930  *
931  * \param hndClient \h_botsense client handle.
932  * \param uTid Request-response transaction id.
933  *
934  * \copydoc doc_return_std
935  */
937 {
938  byte_t bufRsp[BSPROXY_MSG_HDR_LEN];
939 
940  //
941  // Send the ok message.
942  // Note: Even though this is a server defined message, there is no message
943  // body, so use the lower level send function directly.
944  //
945  return ClientSendRsp(hndClient, BSPROXY_VCONN_SERVER, uTid,
946  BsProxyMsgIdRspOk, bufRsp, (size_t)0);
947 }
948 
949 /*!
950  * \brief Send an error response to the client.
951  *
952  * \param hndClient \h_botsense client handle.
953  * \param uTid Request-response transaction id.
954  * \param nECode \copydoc doc_param_ecode
955  * \param sErrFmt Error format string.
956  * \param ... Format string variable arguments.
957  *
958  * \copydoc doc_return_std
959  */
961  BsTid_T uTid,
962  int nECode,
963  const char *sErrFmt,
964  ...)
965 {
966  va_list ap;
967  int rc;
968 
969  va_start(ap, sErrFmt);
970  rc = ClientSendVErrorRsp(hndClient, uTid, nECode, sErrFmt,ap);
971  va_end(ap);
972  return rc;
973 }
974 
975 /*!
976  * \brief Send va_list error response to the client.
977  *
978  * \param hndClient \h_botsense client handle.
979  * \param uTid Request-response transaction id.
980  * \param nECode \copydoc doc_param_ecode
981  * \param sErrFmt Error format string.
982  * \param ap Format string va_list.
983  *
984  * \copydoc doc_return_std
985  */
987  BsTid_T uTid,
988  int nECode,
989  const char *sErrFmt,
990  va_list ap)
991 {
992  BsProxyRspErr_T msgRsp;
993 
994  // Fill in formatted error string as the response message body.
995  vsnprintf(msgRsp.m_emsg, BSPROXY_RSPERR_EMSG_LEN+1, sErrFmt, ap);
996  msgRsp.m_emsg[BSPROXY_RSPERR_EMSG_LEN] = 0;
997 
998  // set error code
999  if( nECode < 0 )
1000  {
1001  nECode = -nECode;
1002  }
1003  msgRsp.m_ecode = (byte_t)nECode;
1004 
1005  // send error message
1006  ClientSendServerRsp(hndClient, uTid, BsProxyMsgIdRspErr, &msgRsp);
1007 
1008  return -nECode;
1009 }
1010 
1011 /*!
1012  * \brief Send a server-terminated response message to client.
1013  *
1014  * The response message header must contain the information except for the
1015  * body length which will be automatically calculated.
1016  *
1017  * \param hndClient \h_botsense client handle.
1018  * \param uTid Request-response transaction id.
1019  * \param uMsgId BsProxyMsgIdRsp<em>x</em> response message id.
1020  * \param [in] pMsgRsp Response message populated structure. NULL if no body.
1021  *
1022  * \return Returns number of bytes written on success.\n
1023  * \copydoc doc_return_ecode.
1024  */
1026  BsTid_T uTid,
1027  BsProxyMsgId_T uMsgId,
1028  void *pMsgRsp)
1029 {
1030  const NMMsgDef_T *pMsgDef;
1031  byte_t bufRsp[BSPROXY_MSG_MAX_LEN];
1032  int n;
1033 
1034  // find the bsProxy server message definition
1035  pMsgDef = BsProxyLookupMsgDef(uMsgId);
1036 
1037  if( pMsgDef == NULL )
1038  {
1040  "Cannot find message definition for MsgId=%u.", uMsgId);
1041  return -BS_ECODE_INTERNAL;
1042  }
1043 
1044  //
1045  // Pack response message body
1046  //
1047  if( pMsgRsp != NULL )
1048  {
1049  n = BsProxyPackMsg(uMsgId, pMsgRsp, BSPROXY_BUF_BODY(bufRsp),
1050  ClientGetTraceState(hndClient));
1051 
1052  if( n < 0 )
1053  {
1054  BSPROXY_LOG_NMERROR(hndClient, n,
1055  "Failed to pack message body for MsgId=%u.", uMsgId);
1056  return -BS_ECODE_INTERNAL;
1057  }
1058  }
1059 
1060  //
1061  // No response message body
1062  //
1063  else
1064  {
1065  n = 0;
1066  }
1067 
1068  return ClientSendRsp(hndClient, BSPROXY_VCONN_SERVER, uTid, uMsgId,
1069  bufRsp, (size_t)n);
1070 }
1071 
1072 /*!
1073  * \brief Send module-specific response to the client.
1074  *
1075  * \param hndClient \h_botsense client handle.
1076  * \param hndVConn Virtual connection handle.
1077  * \param uTid Request-response transaction id.
1078  * \param uMsgId Response message id.
1079  * \param bufRsp Packed repsonse message body.
1080  * \param uRspSize Size of response in buffer (number of bytes).
1081  *
1082  * \copydoc doc_return_std
1083  */
1085  BsVConnHnd_T hndVConn,
1086  BsTid_T uTid,
1087  BsMsgId_T uMsgId,
1088  byte_t bufRsp[],
1089  size_t uRspSize)
1090 {
1091  BsProxyClientCtl_T *pClient;
1092  BsProxyMsgHdr_T msgHdr;
1093  size_t nBytes;
1094  int n;
1095 
1096  // message header
1097  msgHdr.m_hdrMagic = (ushort_t)BSPROXY_MSG_MAGIC;
1098  msgHdr.m_hdrTid = (byte_t)uTid;
1099  msgHdr.m_hdrVConn = (byte_t)hndVConn;
1100  msgHdr.m_hdrMsgId = (ushort_t)uMsgId;
1101  msgHdr.m_hdrBodyLen = (ushort_t)uRspSize;
1102 
1103  n = bsPackMsgHdr(&msgHdr, bufRsp, BSPROXY_MSG_HDR_LEN);
1104 
1105  if( n < 0 )
1106  {
1107  BSPROXY_LOG_ERROR(hndClient, n,
1108  "Failed to pack message header for MsgId=%u.", uMsgId);
1109  return -BS_ECODE_INTERNAL;
1110  }
1111 
1112  nBytes = (size_t)n + uRspSize;
1113 
1114  if( (pClient = ClientAcquire(hndClient)) == NULL )
1115  {
1117  "Failed to acquire Client=%d.", hndClient);
1119  }
1120 
1121  // send message to client
1122  n = (int)ClientWrite(pClient, bufRsp, nBytes);
1123 
1124  ClientRelease(hndClient);
1125 
1126  if( n < 0 )
1127  {
1128  BSPROXY_LOG_SYSERROR(hndClient,
1129  "Failed to send message, MsgId=%u", uMsgId);
1130  return -BS_ECODE_BAD_SEND;
1131  }
1132 
1133  // success
1134  else
1135  {
1136  BSPROXY_LOG_RSP(hndClient, &msgHdr);
1137  return n;
1138  }
1139 }
static void ClientUnlockBusy()
Unlock client&#39;s global busy mutual exclusion.
uint_t BsMsgId_T
client message id type [0-64k].
Definition: BotSense.h:188
int ClientSendRsp(BsProxyClientHnd_T hndClient, BsVConnHnd_T hndVConn, BsTid_T uTid, BsMsgId_T uMsgId, byte_t bufRsp[], size_t uRspSize)
Send module-specific response to the client.
static uint_t timer_elapsed(struct timeval *pTvMark)
Calculate the elapsed time between the given time mark and this call.
Definition: bsLibClient.c:358
BsProxyClientState_T m_eClientState
client state
Definition: bsProxy.h:197
<b><i>BotSense</i></b> bsProxy IP server declarations.
static void fdset_nowarn(int fd, fd_set *pset)
FD_SET() wrapper with no annoying warnings.
const NMMsgDef_T * BsProxyLookupMsgDef(BsProxyMsgId_T eMsgId)
Look up the message definition associated with the message id.
Definition: bsProxyMsgs.c:874
BotSense client application - bsProxy server-terminated core messages.
#define BS_ECODE_BAD_RECV
bad receive
Definition: BotSense.h:69
static int ClientRecvHdr(BsProxyClientCtl_T *pClient, BsProxyMsgHdr_T *pMsgHdr)
Receive client request header.
int ClientRecvReq(BsProxyClientHnd_T hndClient, BsProxyMsgHdr_T *pMsgHdr, byte_t **addrBuf)
Receive a request message from client.
BsProxyClientCtl_T * ClientNew(Socket_T *pSockClient)
Create new client control structure.
uint_t BsTid_T
client transaction id type [0-255].
Definition: BotSense.h:172
static ssize_t ClientWrite(BsProxyClientCtl_T *pClient, byte_t buf[], size_t uCount)
Write count bytes to socket.
#define BSPROXY_LOG_REQ(hndClient, pMsgHdr)
Log client request.
Definition: bsProxy.h:384
Socket_T * m_pClientSock
client opened TCP socket
Definition: bsProxy.h:196
#define BSPROXY_LOG_SYSERROR(hndClient, efmt,...)
Log Proxy Server System Error.
Definition: bsProxy.h:314
static pthread_mutex_t BsClientBusyMutex
busy mutex
Definition: bsProxyClient.c:73
#define BSPROXY_VCONN_SERVER
handle for server-terminated msgs
Definition: BotSense.h:140
#define BS_ECODE_BAD_SEND
bad send
Definition: BotSense.h:70
BsProxyClientCtl_T * ClientAcquire(BsProxyClientHnd_T hndClient)
Acquire client, locking it from other threads.
bool_t m_bBusy
client is [not] busy
Definition: bsProxy.h:194
INLINE_IN_H const char * ServerHasName()
Get the <b><i>BotSense</i></b> server&#39;s official name.
Definition: bsProxy.h:485
int ClientSendErrorRsp(BsProxyClientHnd_T hndClient, BsTid_T uTid, int nECode, const char *sErrFmt,...)
Send an error response to the client.
static ssize_t ClientResync(BsProxyClientCtl_T *pClient, byte_t bufHdr[])
Resync to client message stream.
bool_t m_bServerTrace
do [not] trace server messages
Definition: bsProxy.h:199
static void ClientFlushInput(BsProxyClientCtl_T *pClient, size_t uCount)
Flush receive stream.
void ClientDelete(BsProxyClientCtl_T *pClient)
Delete a client with the server.
void ClientSetState(BsProxyClientCtl_T *pClient, BsProxyClientState_T eNewState)
Set client&#39;s state.
int BsProxyClientHnd_T
bsProxy server client handle
Definition: bsProxy.h:114
INLINE_IN_H BsProxyClientHnd_T ServerClientSd2Hnd(int sd)
Convert the <b><i>BotSense</i></b> server client socket descriptor to client handle.
Definition: bsProxy.h:537
#define BSPROXY_LOG_RSP(hndClient, pMsgHdr)
Log client request.
Definition: bsProxy.h:404
char m_emsg[(NMFVAL_LEN_MAX_STRING)+1]
emsg
Definition: bsProxyMsgs.h:75
void ClientRelease(BsProxyClientHnd_T hndClient)
Release the locked client.
static pthread_cond_t BsClientBusyCond
busy condition
Definition: bsProxyClient.c:74
INLINE_IN_H bool_t ClientGetTraceState(BsProxyClientHnd_T hndClient)
Get the <b><i>BotSense</i></b> client&#39;s server-terminated message trace state.
Definition: bsProxy.h:602
ushort_t m_hdrBodyLen
message body length
Definition: BotSense.h:284
ushort_t m_hdrMsgId
message id (vConnection unique)
Definition: BotSense.h:283
disconnected or fatal, not deleted
Definition: bsProxy.h:186
#define BS_ECODE_MSG_TOO_BIG
message too big
Definition: BotSense.h:74
INLINE_IN_H BsProxyClientCtl_T * ServerGetClient(BsProxyClientHnd_T hndClient)
Get the <b><i>BotSense</i></b> server client.
Definition: bsProxy.h:551
int ClientSendServerRsp(BsProxyClientHnd_T hndClient, BsTid_T uTid, BsProxyMsgId_T uMsgId, void *pMsgRsp)
Send a server-terminated response message to client.
void ClientOneTimeInit()
The <b><i>BotSense</i></b> bsProxy server one-time client control initialization.
#define BS_ECODE_MSG_BAD_HDR
bad message header
Definition: BotSense.h:72
uint_t m_uRefCnt
vconn ref count for this client
Definition: bsProxy.h:198
#define BSPROXY_MSG_MAGIC
message magic pattern
Definition: BotSense.h:261
#define BSPROXY_MSG_HDR_LEN
message header length (bytes)
Definition: BotSense.h:258
ushort_t m_hdrMagic
"unique" magic pattern
Definition: BotSense.h:280
#define BSPROXY_LOG_ERROR(hndClient, ecode, efmt,...)
Log Proxy Server Error.
Definition: bsProxy.h:288
#define CLIENT_HND(pClient)
Convert pointer to client to client handle.
Definition: bsProxyClient.c:81
static ssize_t ClientRead(BsProxyClientCtl_T *pClient, byte_t buf[], size_t uCount)
Read count bytes from socket.
BsProxyMsgId_T
Definition: bsProxyMsgs.h:35
BotSense Proxy Message Header Structure.
Definition: BotSense.h:278
#define BSPROXY_LOG_NMERROR(hndClient, nmecode, efmt,...)
Log Proxy Server NetMsgs (Un)Packing Error.
Definition: bsProxy.h:302
normal operation
Definition: bsProxy.h:184
BsProxyClientState_T
Definition: bsProxy.h:181
int ClientSendOkRsp(BsProxyClientHnd_T hndClient, BsTid_T uTid)
Send an ok response to the client.
#define BSPROXY_MSG_BODY_MAX
maximum msg body length (sans header)
Definition: BotSense.h:123
#define BS_ECODE_SERVER_BAD_CLIENT
server detected bad client
Definition: BotSense.h:91
byte_t m_ecode
ecode
Definition: bsProxyMsgs.h:74
#define BS_ECODE_MSG_FRAG
message fragment
Definition: BotSense.h:73
#define BS_ECODE_INTERNAL
internal error (bug)
Definition: BotSense.h:93
#define BSPROXY_BUF_BODY(buf)
Convenience macro to produce a buffer (offset, size) 2-tuple.
Definition: BotSense.h:272
byte_t m_hdrTid
transaction id
Definition: BotSense.h:281
static void ClientLockBusy()
Lock client&#39;s global busy mutual exclusion.
Definition: bsProxyClient.c:91
static void ClientBroadcastNotBusy()
Broadcast that a client has been freed or deleted.
const char * m_sClientName
client&#39;s (remote) name
Definition: bsProxy.h:195
#define BSPROXY_MSG_MAX_LEN
total message maximum length
Definition: BotSense.h:259
static void timer_mark(struct timeval *pTvMark)
Mark the current time. Resolution is microseconds.
Definition: bsLibClient.c:340
#define BSPROXY_TUNE_T_SEND
0.5s send time out
Definition: bsProxy.h:95
#define BSPROXY_TUNE_T_RECV
1.0s receive time out
Definition: bsProxy.h:94
resyncing server with client
Definition: bsProxy.h:185
int BsProxyPackMsg(BsProxyMsgId_T eMsgId, void *pStruct, byte_t buf[], size_t bufSize, bool_t bTrace)
Pack a ITV message in big-endian byte order.
Definition: bsProxyMsgs.c:924
byte_t m_hdrVConn
virtual connection handle (server unique)
Definition: BotSense.h:282
static bool_t ClientBusyTryLock()
Try to lock client&#39;s global busy mutual exclusion.
static void ClientWaitNotBusy()
Wait on a client to become free.
static int ClientRecvBody(BsProxyClientCtl_T *pClient, byte_t buf[], size_t uBodyLen)
Receive client request message body.
<b><i>BotSense</i></b> package top-level, unifying header declarations.
int BsVConnHnd_T
virtual connection handle type
Definition: BotSense.h:151
int ClientSendVErrorRsp(BsProxyClientHnd_T hndClient, BsTid_T uTid, int nECode, const char *sErrFmt, va_list ap)
Send va_list error response to the client.
INLINE_IN_H const char * ClientThisHasName(BsProxyClientCtl_T *pClient)
Get this <b><i>BotSense</i></b> client official name.
Definition: bsProxy.h:574
#define BSPROXY_RSPERR_EMSG_LEN
Definition: bsProxyMsgs.h:67