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