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