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-2016 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   * Originally written by Kern Sibbald for inclusion in apcupsd,
25   * but heavily modified for BAREOS
26   */
27  /*
28   * @file
29   * tcp server code
30   */
31 
32 #include "include/bareos.h"
33 #include "lib/bnet_server_tcp.h"
34 #include "lib/bsys.h"
35 
36 #include <netinet/in.h>
37 #include <sys/socket.h>
38 #include <stdlib.h>
39 #include <arpa/inet.h>
40 #include <netdb.h>
41 #ifdef HAVE_ARPA_NAMESER_H
42 #include <arpa/nameser.h>
43 #endif
44 #ifdef HAVE_RESOLV_H
45 //#include <resolv.h>
46 #endif
47 
48 #ifdef HAVE_POLL_H
49 #include <poll.h>
50 #elif HAVE_SYS_POLL_H
51 #include <sys/poll.h>
52 #endif
53 #include <atomic>
54 
55 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
56 
57 #ifdef HAVE_LIBWRAP
58 #include "tcpd.h"
59 int allow_severity = LOG_NOTICE;
60 int deny_severity = LOG_WARNING;
61 #endif
62 
63 static bool quit = false;
64 
65 struct s_sockfd {
66    int fd;
67    int port;
68 };
69 
70 /**
71  * Stop the Threaded Network Server if its realy running in a separate thread.
72  * e.g. set the quit flag and wait for the other thread to exit cleanly.
73  */
BnetStopAndWaitForThreadServerTcp(pthread_t tid)74 void BnetStopAndWaitForThreadServerTcp(pthread_t tid)
75 {
76    Dmsg0(100, "BnetThreadServer: Request Stop\n");
77    quit = true;
78    if (!pthread_equal(tid, pthread_self())) {
79       pthread_kill(tid, TIMEOUT_SIGNAL);
80       Dmsg0(100, "BnetThreadServer: Wait until finished\n");
81       pthread_join(tid, nullptr);
82       Dmsg0(100, "BnetThreadServer: finished\n");
83    }
84 }
85 
86 /**
87  * Perform a cleanup for the Threaded Network Server check if there is still
88  * something to do or that the cleanup already took place.
89  */
CleanupBnetThreadServerTcp(alist * sockfds,workq_t * client_wq)90 static void CleanupBnetThreadServerTcp(alist *sockfds, workq_t *client_wq)
91 {
92    Dmsg0(100, "CleanupBnetThreadServerTcp: start\n");
93 
94    if (sockfds && !sockfds->empty()) {
95       s_sockfd *fd_ptr = (s_sockfd *)sockfds->first();
96       while (fd_ptr) {
97          close(fd_ptr->fd);
98          fd_ptr = (s_sockfd *)sockfds->next();
99       }
100       sockfds->destroy();
101    }
102 
103    if (client_wq) {
104       int status = WorkqDestroy(client_wq);
105       if (status != 0) {
106           BErrNo be;
107           be.SetErrno(status);
108           Emsg1(M_ERROR, 0, _("Could not destroy client queue: ERR=%s\n"),
109                 be.bstrerror());
110       }
111    }
112    Dmsg0(100, "CleanupBnetThreadServerTcp: finish\n");
113 }
114 
115 class BNetThreadServerCleanupObject
116 {
117 public:
BNetThreadServerCleanupObject(alist * sockfds,workq_t * client_wq)118    BNetThreadServerCleanupObject(alist *sockfds,
119                      workq_t *client_wq)
120      : sockfds_(sockfds)
121      , client_wq_(client_wq)
122    {}
123 
~BNetThreadServerCleanupObject()124    ~BNetThreadServerCleanupObject() {
125      CleanupBnetThreadServerTcp(sockfds_, client_wq_);
126    }
127 private:
128    alist *sockfds_;
129    workq_t *client_wq_;
130 };
131 
132 /**
133  * Become Threaded Network Server
134  *
135  * This function is able to handle multiple server ips in
136  * ipv4 and ipv6 style. The Addresse are give in a comma
137  * separated string in bind_addr
138  *
139  * At the moment it is impossible to bind to different ports.
140  */
BnetThreadServerTcp(dlist * addr_list,int max_clients,alist * sockfds,workq_t * client_wq,bool nokeepalive,void * HandleConnectionRequest (ConfigurationParser * config,void * bsock),ConfigurationParser * config,std::atomic<BnetServerState> * const server_state)141 void BnetThreadServerTcp(dlist *addr_list,
142                             int max_clients,
143                             alist *sockfds,
144                             workq_t *client_wq,
145                             bool nokeepalive,
146                             void *HandleConnectionRequest(ConfigurationParser *config,
147                                                         void *bsock),
148                             ConfigurationParser *config,
149                             std::atomic<BnetServerState> *const server_state)
150 {
151    int newsockfd, status;
152    socklen_t clilen;
153    struct sockaddr cli_addr;       /* client's address */
154    int tlog;
155    int value;
156 #ifdef HAVE_LIBWRAP
157    struct request_info request;
158 #endif
159    IPADDR *ipaddr, *next, *to_free;
160    s_sockfd *fd_ptr = NULL;
161    char buf[128];
162 #ifdef HAVE_POLL
163    nfds_t nfds;
164    int events;
165    struct pollfd *pfds;
166 #endif
167 
168    char allbuf[256 * 10];
169 
170    BNetThreadServerCleanupObject cleanup_object(sockfds, client_wq);
171 
172    quit = false;
173    if (server_state) { server_state->store(BnetServerState::kStarting); }
174 
175    /*
176     * Remove any duplicate addresses.
177     */
178    for (ipaddr = (IPADDR *)addr_list->first();
179         ipaddr;
180         ipaddr = (IPADDR *)addr_list->next(ipaddr)) {
181       next = (IPADDR *)addr_list->next(ipaddr);
182       while (next) {
183          /*
184           * See if the addresses match.
185           */
186          if (ipaddr->GetSockaddrLen() == next->GetSockaddrLen() &&
187              memcmp(ipaddr->get_sockaddr(), next->get_sockaddr(),
188                     ipaddr->GetSockaddrLen()) == 0) {
189             to_free = next;
190             next = (IPADDR *)addr_list->next(next);
191             addr_list->remove(to_free);
192             delete to_free;
193          } else {
194             next = (IPADDR *)addr_list->next(next);
195          }
196       }
197    }
198 
199    Dmsg1(100, "Addresses %s\n", BuildAddressesString(addr_list, allbuf, sizeof(allbuf)));
200 
201    if (nokeepalive) {
202       value = 0;
203    } else {
204       value = 1;
205    }
206 
207 #ifdef HAVE_POLL
208    nfds = 0;
209 #endif
210    foreach_dlist(ipaddr, addr_list) {
211       /*
212        * Allocate on stack from -- no need to free
213        */
214       fd_ptr = (s_sockfd *)alloca(sizeof(s_sockfd));
215       fd_ptr->port = ipaddr->GetPortNetOrder();
216 
217       /*
218        * Open a TCP socket
219        */
220       for (tlog= 60; (fd_ptr->fd=socket(ipaddr->GetFamily(), SOCK_STREAM, 0)) < 0; tlog -= 10) {
221          if (tlog <= 0) {
222             BErrNo be;
223             char curbuf[256];
224             Emsg3(M_ABORT, 0, _("Cannot open stream socket. ERR=%s. Current %s All %s\n"),
225                        be.bstrerror(),
226                        ipaddr->build_address_str(curbuf, sizeof(curbuf)),
227                        BuildAddressesString(addr_list, allbuf, sizeof(allbuf)));
228          }
229          Bmicrosleep(10, 0);
230       }
231 
232       if (setsockopt(fd_ptr->fd, SOL_SOCKET, SO_REUSEADDR, (sockopt_val_t)&value, sizeof(value)) < 0) {
233          BErrNo be;
234          Emsg1(M_WARNING, 0, _("Cannot set SO_REUSEADDR on socket: %s\n"),
235                be.bstrerror());
236       }
237 
238       int tries = 3;
239       int wait_seconds = 5;
240       bool ok = false;
241 
242       do {
243          int ret = bind(fd_ptr->fd, ipaddr->get_sockaddr(), ipaddr->GetSockaddrLen());
244          if (ret < 0) {
245            BErrNo be;
246            Emsg2(M_WARNING, 0, _("Cannot bind port %d: ERR=%s: Retrying ...\n"),
247                  ntohs(fd_ptr->port), be.bstrerror());
248            Bmicrosleep(wait_seconds, 0);
249          } else {
250            ok = true;
251          }
252       } while (!ok && --tries);
253 
254       if (!ok) {
255          BErrNo be;
256          Emsg2(M_ERROR, 0, _("Cannot bind port %d: ERR=%s.\n"), ntohs(fd_ptr->port),
257                be.bstrerror());
258          if (server_state) { server_state->store(BnetServerState::kError); }
259          return;
260       }
261 
262       listen(fd_ptr->fd, 50);      /* tell system we are ready */
263       sockfds->append(fd_ptr);
264 
265 #ifdef HAVE_POLL
266       nfds++;
267 #endif
268    }
269 
270    /*
271     * Start work queue thread
272     */
273    if ((status = WorkqInit(client_wq, max_clients, HandleConnectionRequest)) != 0) {
274       BErrNo be;
275       be.SetErrno(status);
276       Emsg1(M_ABORT, 0, _("Could not init client queue: ERR=%s\n"), be.bstrerror());
277    }
278 
279 #ifdef HAVE_POLL
280    /*
281     * Allocate on stack from -- no need to free
282     */
283    pfds = (struct pollfd *)alloca(sizeof(struct pollfd) * nfds);
284    memset(pfds, 0, sizeof(struct pollfd) * nfds);
285 
286    nfds = 0;
287    events = POLLIN;
288 #if defined(POLLRDNORM)
289    events |= POLLRDNORM;
290 #endif
291 #if defined(POLLRDBAND)
292    events |= POLLRDBAND;
293 #endif
294 #if defined(POLLPRI)
295    events |= POLLPRI;
296 #endif
297 
298    foreach_alist(fd_ptr, sockfds) {
299       pfds[nfds].fd = fd_ptr->fd;
300       pfds[nfds].events = events;
301       nfds++;
302    }
303 #endif
304 
305    if (server_state) { server_state->store(BnetServerState::kStarted); }
306 
307    while (!quit) {
308 #ifndef HAVE_POLL
309       unsigned int maxfd = 0;
310       fd_set sockset;
311       FD_ZERO(&sockset);
312 
313       foreach_alist(fd_ptr, sockfds) {
314          FD_SET((unsigned)fd_ptr->fd, &sockset);
315          if ((unsigned)fd_ptr->fd > maxfd) {
316             maxfd = fd_ptr->fd;
317          }
318       }
319 
320       errno = 0;
321       if ((status = select(maxfd + 1, &sockset, NULL, NULL, NULL)) < 0) {
322          BErrNo be;                   /* capture errno */
323          if (errno == EINTR) {
324             continue;
325          }
326          if(server_state) { server_state->store(BnetServerState::kError); }
327          Emsg1(M_FATAL, 0, _("Error in select: %s\n"), be.bstrerror());
328          break;
329       }
330 
331       foreach_alist(fd_ptr, sockfds) {
332          if (FD_ISSET(fd_ptr->fd, &sockset)) {
333 #else
334       int cnt;
335 
336       errno = 0;
337       if ((status = poll(pfds, nfds, -1)) < 0) {
338          BErrNo be;                   /* capture errno */
339          if (errno == EINTR) {
340             continue;
341          }
342          Emsg1(M_FATAL, 0, _("Error in poll: %s\n"), be.bstrerror());
343 
344          break;
345       }
346 
347       cnt = 0;
348       foreach_alist(fd_ptr, sockfds) {
349          if (pfds[cnt++].revents & events) {
350 #endif
351             /*
352              * Got a connection, now accept it.
353              */
354             do {
355                clilen = sizeof(cli_addr);
356                newsockfd = accept(fd_ptr->fd, &cli_addr, &clilen);
357             } while (newsockfd < 0 && errno == EINTR);
358             if (newsockfd < 0) {
359                continue;
360             }
361 #ifdef HAVE_LIBWRAP
362             P(mutex);              /* HostsAccess is not thread safe */
363             request_init(&request, RQ_DAEMON, my_name, RQ_FILE, newsockfd, 0);
364             fromhost(&request);
365             if (!HostsAccess(&request)) {
366                V(mutex);
367                Jmsg2(NULL, M_SECURITY, 0,
368                      _("Connection from %s:%d refused by hosts.access\n"),
369                      SockaddrToAscii(&cli_addr, buf, sizeof(buf)),
370                      SockaddrGetPort(&cli_addr));
371                close(newsockfd);
372                continue;
373             }
374             V(mutex);
375 #endif
376 
377             /*
378              * Receive notification when connection dies.
379              */
380             if (setsockopt(newsockfd, SOL_SOCKET, SO_KEEPALIVE, (sockopt_val_t)&value, sizeof(value)) < 0) {
381                BErrNo be;
382                Emsg1(M_WARNING, 0, _("Cannot set SO_KEEPALIVE on socket: %s\n"), be.bstrerror());
383             }
384 
385             /*
386              * See who client is. i.e. who connected to us.
387              */
388             P(mutex);
389             SockaddrToAscii(&cli_addr, buf, sizeof(buf));
390             V(mutex);
391 
392             BareosSocket *bs;
393             bs = New(BareosSocketTCP);
394             if (nokeepalive) {
395                bs->ClearKeepalive();
396             }
397 
398             bs->fd_ = newsockfd;
399             bs->SetWho(bstrdup("client"));
400             bs->SetHost(bstrdup(buf));
401             bs->SetPort(ntohs(fd_ptr->port));
402             memset(&bs->peer_addr, 0, sizeof(bs->peer_addr));
403             memcpy(&bs->client_addr, &cli_addr, sizeof(bs->client_addr));
404 
405             /*
406              * Queue client to be served
407              */
408             if ((status = WorkqAdd(client_wq, config, (void *)bs, NULL)) != 0) {
409                BErrNo be;
410                be.SetErrno(status);
411                Jmsg1(NULL, M_ABORT, 0, _("Could not add job to client queue: ERR=%s\n"),
412                      be.bstrerror());
413             }
414          }
415       }
416    }
417    if(server_state) { server_state->store(BnetServerState::kEnded); }
418 }
419