1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2020 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22 */
23 /*
24  * Network Utility Routines
25  *
26  * by Kern Sibbald
27  *
28  * Adapted and enhanced for BAREOS, originally written
29  * for inclusion in the Apcupsd package
30  */
31 /**
32  * @file
33  * Network Utility Routines
34  */
35 
36 #include "include/bareos.h"
37 #include "jcr.h"
38 #include "lib/berrno.h"
39 #include "lib/bnet.h"
40 #include "lib/bsock.h"
41 #include "lib/bsys.h"
42 #include "lib/ascii_control_characters.h"
43 #include "lib/bstringlist.h"
44 
45 #include <netdb.h>
46 #include "lib/tls.h"
47 
48 #ifndef INADDR_NONE
49 #  define INADDR_NONE -1
50 #endif
51 
52 #ifndef HAVE_GETADDRINFO
53 static pthread_mutex_t ip_mutex = PTHREAD_MUTEX_INITIALIZER;
54 #endif
55 
56 /**
57  * Receive a message from the other end. Each message consists of
58  * two packets. The first is a header that contains the size
59  * of the data that follows in the second packet.
60  * Returns number of bytes read (may return zero)
61  * Returns -1 on signal (BNET_SIGNAL)
62  * Returns -2 on hard end of file (BNET_HARDEOF)
63  * Returns -3 on error  (BNET_ERROR)
64  *
65  *  Unfortunately, it is a bit complicated because we have these
66  *    four return types:
67  *    1. Normal data
68  *    2. Signal including end of data stream
69  *    3. Hard end of file
70  *    4. Error
71  *  Using IsBnetStop() and IsBnetError() you can figure this all out.
72  */
BnetRecv(BareosSocket * bsock)73 int32_t BnetRecv(BareosSocket* bsock) { return bsock->recv(); }
74 
75 
76 /**
77  * Return 1 if there are errors on this bsock or it is closed,
78  *   i.e. stop communicating on this line.
79  */
IsBnetStop(BareosSocket * bsock)80 bool IsBnetStop(BareosSocket* bsock) { return bsock->IsStop(); }
81 
82 /**
83  * Return number of errors on socket
84  */
IsBnetError(BareosSocket * bsock)85 int IsBnetError(BareosSocket* bsock) { return bsock->IsError(); }
86 
87 /**
88  * Call here after error during closing to suppress error
89  *  messages which are due to the other end shutting down too.
90  */
BnetSuppressErrorMessages(BareosSocket * bsock,bool flag)91 void BnetSuppressErrorMessages(BareosSocket* bsock, bool flag)
92 {
93   bsock->suppress_error_msgs_ = flag;
94 }
95 
96 /**
97  * Send a message over the network. The send consists of
98  * two network packets. The first is sends a 32 bit integer containing
99  * the length of the data packet which follows.
100  *
101  * Returns: false on failure
102  *          true  on success
103  */
BnetSend(BareosSocket * bsock)104 bool BnetSend(BareosSocket* bsock) { return bsock->send(); }
105 
106 
107 /**
108  * Establish a TLS connection -- server side
109  *  Returns: true  on success
110  *           false on failure
111  */
112 #ifdef HAVE_TLS
BnetTlsServer(BareosSocket * bsock,const std::vector<std::string> & verify_list)113 bool BnetTlsServer(BareosSocket* bsock,
114                    const std::vector<std::string>& verify_list)
115 {
116   JobControlRecord* jcr = bsock->jcr();
117 
118   if (!bsock->tls_conn_init) {
119     Dmsg0(100, "No TLS Connection: Cannot call TlsBsockAccept\n");
120     goto err;
121   }
122 
123   if (!bsock->tls_conn_init->TlsBsockAccept(bsock)) {
124     Qmsg0(bsock->jcr(), M_FATAL, 0, _("TLS Negotiation failed.\n"));
125     goto err;
126   }
127 
128   if (!verify_list.empty()) {
129     if (!bsock->tls_conn_init->TlsPostconnectVerifyCn(jcr, verify_list)) {
130       Qmsg1(bsock->jcr(), M_FATAL, 0,
131             _("TLS certificate verification failed."
132               " Peer certificate did not match a required commonName\n"),
133             bsock->host());
134       goto err;
135     }
136   }
137 
138   bsock->LockMutex();
139   bsock->tls_conn = std::move(bsock->tls_conn_init);
140   bsock->UnlockMutex();
141 
142   Dmsg0(50, "TLS server negotiation established.\n");
143   return true;
144 
145 err:
146   bsock->CloseTlsConnectionAndFreeMemory();
147   return false;
148 }
149 
150 /**
151  * Establish a TLS connection -- client side
152  * Returns: true  on success
153  *          false on failure
154  */
BnetTlsClient(BareosSocket * bsock,bool VerifyPeer,const std::vector<std::string> & verify_list)155 bool BnetTlsClient(BareosSocket* bsock,
156                    bool VerifyPeer,
157                    const std::vector<std::string>& verify_list)
158 {
159   JobControlRecord* jcr = bsock->jcr();
160 
161   if (!bsock->tls_conn_init) {
162     Dmsg0(100, "No TLS Connection: Cannot call TlsBsockConnect\n");
163     goto err;
164   }
165 
166   if (!bsock->tls_conn_init->TlsBsockConnect(bsock)) { goto err; }
167 
168   if (VerifyPeer) {
169     /*
170      * If there's an Allowed CN verify list, use that to validate the remote
171      * certificate's CN. Otherwise, we use standard host/CN matching.
172      */
173     if (!verify_list.empty()) {
174       if (!bsock->tls_conn_init->TlsPostconnectVerifyCn(jcr, verify_list)) {
175         Qmsg1(bsock->jcr(), M_FATAL, 0,
176               _("TLS certificate verification failed."
177                 " Peer certificate did not match a required commonName\n"),
178               bsock->host());
179         goto err;
180       }
181     } else {
182       if (!bsock->tls_conn_init->TlsPostconnectVerifyHost(jcr, bsock->host())) {
183         Qmsg1(bsock->jcr(), M_FATAL, 0,
184               _("TLS host certificate verification failed. Host name \"%s\" "
185                 "did not match presented certificate\n"),
186               bsock->host());
187         goto err;
188       }
189     }
190   }
191 
192   bsock->LockMutex();
193   bsock->tls_conn = std::move(bsock->tls_conn_init);
194   bsock->UnlockMutex();
195 
196   Dmsg0(50, "TLS client negotiation established.\n");
197   return true;
198 
199 err:
200   bsock->CloseTlsConnectionAndFreeMemory();
201   return false;
202 }
203 #else
BnetTlsServer(BareosSocket * bsock,const std::vector<std::string> & verify_list)204 bool BnetTlsServer(/* std::shared_ptr<TlsImplementation> tls_implementation, */
205                    BareosSocket* bsock,
206                    const std::vector<std::string>& verify_list)
207 {
208   Jmsg(bsock->jcr(), M_ABORT, 0, _("TLS enabled but not configured.\n"));
209   return false;
210 }
211 
BnetTlsClient(BareosSocket * bsock,bool VerifyPeer,const std::vector<std::string> & verify_list)212 bool BnetTlsClient(/* std::shared_ptr<TLS_IMPLEMENTATION> tls_implementation, */
213                    BareosSocket* bsock,
214                    bool VerifyPeer,
215                    const std::vector<std::string>& verify_list)
216 {
217   Jmsg(bsock->jcr(), M_ABORT, 0, _("TLS enabled but not configured.\n"));
218   return false;
219 }
220 #endif /* HAVE_TLS */
221 
222 /**
223  * Wait for a specified time for data to appear on
224  * the BareosSocket connection.
225  *
226  *   Returns: 1 if data available
227  *            0 if timeout
228  *           -1 if error
229  */
BnetWaitData(BareosSocket * bsock,int sec)230 int BnetWaitData(BareosSocket* bsock, int sec) { return bsock->WaitData(sec); }
231 
232 /**
233  * As above, but returns on interrupt
234  */
BnetWaitDataIntr(BareosSocket * bsock,int sec)235 int BnetWaitDataIntr(BareosSocket* bsock, int sec)
236 {
237   return bsock->WaitDataIntr(sec);
238 }
239 
240 #ifndef NETDB_INTERNAL
241 #  define NETDB_INTERNAL -1 /* See errno. */
242 #endif
243 #ifndef NETDB_SUCCESS
244 #  define NETDB_SUCCESS 0 /* No problem. */
245 #endif
246 #ifndef HOST_NOT_FOUND
247 #  define HOST_NOT_FOUND 1 /* Authoritative Answer Host not found. */
248 #endif
249 #ifndef TRY_AGAIN
250 #  define TRY_AGAIN 2 /* Non-Authoritative Host not found, or SERVERFAIL. */
251 #endif
252 #ifndef NO_RECOVERY
253 #  define NO_RECOVERY 3 /* unrecoverable errors, FORMERR, REFUSED, NOTIMP. */
254 #endif
255 #ifndef NO_DATA
256 #  define NO_DATA 4 /* Valid name, no data record of requested type. */
257 #endif
258 
259 #if HAVE_GETADDRINFO
resolv_host(int family,const char * host,dlist * addr_list)260 const char* resolv_host(int family, const char* host, dlist* addr_list)
261 {
262   int res;
263   struct addrinfo hints;
264   struct addrinfo *ai, *rp;
265   IPADDR* addr;
266 
267   memset(&hints, 0, sizeof(struct addrinfo));
268   hints.ai_family = family;
269   hints.ai_socktype = SOCK_STREAM;
270   hints.ai_protocol = IPPROTO_TCP;
271   hints.ai_flags = 0;
272 
273   res = getaddrinfo(host, NULL, &hints, &ai);
274   if (res != 0) { return gai_strerror(res); }
275 
276   for (rp = ai; rp != NULL; rp = rp->ai_next) {
277     switch (rp->ai_addr->sa_family) {
278       case AF_INET:
279         addr = new IPADDR(rp->ai_addr->sa_family);
280         addr->SetType(IPADDR::R_MULTIPLE);
281         /*
282          * Some serious casting to get the struct in_addr *
283          * rp->ai_addr == struct sockaddr
284          * as this is AF_INET family we can cast that
285          * to struct_sockaddr_in. Of that we need the
286          * address of the sin_addr member which contains a
287          * struct in_addr
288          */
289         addr->SetAddr4(&(((struct sockaddr_in*)rp->ai_addr)->sin_addr));
290         break;
291 #  ifdef HAVE_IPV6
292       case AF_INET6:
293         addr = new IPADDR(rp->ai_addr->sa_family);
294         addr->SetType(IPADDR::R_MULTIPLE);
295         /*
296          * Some serious casting to get the struct in6_addr *
297          * rp->ai_addr == struct sockaddr
298          * as this is AF_INET6 family we can cast that
299          * to struct_sockaddr_in6. Of that we need the
300          * address of the sin6_addr member which contains a
301          * struct in6_addr
302          */
303         addr->SetAddr6(&(((struct sockaddr_in6*)rp->ai_addr)->sin6_addr));
304         break;
305 #  endif
306       default:
307         continue;
308     }
309     addr_list->append(addr);
310   }
311   freeaddrinfo(ai);
312   return NULL;
313 }
314 #else
315 /**
316  * Get human readable error for gethostbyname()
317  */
gethost_strerror()318 static const char* gethost_strerror()
319 {
320   const char* msg;
321   BErrNo be;
322   switch (h_errno) {
323     case NETDB_INTERNAL:
324       msg = be.bstrerror();
325       break;
326     case NETDB_SUCCESS:
327       msg = _("No problem.");
328       break;
329     case HOST_NOT_FOUND:
330       msg = _("Authoritative answer for host not found.");
331       break;
332     case TRY_AGAIN:
333       msg = _("Non-authoritative for host not found, or ServerFail.");
334       break;
335     case NO_RECOVERY:
336       msg = _("Non-recoverable errors, FORMERR, REFUSED, or NOTIMP.");
337       break;
338     case NO_DATA:
339       msg = _("Valid name, no data record of resquested type.");
340       break;
341     default:
342       msg = _("Unknown error.");
343   }
344   return msg;
345 }
346 
resolv_host(int family,const char * host,dlist * addr_list)347 static const char* resolv_host(int family, const char* host, dlist* addr_list)
348 {
349   struct hostent* hp;
350   const char* errmsg;
351   char** p;
352   IPADDR* addr;
353 
354   P(ip_mutex); /* gethostbyname() is not thread safe */
355 #  ifdef HAVE_GETHOSTBYNAME2
356   if ((hp = gethostbyname2(host, family)) == NULL) {
357 #  else
358   if ((hp = gethostbyname(host)) == NULL) {
359 #  endif
360     /* may be the strerror give not the right result -:( */
361     errmsg = gethost_strerror();
362     V(ip_mutex);
363     return errmsg;
364   } else {
365     for (p = hp->h_addr_list; *p != 0; p++) {
366       switch (hp->h_addrtype) {
367         case AF_INET:
368           addr = new IPADDR(hp->h_addrtype);
369           addr->SetType(IPADDR::R_MULTIPLE);
370           addr->SetAddr4((struct in_addr*)*p);
371           break;
372 #  ifdef HAVE_IPV6
373         case AF_INET6:
374           addr = new IPADDR(hp->h_addrtype);
375           addr->SetType(IPADDR::R_MULTIPLE);
376           addr->SetAddr6((struct in6_addr*)*p);
377           break;
378 #  endif
379         default:
380           continue;
381       }
382       addr_list->append(addr);
383     }
384     V(ip_mutex);
385   }
386   return NULL;
387 }
388 #endif
389 
390 static IPADDR* add_any(int family)
391 {
392   IPADDR* addr = new IPADDR(family);
393   addr->SetType(IPADDR::R_MULTIPLE);
394   addr->SetAddrAny();
395   return addr;
396 }
397 
398 /**
399  * i host = 0 mean INADDR_ANY only ipv4
400  */
401 dlist* BnetHost2IpAddrs(const char* host, int family, const char** errstr)
402 {
403   struct in_addr inaddr;
404   IPADDR* addr = 0;
405   const char* errmsg;
406 #ifdef HAVE_IPV6
407   struct in6_addr inaddr6;
408 #endif
409 
410   dlist* addr_list = new dlist(addr, &addr->link);
411   if (!host || host[0] == '\0') {
412     if (family != 0) {
413       addr_list->append(add_any(family));
414     } else {
415       addr_list->append(add_any(AF_INET));
416 #ifdef HAVE_IPV6
417       addr_list->append(add_any(AF_INET6));
418 #endif
419     }
420   } else if (inet_aton(host, &inaddr)) { /* MA Bug 4 */
421     addr = new IPADDR(AF_INET);
422     addr->SetType(IPADDR::R_MULTIPLE);
423     addr->SetAddr4(&inaddr);
424     addr_list->append(addr);
425 #ifdef HAVE_IPV6
426 #  ifndef HAVE_WIN32
427   } else if (inet_pton(AF_INET6, host, &inaddr6) == 1) {
428 #  else
429   } else if (p_InetPton && p_InetPton(AF_INET6, host, &inaddr6) == 1) {
430 #  endif
431     addr = new IPADDR(AF_INET6);
432     addr->SetType(IPADDR::R_MULTIPLE);
433     addr->SetAddr6(&inaddr6);
434     addr_list->append(addr);
435 #endif
436   } else {
437     if (family != 0) {
438       errmsg = resolv_host(family, host, addr_list);
439       if (errmsg) {
440         *errstr = errmsg;
441         FreeAddresses(addr_list);
442         return 0;
443       }
444     } else {
445 #ifdef HAVE_IPV6
446       /* We try to resolv host for ipv6 and ipv4, the connection procedure
447        * will try to reach the host for each protocols. We report only "Host
448        * not found" ipv4 message (no need to have ipv6 and ipv4 messages).
449        */
450       resolv_host(AF_INET6, host, addr_list);
451 #endif
452       errmsg = resolv_host(AF_INET, host, addr_list);
453 
454       if (addr_list->size() == 0) {
455         *errstr = errmsg;
456         FreeAddresses(addr_list);
457         return 0;
458       }
459     }
460   }
461   return addr_list;
462 }
463 
464 /**
465  * Return the string for the error that occurred
466  * on the socket. Only the first error is retained.
467  */
468 const char* BnetStrerror(BareosSocket* bsock) { return bsock->bstrerror(); }
469 
470 /**
471  * Format and send a message
472  *  Returns: false on error
473  *           true  on success
474  */
475 bool BnetFsend(BareosSocket* bs, const char* fmt, ...)
476 {
477   va_list arg_ptr;
478   int maxlen;
479 
480   if (bs->errors || bs->IsTerminated()) { return false; }
481   /* This probably won't work, but we vsnprintf, then if we
482    * get a negative length or a length greater than our buffer
483    * (depending on which library is used), the printf was truncated, so
484    * get a bigger buffer and try again.
485    */
486   for (;;) {
487     maxlen = SizeofPoolMemory(bs->msg) - 1;
488     va_start(arg_ptr, fmt);
489     bs->message_length = Bvsnprintf(bs->msg, maxlen, fmt, arg_ptr);
490     va_end(arg_ptr);
491     if (bs->message_length > 0 && bs->message_length < (maxlen - 5)) { break; }
492     bs->msg = ReallocPoolMemory(bs->msg, maxlen + maxlen / 2);
493   }
494   return bs->send();
495 }
496 
497 int BnetGetPeer(BareosSocket* bs, char* buf, socklen_t buflen)
498 {
499   return bs->GetPeer(buf, buflen);
500 }
501 
502 /**
503  * Set the network buffer size, suggested size is in size.
504  *  Actual size obtained is returned in bs->message_length
505  *
506  *  Returns: 0 on failure
507  *           1 on success
508  */
509 bool BnetSetBufferSize(BareosSocket* bs, uint32_t size, int rw)
510 {
511   return bs->SetBufferSize(size, rw);
512 }
513 
514 /**
515  * Set socket non-blocking
516  * Returns previous socket flag
517  */
518 int BnetSetNonblocking(BareosSocket* bsock) { return bsock->SetNonblocking(); }
519 
520 /**
521  * Set socket blocking
522  * Returns previous socket flags
523  */
524 int BnetSetBlocking(BareosSocket* bsock) { return bsock->SetBlocking(); }
525 
526 /**
527  * Restores socket flags
528  */
529 void BnetRestoreBlocking(BareosSocket* bsock, int flags)
530 {
531   bsock->RestoreBlocking(flags);
532 }
533 
534 bool BnetSig(BareosSocket* bs, int signal) { return bs->signal(signal); }
535 
536 /**
537  * Convert a network "signal" code into
538  * human readable ASCII.
539  */
540 
541 /* clang-format off */
542 std::map<int, std::pair<std::string, std::string>> bnet_signal_to_text {
543     {BNET_EOD, {"BNET_EOD", "End of data stream, new data may follow"}},
544     {BNET_EOD_POLL, {"BNET_EOD_POLL", "End of data and poll all in one "}},
545     {BNET_STATUS, {"BNET_STATUS", "Send full status"}},
546     {BNET_TERMINATE, {"BNET_TERMINATE", "Conversation terminated, doing close() "}},
547     {BNET_POLL, {"BNET_POLL", "Poll request, I'm hanging on a read "}},
548     {BNET_HEARTBEAT, {"BNET_HEARTBEAT", "Heartbeat Response requested "}},
549     {BNET_HB_RESPONSE, {"BNET_HB_RESPONSE", "Only response permited to HB "}},
550     {BNET_xxxxxxPROMPT, {"BNET_xxxxxxPROMPT", "No longer used -- Prompt for subcommand "}},
551     {BNET_BTIME, {"BNET_BTIME", "Send UTC btime "}},
552     {BNET_BREAK, {"BNET_BREAK", "Stop current command -- ctl-c "}},
553     {BNET_START_SELECT, {"BNET_START_SELECT", "Start of a selection list "}},
554     {BNET_END_SELECT, {"BNET_END_SELECT", "End of a select list "}},
555     {BNET_INVALID_CMD, {"BNET_INVALID_CMD", "Invalid command sent "}},
556     {BNET_CMD_FAILED, {"BNET_CMD_FAILED", "Command failed "}},
557     {BNET_CMD_OK, {"BNET_CMD_OK", "Command succeeded "}},
558     {BNET_CMD_BEGIN, {"BNET_CMD_BEGIN", "Start command execution "}},
559     {BNET_MSGS_PENDING, {"BNET_MSGS_PENDING", "Messages pending "}},
560     {BNET_MAIN_PROMPT, {"BNET_MAIN_PROMPT", "Server ready and waiting "}},
561     {BNET_SELECT_INPUT, {"BNET_SELECT_INPUT", "Return selection input "}},
562     {BNET_WARNING_MSG, {"BNET_WARNING_MSG", "Warning message "}},
563     {BNET_ERROR_MSG, {"BNET_ERROR_MSG", "Error message -- command failed "}},
564     {BNET_INFO_MSG, {"BNET_INFO_MSG", "Info message -- status line "}},
565     {BNET_RUN_CMD, {"BNET_RUN_CMD", "Run command follows "}},
566     {BNET_YESNO, {"BNET_YESNO", "Request yes no response "}},
567     {BNET_START_RTREE, {"BNET_START_RTREE", "Start restore tree mode "}},
568     {BNET_END_RTREE, {"BNET_END_RTREE", "End restore tree mode "}},
569     {BNET_SUB_PROMPT, {"BNET_SUB_PROMPT", "Indicate we are at a subprompt "}},
570     {BNET_TEXT_INPUT, {"BNET_TEXT_INPUT", "Get text input from user "}},
571 };
572 /* clang-format on */
573 
574 std::string BnetSignalToString(int signal)
575 {
576   if (bnet_signal_to_text.find(signal) != bnet_signal_to_text.end()) {
577     return bnet_signal_to_text[signal].first;
578   }
579   return "Unknown sig " + std::to_string(signal);
580 }
581 
582 std::string BnetSignalToString(const BareosSocket* bs)
583 {
584   return BnetSignalToString(bs->message_length);
585 }
586 
587 std::string BnetSignalToDescription(int signal)
588 {
589   if (bnet_signal_to_text.find(signal) != bnet_signal_to_text.end()) {
590     return bnet_signal_to_text[signal].second;
591   }
592   return "Unknown sig " + std::to_string(signal);
593 }
594 
595 bool ReadoutCommandIdFromMessage(const BStringList& list_of_arguments,
596                                  uint32_t& id_out)
597 {
598   if (list_of_arguments.size() < 1) { return false; }
599 
600   uint32_t id = kMessageIdUnknown;
601 
602   try { /* "1000 OK: <director name> ..." */
603     const std::string& first_argument = list_of_arguments.front();
604     id = std::stoul(first_argument);
605   } catch (const std::exception& e) {
606     id_out = kMessageIdProtokollError;
607     return false;
608   }
609 
610   id_out = id;
611   return true;
612 }
613 
614 bool EvaluateResponseMessageId(const std::string& message,
615                                uint32_t& id_out,
616                                BStringList& args_out)
617 {
618   BStringList list_of_arguments(message,
619                                 AsciiControlCharacters::RecordSeparator());
620   uint32_t id = kMessageIdUnknown;
621 
622   bool ok = ReadoutCommandIdFromMessage(list_of_arguments, id);
623 
624   if (ok) { id_out = id; }
625   args_out = list_of_arguments;
626 
627   return ok;
628 }
629 
630 bool BareosSocket::ReceiveAndEvaluateResponseMessage(uint32_t& id_out,
631                                                      BStringList& args_out)
632 {
633   StartTimer(30);  // 30 seconds
634   int ret = recv();
635   StopTimer();
636 
637   if (ret <= 0) {
638     Dmsg1(100, "Error while receiving response message: %s", msg);
639     return false;
640   }
641 
642   std::string message(msg);
643 
644   if (message.empty()) {
645     Dmsg0(100, "Received message is empty\n");
646     return false;
647   }
648 
649   return EvaluateResponseMessageId(message, id_out, args_out);
650 }
651 
652 bool BareosSocket::FormatAndSendResponseMessage(
653     uint32_t id,
654     const BStringList& list_of_arguments)
655 {
656   std::string m = std::to_string(id);
657   m += AsciiControlCharacters::RecordSeparator();
658   m += list_of_arguments.Join(AsciiControlCharacters::RecordSeparator());
659 
660   StartTimer(30);  // 30 seconds
661   if (send(m.c_str(), m.size()) <= 0) {
662     Dmsg1(100, "Could not send response message: %d\n", m.c_str());
663     StopTimer();
664     return false;
665   }
666   StopTimer();
667   return true;
668 }
669 
670 bool BareosSocket::FormatAndSendResponseMessage(uint32_t id,
671                                                 const std::string& str)
672 {
673   BStringList message;
674   message << str;
675 
676   return FormatAndSendResponseMessage(id, message);
677 }
678