1 
2 #include <platform.h>
3 #include <server_code.h>
4 
5 #include <cf3.extern.h>                 // BINDINTERFACE
6 #include <printsize.h>                  // PRINTSIZE
7 #include <systype.h>                    // CLASSTEXT
8 #include <signals.h>                    // GetSignalPipe
9 #include <cleanup.h>                    // DoCleanupAndExit
10 #include <ctype.h>                      // isdigit
11 
12 #if HAVE_SYSTEMD_SD_DAEMON_H
13 #include <systemd/sd-daemon.h>          // sd_listen_fds
14 #endif
15 
16 /* Wait up to a minute for an in-coming connection.
17  *
18  * @param sd The listening socket or -1.
19  * @param tm_sec timeout in seconds
20  * @retval > 0 In-coming connection.
21  * @retval 0 No in-coming connection.
22  * @retval -1 Error (other than interrupt).
23  * @retval < -1 Interrupted while waiting.
24  */
WaitForIncoming(int sd,time_t tm_sec)25 int WaitForIncoming(int sd, time_t tm_sec)
26 {
27     Log(LOG_LEVEL_DEBUG, "Waiting at incoming select...");
28     struct timeval timeout = { .tv_sec = tm_sec };
29     int signal_pipe = GetSignalPipe();
30     fd_set rset;
31     FD_ZERO(&rset);
32     FD_SET(signal_pipe, &rset);
33 
34     /* sd might be -1 if "listen" attribute in body server control is set
35      * to off (enterprise feature for call-collected clients). */
36     if (sd != -1)
37     {
38         FD_SET(sd, &rset);
39     }
40 
41     int result = select(MAX(sd, signal_pipe) + 1,
42                         &rset, NULL, NULL, &timeout);
43     if (result == -1)
44     {
45         return (errno == EINTR) ? -2 : -1;
46     }
47     assert(result >= 0);
48 
49     /* Empty the signal pipe, it is there to only detect missed
50      * signals in-between checking IsPendingTermination() and calling
51      * select(). */
52     unsigned char buf;
53     while (recv(signal_pipe, &buf, 1, 0) > 0)
54     {
55         /* skip */
56     }
57 
58     /* We have an incoming connection if select() marked sd as ready: */
59     if (sd != -1 && result > 0 && FD_ISSET(sd, &rset))
60     {
61         return 1;
62     }
63     return 0;
64 }
65 
66 /**
67  * Orders 'struct addrinfo *' linked list in a descending order based on the
68  * ai_family, prefering IPV6.
69  */
OrderAddrInfoResponsesByAIfamily(struct addrinfo ** first_response)70 static void OrderAddrInfoResponsesByAIfamily(struct addrinfo **first_response)
71 {
72     /* Below is just a trivial implementation of the Bubble-sort algorithm to
73      * sort the linked list. */
74     struct addrinfo *first = *first_response;
75     bool change = true;
76     while (change)
77     {
78         change = false;
79         struct addrinfo *item = first;
80         struct addrinfo *prev = NULL;
81         while (item->ai_next != NULL)
82         {
83             /* AF_INET6 should be greater than AF_INET, but let's not rely on
84              * that and use a direct comparison. */
85             if ((item->ai_family == AF_INET) && (item->ai_next->ai_family == AF_INET6))
86             {
87                 struct addrinfo *orig_next = item->ai_next;
88                 item->ai_next = orig_next->ai_next;
89                 orig_next->ai_next = item;
90                 if (prev != NULL)
91                 {
92                     prev->ai_next = orig_next;
93                 }
94                 else
95                 {
96                     first = orig_next;
97                 }
98                 prev = orig_next;
99                 change = true;
100             }
101             else
102             {
103                 item = item->ai_next;
104             }
105         }
106     }
107     *first_response = first;
108 }
109 
110 /**
111  * @param bind_address address to bind to or %NULL to use the default BINDINTERFACE
112  */
OpenReceiverChannel(char * bind_address)113 static int OpenReceiverChannel(char *bind_address)
114 {
115     struct addrinfo *response = NULL, *ap;
116     struct addrinfo query = {
117         .ai_flags = AI_PASSIVE,
118         .ai_family = AF_UNSPEC,
119         .ai_socktype = SOCK_STREAM
120     };
121 
122     if (bind_address == NULL)
123     {
124         bind_address = BINDINTERFACE;
125     }
126 
127     /* Listen to INADDR(6)_ANY if BINDINTERFACE unset. */
128     char *ptr = NULL;
129     if (bind_address[0] != '\0')
130     {
131         ptr = bind_address;
132 
133         /* Just a quick check if the string looks like an IPv4 address to see if
134          * we should use AI_NUMERICHOST or not. IPv6 addresses could use it too,
135          * but they are not so easy to recognize and the proper check would be
136          * more expensive than the optimization. */
137         bool is_numeric_host = true;
138         for (char *c = ptr; is_numeric_host && (*c != '\0'); c++)
139         {
140             is_numeric_host = ((*c == '.') || isdigit(*c));
141         }
142         if (is_numeric_host)
143         {
144             query.ai_flags |= AI_NUMERICHOST;
145         }
146     }
147 
148     /* Resolve listening interface. */
149     int gres = getaddrinfo(ptr, CFENGINE_PORT_STR, &query, &response);
150     if (gres != 0)
151     {
152         Log(LOG_LEVEL_ERR, "DNS/service lookup failure. (getaddrinfo: %s)",
153             gai_strerror(gres));
154         if (response)
155         {
156             freeaddrinfo(response);
157         }
158         return -1;
159     }
160 
161     /* Make sure IPV6 addresses/interfaces are preferred over IPV4 ones (binding
162      * to the IPV6 address makes connections to the IPV4 equivalent work too, in
163      * particular for INADDR6_ANY (::) and INADDR_ANY (0.0.0.0/0). */
164     OrderAddrInfoResponsesByAIfamily(&response);
165 
166     int sd = -1;
167     for (ap = response; ap != NULL; ap = ap->ai_next)
168     {
169         sd = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol);
170         if (sd == -1)
171         {
172             if (ap->ai_family == AF_INET)
173             {
174                 Log(LOG_LEVEL_VERBOSE, "Failed to create socket for binding to an IPV4 interface");
175             }
176             else if (ap->ai_family == AF_INET6)
177             {
178                 Log(LOG_LEVEL_VERBOSE, "Failed to create socket for binding to an IPV6 interface");
179             }
180             else
181             {
182                 Log(LOG_LEVEL_VERBOSE,
183                     "Failed to create socket for binding to an interface of ai_family %d",
184                     ap->ai_family);
185             }
186             continue;
187         }
188 
189        #ifdef IPV6_V6ONLY
190         /* Properly implemented getaddrinfo(AI_PASSIVE) should return the IPV6
191            loopback address first. Some platforms (notably Windows) don't
192            listen to both address families when binding to it and need this
193            flag. Some other platforms won't even honour this flag
194            (openbsd). */
195         if (bind_address[0] == '\0' && ap->ai_family == AF_INET6)
196         {
197             int no = 0;
198             if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY,
199                            &no, sizeof(no)) == -1)
200             {
201                 Log(LOG_LEVEL_VERBOSE,
202                     "Failed to clear IPv6-only flag on listening socket"
203                     " (setsockopt: %s)",
204                     GetErrorStr());
205             }
206         }
207         #endif
208 
209         int yes = 1;
210         if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR,
211                        &yes, sizeof(yes)) == -1)
212         {
213             Log(LOG_LEVEL_VERBOSE,
214                 "Socket option SO_REUSEADDR was not accepted. (setsockopt: %s)",
215                 GetErrorStr());
216         }
217 
218         struct linger cflinger = {
219             .l_onoff = 1,
220             .l_linger = 60
221         };
222         if (setsockopt(sd, SOL_SOCKET, SO_LINGER,
223                        &cflinger, sizeof(cflinger)) == -1)
224         {
225             Log(LOG_LEVEL_INFO,
226                 "Socket option SO_LINGER was not accepted. (setsockopt: %s)",
227                 GetErrorStr());
228         }
229 
230         if (bind(sd, ap->ai_addr, ap->ai_addrlen) != -1)
231         {
232             if (WouldLog(LOG_LEVEL_DEBUG))
233             {
234                 /* Convert IP address to string, no DNS lookup performed. */
235                 char txtaddr[CF_MAX_IP_LEN] = "";
236                 getnameinfo(ap->ai_addr, ap->ai_addrlen,
237                             txtaddr, sizeof(txtaddr),
238                             NULL, 0, NI_NUMERICHOST);
239                 Log(LOG_LEVEL_DEBUG, "Bound to address '%s' on '%s' = %d", txtaddr,
240                     CLASSTEXT[VSYSTEMHARDCLASS], VSYSTEMHARDCLASS);
241             }
242             break;
243         }
244         Log(LOG_LEVEL_ERR, "Could not bind server address. (bind: %s)", GetErrorStr());
245         cf_closesocket(sd);
246         sd = -1;
247     }
248 
249     if (sd == -1)
250     {
251         Log(LOG_LEVEL_ERR,
252             "Failed to bind to all attempted addresses (bind specification: '%s'",
253             bind_address);
254     }
255 
256     assert(response != NULL);               /* getaddrinfo() was successful */
257     freeaddrinfo(response);
258     return sd;
259 }
260 
261 /**
262  * @param queue_size   length of the queue for pending connections
263  * @param bind_address address to bind to or %NULL to use the default BINDINTERFACE
264  */
InitServer(size_t queue_size,char * bind_address)265 int InitServer(size_t queue_size, char *bind_address)
266 {
267 #if HAVE_SYSTEMD_SD_DAEMON_H
268     int n = sd_listen_fds(0);
269     if (n > 1)
270     {
271         Log(LOG_LEVEL_ERR, "Too many file descriptors received from systemd");
272     }
273     else if (n == 1)
274     {
275         // we can check here that we have a socket with sd_is_socket_inet(3)
276         // but why should we limit ourselves
277         return SD_LISTEN_FDS_START;
278     }
279     else
280 #endif // HAVE_SYSTEMD_SD_DAEMON_H
281     {
282         int sd = OpenReceiverChannel(bind_address);
283 
284         if (sd == -1)
285         {
286             /* More detailed info is logged in case of error in
287              * OpenReceiverChannel() */
288             Log(LOG_LEVEL_ERR, "Unable to start server");
289         }
290         else if (listen(sd, queue_size) == -1)
291         {
292             Log(LOG_LEVEL_ERR, "Failed to listen on the '%s' address (listen: %s)",
293                 bind_address, GetErrorStr());
294             cf_closesocket(sd);
295         }
296         else
297         {
298             return sd;
299         }
300     }
301 
302     DoCleanupAndExit(EXIT_FAILURE);
303 }
304