]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/bnet_server.c
b050d88405b61658aa41abddcffa19cd30b92bab
[bacula/bacula] / bacula / src / lib / bnet_server.c
1 /*
2    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
3
4    This program is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License as
6    published by the Free Software Foundation; either version 2 of
7    the License, or (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12    General Public License for more details.
13
14    You should have received a copy of the GNU General Public
15    License along with this program; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
17    MA 02111-1307, USA.
18
19  */
20  /* 
21   * Originally written by Kern Sibbald for inclusion in apcupsd,
22   *  but heavily modified for Bacula
23   *
24   *   Version $Id$
25   */
26
27 #include "bacula.h"
28 #undef DEV_BSIZE
29 #include <netinet/in.h>
30 #include <sys/socket.h>
31 #include <arpa/inet.h>
32 #include <netdb.h>
33 #ifdef HAVE_ARPA_NAMESER_H
34 #include <arpa/nameser.h>
35 #endif
36 #ifdef HAVE_RESOLV_H
37 #include <resolv.h>
38 #endif
39
40
41 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
42
43 #ifdef HAVE_LIBWRAP
44 #include "tcpd.h"
45 int allow_severity = LOG_NOTICE;
46 int deny_severity = LOG_WARNING;
47 #endif
48
49 /* Become Threaded Network Server */
50 void
51 bnet_thread_server(char *bind_addr, int port, int max_clients, workq_t *client_wq, 
52                    void *handle_client_request(void *bsock))
53 {
54    int newsockfd, sockfd, stat;
55    socklen_t clilen;
56    struct sockaddr_in cli_addr;       /* client's address */
57    struct sockaddr_in serv_addr;      /* our address */
58    struct in_addr bind_ip;            /* address to bind to */
59    int tlog;
60    int turnon = 1;
61    char *caller;
62 #ifdef HAVE_LIBWRAP
63    struct request_info request;
64 #endif
65
66    /*
67     * Open a TCP socket  
68     */
69    for (tlog=0; (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0; tlog -= 10 ) {
70       if (tlog <= 0) {
71          tlog = 60; 
72          Emsg1(M_ERROR, 0, _("Cannot open stream socket: %s. Retrying ...\n"), strerror(errno));
73       }
74       bmicrosleep(10, 0);
75    }
76
77    /*
78     * Reuse old sockets 
79     */
80    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (sockopt_val_t)&turnon, sizeof(turnon)) < 0) {
81       Emsg1(M_WARNING, 0, _("Cannot set SO_REUSEADDR on socket: %s\n"), strerror(errno));
82    }
83
84    /* 
85     * Bind our local address so that the client can send to us.
86     */
87    bind_ip.s_addr = htonl(INADDR_ANY);
88    if (bind_addr && bind_addr[0]) {
89 #ifdef HAVE_INET_PTON
90       if (inet_pton(AF_INET, bind_addr, &bind_ip) <= 0) {
91 #else
92       if (inet_aton(bind_addr, &bind_ip) <= 0) {
93 #endif
94          Emsg1(M_WARNING, 0, _("Invalid bind address: %s, using INADDR_ANY\n"),
95             bind_addr);
96          bind_ip.s_addr = htonl(INADDR_ANY);
97       }
98    }
99    memset((char *) &serv_addr, 0, sizeof(serv_addr));
100    serv_addr.sin_family = AF_INET;
101    serv_addr.sin_addr.s_addr = bind_ip.s_addr;
102    serv_addr.sin_port = htons(port);
103
104    int tmax = 30 * (60 / 5);          /* wait 30 minutes max */
105    for (tlog=0; bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0; tlog -= 5 ) {
106       if (tlog <= 0) {
107          tlog = 2*60;                 /* Complain every 2 minutes */
108          Emsg2(M_WARNING, 0, _("Cannot bind port %d: %s. Retrying ...\n"), port, strerror(errno));
109       }
110       bmicrosleep(5, 0);
111       if (--tmax <= 0) {
112          Emsg2(M_ABORT, 0, _("Cannot bind port %d: %s.\n"), port, strerror(errno));
113       }
114    }
115    listen(sockfd, 5);                 /* tell system we are ready */
116
117    /* Start work queue thread */
118    if ((stat = workq_init(client_wq, max_clients, handle_client_request)) != 0) {
119       Emsg1(M_ABORT, 0, _("Could not init client queue: ERR=%s\n"), strerror(stat));
120    }
121
122    /* 
123     * Wait for a connection from the client process.
124     */
125    for (;;) {
126       fd_set sockset;
127       FD_ZERO(&sockset);
128       FD_SET(sockfd, &sockset);
129       errno = 0;
130       if ((stat = select(sockfd+1, &sockset, NULL, NULL, NULL)) < 0) {
131          if (errno == EINTR || errno == EAGAIN) {
132             continue;
133          }
134          /* Error, get out */
135          close(sockfd);
136          Emsg1(M_FATAL, 0, _("Error in select: %s\n"), strerror(errno));
137          break;
138       }
139
140       /* Got a connection, now accept it. */
141       do {
142          clilen = sizeof(cli_addr);
143          newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
144       } while (newsockfd < 0 && (errno == EINTR || errno == EAGAIN));
145       if (newsockfd < 0) {
146          continue;
147       }
148
149
150 #ifdef HAVE_LIBWRAP
151       P(mutex);                       /* hosts_access is not thread safe */
152       request_init(&request, RQ_DAEMON, my_name, RQ_FILE, newsockfd, 0);
153       fromhost(&request);
154       if (!hosts_access(&request)) {
155          V(mutex);
156          Jmsg2(NULL, M_SECURITY, 0, _("Connection from %s:%d refused by hosts.access\n"),
157                inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
158          close(newsockfd);
159          continue;
160       }
161       V(mutex);
162 #endif
163
164       /*
165        * Receive notification when connection dies.
166        */
167       if (setsockopt(newsockfd, SOL_SOCKET, SO_KEEPALIVE, (sockopt_val_t)&turnon, sizeof(turnon)) < 0) {
168          Emsg1(M_WARNING, 0, _("Cannot set SO_KEEPALIVE on socket: %s\n") , strerror(errno));
169       }
170
171       /* see who client is. i.e. who connected to us. */
172       P(mutex);
173       caller = inet_ntoa(cli_addr.sin_addr);  /* NOT thread safe, use mutex */
174       if (caller == NULL) {
175          caller = "unknown client";
176       }
177
178       BSOCK *bs = init_bsock(NULL, newsockfd, "client", caller, port, &cli_addr);
179       if (bs == NULL) {
180          Jmsg0(NULL, M_ABORT, 0, _("Could not create client BSOCK.\n"));
181       }
182
183       /* Queue client to be served */
184       if ((stat = workq_add(client_wq, (void *)bs, NULL, 0)) != 0) {
185          V(mutex);
186          Jmsg1(NULL, M_ABORT, 0, _("Could not add job to client queue: ERR=%s\n"), strerror(stat));
187       }
188       V(mutex);
189    }
190
191    /* Stop work queue thread */
192    if ((stat = workq_destroy(client_wq)) != 0) {
193       Emsg1(M_FATAL, 0, _("Could not destroy client queue: ERR=%s\n"), strerror(stat));
194    }
195 }   
196
197
198 #ifdef REALLY_USED   
199 /*
200  * Bind an address so that we may accept connections
201  * one at a time.
202  */
203 BSOCK *
204 bnet_bind(int port)
205 {
206    int sockfd;
207    struct sockaddr_in serv_addr;      /* our address */
208    int tlog;
209    int turnon = 1;
210
211    /*
212     * Open a TCP socket  
213     */
214    for (tlog=0; (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0; tlog -= 10 ) {
215       if (errno == EINTR || errno == EAGAIN) {
216          continue;
217       }
218       if (tlog <= 0) {
219          tlog = 2*60; 
220          Emsg1(M_ERROR, 0, _("Cannot open stream socket: %s\n"), strerror(errno));
221       }
222       bmicrosleep(60, 0);
223    }
224
225    /*
226     * Reuse old sockets 
227     */
228    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (sockopt_val_t)&turnon, sizeof(turnon)) < 0) {
229       Emsg1(M_WARNING, 0, _("Cannot set SO_REUSEADDR on socket: %s\n") , strerror(errno));
230    }
231
232    /* 
233     * Bind our local address so that the client can send to us.
234     */
235    bzero((char *) &serv_addr, sizeof(serv_addr));
236    serv_addr.sin_family = AF_INET;
237    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
238    serv_addr.sin_port = htons(port);
239
240    for (tlog=0; bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0; tlog -= 5 ) {
241       if (errno == EINTR || errno == EAGAIN) {
242          continue;
243       }
244       if (tlog <= 0) {
245          tlog = 2*60;
246          Emsg2(M_WARNING, 0, _("Cannot bind port %d: %s: retrying ...\n"), port, strerror(errno));
247       }
248       bmicrosleep(5, 0);
249    }
250    listen(sockfd, 1);                 /* tell system we are ready */
251    return init_bsock(NULL, sockfd, _("Server socket"), _("client"), port, &serv_addr);
252 }
253
254 /*
255  * Accept a single connection 
256  */
257 BSOCK *
258 bnet_accept(BSOCK *bsock, char *who)
259 {
260    fd_set ready, sockset;
261    int newsockfd, stat, len;
262    socklen_t clilen;
263    struct sockaddr_in cli_addr;       /* client's address */
264    char *caller, *buf;
265    BSOCK *bs;
266    int turnon = 1;
267 #ifdef HAVE_LIBWRAP
268    struct request_info request;
269 #endif
270
271    /* 
272     * Wait for a connection from the client process.
273     */
274    FD_ZERO(&sockset);
275    FD_SET(bsock->fd, &sockset);
276
277    for (;;) {
278       /* 
279        * Wait for a connection from a client process.
280        */
281       ready = sockset;
282       if ((stat = select(bsock->fd+1, &ready, NULL, NULL, NULL)) < 0) {
283          if (errno == EINTR || errno = EAGAIN) {
284             errno = 0;
285             continue;
286          }
287          Emsg1(M_FATAL, 0, _("Error in select: %s\n"), strerror(errno));
288          newsockfd = -1;
289          break;
290       }
291       do {
292          clilen = sizeof(cli_addr);
293          newsockfd = accept(bsock->fd, (struct sockaddr *)&cli_addr, &clilen);
294       } while (newsockfd < 0 && (errno == EINTR || errno = EAGAIN));
295       if (newsockfd >= 0) {
296          break;
297       }
298    }
299
300 #ifdef HAVE_LIBWRAP
301    P(mutex);
302    request_init(&request, RQ_DAEMON, my_name, RQ_FILE, newsockfd, 0);
303    fromhost(&request);
304    if (!hosts_access(&request)) {
305       V(mutex);
306       Emsg2(M_SECURITY, 0, _("Connection from %s:%d refused by hosts.access\n"),
307             inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
308       close(newsockfd);
309       return NULL;
310    }
311    V(mutex);
312 #endif
313
314    /*
315     * Receive notification when connection dies.
316     */
317    if (setsockopt(newsockfd, SOL_SOCKET, SO_KEEPALIVE, (sockopt_val_t)&turnon, sizeof(turnon)) < 0) {
318       Emsg1(M_WARNING, 0, _("Cannot set SO_KEEPALIVE on socket: %s\n"), strerror(errno));
319    }
320
321    /* see who client is. I.e. who connected to us.
322     * return it in the input message buffer.
323     */
324    if ((caller = inet_ntoa(cli_addr.sin_addr)) != NULL) {
325       pm_strcpy(&bsock->msg, caller);
326    } else {
327       bsock->msg[0] = 0;
328    }
329    bsock->msglen = strlen(bsock->msg);
330
331    if (newsockfd < 0) {
332       Emsg2(M_FATAL, 0, _("Socket accept error for %s. ERR=%s\n"), who,
333             strerror(errno));
334       return NULL;
335    } else {
336       if (caller == NULL) {
337          caller = "unknown";
338       }
339       len = strlen(caller) + strlen(who) + 3;
340       buf = (char *) malloc(len);
341       strcpy(buf, who);
342       strcat(buf, ": ");
343       strcat(buf, caller);
344       bs = init_bsock(NULL, newsockfd, "client", buf, bsock->port, &cli_addr);
345       free(buf);
346       return bs;                      /* return new BSOCK */
347    }
348 }   
349
350 #endif