/src/proftpd/src/bindings.c
Line | Count | Source |
1 | | /* |
2 | | * ProFTPD - FTP server daemon |
3 | | * Copyright (c) 2001-2026 The ProFTPD Project team |
4 | | * |
5 | | * This program is free software; you can redistribute it and/or modify |
6 | | * it under the terms of the GNU General Public License as published by |
7 | | * the Free Software Foundation; either version 2 of the License, or |
8 | | * (at your option) any later version. |
9 | | * |
10 | | * This program is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | * GNU General Public License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU General Public License |
16 | | * along with this program; if not, see <https://www.gnu.org/licenses/>. |
17 | | * |
18 | | * As a special exemption, The ProFTPD Project team and other respective |
19 | | * copyright holders give permission to link this program with OpenSSL, and |
20 | | * distribute the resulting executable, without including the source code for |
21 | | * OpenSSL in the source distribution. |
22 | | */ |
23 | | |
24 | | /* Routines to work with ProFTPD bindings. */ |
25 | | |
26 | | #include "conf.h" |
27 | | |
28 | | /* From src/dirtree.c */ |
29 | | extern xaset_t *server_list; |
30 | | extern server_rec *main_server; |
31 | | |
32 | | static pr_ipbind_t *ipbind_table[PR_BINDINGS_TABLE_SIZE]; |
33 | | static int ipbind_table_initialized = FALSE; |
34 | | |
35 | | static pool *binding_pool = NULL; |
36 | | static pr_ipbind_t *ipbind_default_server = NULL, |
37 | | *ipbind_localhost_server = NULL; |
38 | | |
39 | | static const char *trace_channel = "binding"; |
40 | | |
41 | | static unsigned int process_serveralias(server_rec *s); |
42 | | static void trace_ipbind_table(void); |
43 | | |
44 | 0 | static void init_ipbind_table(void) { |
45 | 0 | if (ipbind_table_initialized == TRUE) { |
46 | 0 | return; |
47 | 0 | } |
48 | | |
49 | 0 | memset(ipbind_table, 0, sizeof(ipbind_table)); |
50 | 0 | ipbind_table_initialized = TRUE; |
51 | 0 | } |
52 | | |
53 | | /* Server cleanup callback function */ |
54 | 0 | static void server_cleanup_cb(void *conn) { |
55 | 0 | *((conn_t **) conn) = NULL; |
56 | 0 | } |
57 | | |
58 | | /* The hashing function for the hash table of bindings. This algorithm |
59 | | * is stolen from Apache's http_vhost.c |
60 | | */ |
61 | | static unsigned int ipbind_hash_addr(const pr_netaddr_t *addr, |
62 | 0 | unsigned int port) { |
63 | 0 | size_t offset; |
64 | 0 | unsigned int key, idx; |
65 | |
|
66 | 0 | offset = pr_netaddr_get_inaddr_len(addr); |
67 | | |
68 | | /* The key is the last four bytes of the IP address. |
69 | | * For IPv4, this is the entire address, as always. |
70 | | * For IPv6, this is usually part of the MAC address. |
71 | | */ |
72 | 0 | key = *(unsigned *) ((char *) pr_netaddr_get_inaddr(addr) + offset - 4); |
73 | | |
74 | | /* Add in the port number, to give better spread of key values when many |
75 | | * different port values are used. |
76 | | */ |
77 | 0 | key += port; |
78 | |
|
79 | 0 | key ^= (key >> 16); |
80 | 0 | idx = ((key >> 8) ^ key) % PR_BINDINGS_TABLE_SIZE; |
81 | 0 | return idx; |
82 | 0 | } |
83 | | |
84 | | static pool *listening_conn_pool = NULL; |
85 | | static xaset_t *listening_conn_list = NULL; |
86 | | struct listener_rec { |
87 | | struct listener_rec *next, *prev; |
88 | | |
89 | | pool *pool; |
90 | | const pr_netaddr_t *addr; |
91 | | unsigned int port; |
92 | | conn_t *conn; |
93 | | int claimed; |
94 | | }; |
95 | | |
96 | | conn_t *pr_ipbind_get_listening_conn(server_rec *server, |
97 | 0 | const pr_netaddr_t *addr, unsigned int port) { |
98 | 0 | conn_t *l, *sl; |
99 | 0 | pool *p; |
100 | 0 | struct listener_rec *lr; |
101 | |
|
102 | 0 | if (listening_conn_list) { |
103 | 0 | for (lr = (struct listener_rec *) listening_conn_list->xas_list; lr; |
104 | 0 | lr = lr->next) { |
105 | 0 | int use_elt = FALSE; |
106 | |
|
107 | 0 | pr_signals_handle(); |
108 | |
|
109 | 0 | if (addr != NULL && |
110 | 0 | lr->addr != NULL) { |
111 | 0 | const char *lr_ipstr = NULL; |
112 | |
|
113 | 0 | lr_ipstr = pr_netaddr_get_ipstr(lr->addr); |
114 | | |
115 | | /* Note: lr_ipstr should never be null. If it is, it means that |
116 | | * the lr->addr object never had its IP address resolved/stashed, |
117 | | * and in attempting to do, getnameinfo(3) failed for some reason. |
118 | | * |
119 | | * The IP address on which it's listening, if not available via |
120 | | * lr->addr, should thus be available via lr->conn->local_addr. |
121 | | */ |
122 | |
|
123 | 0 | if (lr_ipstr == NULL && |
124 | 0 | lr->conn != NULL) { |
125 | 0 | lr_ipstr = pr_netaddr_get_ipstr(lr->conn->local_addr); |
126 | 0 | } |
127 | |
|
128 | 0 | if (lr_ipstr != NULL) { |
129 | 0 | if (strcmp(pr_netaddr_get_ipstr(addr), lr_ipstr) == 0 && |
130 | 0 | port == lr->port) { |
131 | 0 | use_elt = TRUE; |
132 | 0 | } |
133 | 0 | } |
134 | |
|
135 | 0 | } else if (addr == NULL && |
136 | 0 | port == lr->port) { |
137 | 0 | use_elt = TRUE; |
138 | 0 | } |
139 | |
|
140 | 0 | if (use_elt == TRUE) { |
141 | 0 | lr->claimed = TRUE; |
142 | 0 | return lr->conn; |
143 | 0 | } |
144 | 0 | } |
145 | 0 | } |
146 | | |
147 | 0 | if (listening_conn_pool == NULL) { |
148 | 0 | listening_conn_pool = make_sub_pool(permanent_pool); |
149 | 0 | pr_pool_tag(listening_conn_pool, "Listening Connection Pool"); |
150 | |
|
151 | 0 | listening_conn_list = xaset_create(listening_conn_pool, NULL); |
152 | 0 | } |
153 | |
|
154 | 0 | p = make_sub_pool(listening_conn_pool); |
155 | 0 | pr_pool_tag(p, "Listening conn subpool"); |
156 | |
|
157 | 0 | l = pr_inet_create_conn(p, -1, addr, port, FALSE); |
158 | 0 | if (l == NULL) { |
159 | 0 | return NULL; |
160 | 0 | } |
161 | | |
162 | | /* Inform any interested listeners that this socket was opened. In order |
163 | | * to convey the discovered conn_t `l` to the event listener, we set it |
164 | | * on the server `s` temporarily. |
165 | | */ |
166 | 0 | sl = server->listen; |
167 | 0 | server->listen = l; |
168 | 0 | pr_inet_generate_socket_event("core.ctrl-listen", server, l->local_addr, |
169 | 0 | l->listen_fd); |
170 | 0 | server->listen = sl; |
171 | |
|
172 | 0 | lr = pcalloc(p, sizeof(struct listener_rec)); |
173 | 0 | lr->pool = p; |
174 | 0 | lr->conn = l; |
175 | 0 | lr->addr = pr_netaddr_dup(p, addr); |
176 | 0 | if (lr->addr == NULL && |
177 | 0 | errno != EINVAL) { |
178 | 0 | return NULL; |
179 | 0 | } |
180 | 0 | lr->port = port; |
181 | 0 | lr->claimed = TRUE; |
182 | |
|
183 | 0 | xaset_insert(listening_conn_list, (xasetmember_t *) lr); |
184 | 0 | return l; |
185 | 0 | } |
186 | | |
187 | | /* Slight (clever?) optimization: the loop in server_loop() always |
188 | | * calls pr_ipbind_listen(), selects, then pr_ipbind_accept_conn(). Now, |
189 | | * rather than having both pr_ipbind_listen() and pr_ipbind_accept_conn() |
190 | | * scan the entire ipbind table looking for matches, what if pr_ipbind_listen |
191 | | * kept track of which listeners (connt_s) it used, so that |
192 | | * pr_ipbind_accept_conn() need merely check those listeners, rather than |
193 | | * scanning the entire table itself? |
194 | | */ |
195 | | |
196 | | static array_header *listener_list = NULL; |
197 | | |
198 | 0 | conn_t *pr_ipbind_accept_conn(fd_set *readfds, int *listenfd) { |
199 | 0 | conn_t **listeners = listener_list->elts; |
200 | 0 | register unsigned int i = 0; |
201 | |
|
202 | 0 | if (readfds == NULL || |
203 | 0 | listenfd == NULL) { |
204 | 0 | errno = EINVAL; |
205 | 0 | return NULL; |
206 | 0 | } |
207 | | |
208 | 0 | for (i = 0; i < listener_list->nelts; i++) { |
209 | 0 | conn_t *listener = listeners[i]; |
210 | |
|
211 | 0 | pr_signals_handle(); |
212 | 0 | if (FD_ISSET(listener->listen_fd, readfds) && |
213 | 0 | listener->mode == CM_LISTEN) { |
214 | 0 | int fd = pr_inet_accept_nowait(listener->pool, listener); |
215 | |
|
216 | 0 | if (fd == -1) { |
217 | 0 | int xerrno = errno; |
218 | | |
219 | | /* Handle errors gracefully. If we're here, then |
220 | | * ipbind->ib_server->listen contains either error information, or |
221 | | * we just got caught in a blocking condition. |
222 | | */ |
223 | 0 | if (listener->mode == CM_ERROR) { |
224 | | |
225 | | /* Ignore ECONNABORTED, as they tend to be health checks/probes by |
226 | | * e.g. load balancers and other naive TCP clients. |
227 | | */ |
228 | 0 | if (listener->xerrno != ECONNABORTED) { |
229 | 0 | pr_log_pri(PR_LOG_ERR, "error: unable to accept an incoming " |
230 | 0 | "connection: %s", strerror(listener->xerrno)); |
231 | 0 | } |
232 | |
|
233 | 0 | listener->xerrno = 0; |
234 | 0 | listener->mode = CM_LISTEN; |
235 | |
|
236 | 0 | errno = xerrno; |
237 | 0 | return NULL; |
238 | 0 | } |
239 | 0 | } |
240 | | |
241 | 0 | *listenfd = fd; |
242 | 0 | return listener; |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | 0 | errno = ENOENT; |
247 | 0 | return NULL; |
248 | 0 | } |
249 | | |
250 | 0 | int pr_ipbind_add_binds(server_rec *serv) { |
251 | 0 | int res = 0; |
252 | 0 | config_rec *c = NULL; |
253 | 0 | conn_t *listen_conn = NULL; |
254 | 0 | const pr_netaddr_t *addr = NULL; |
255 | |
|
256 | 0 | if (serv == NULL) { |
257 | 0 | errno = EINVAL; |
258 | 0 | return -1; |
259 | 0 | } |
260 | | |
261 | 0 | c = find_config(serv->conf, CONF_PARAM, "_bind_", FALSE); |
262 | 0 | while (c != NULL) { |
263 | 0 | listen_conn = NULL; |
264 | |
|
265 | 0 | pr_signals_handle(); |
266 | |
|
267 | 0 | addr = pr_netaddr_get_addr(serv->pool, c->argv[0], NULL); |
268 | 0 | if (addr == NULL) { |
269 | 0 | pr_log_pri(PR_LOG_WARNING, |
270 | 0 | "notice: unable to determine IP address of '%s'", (char *) c->argv[0]); |
271 | 0 | c = find_config_next(c, c->next, CONF_PARAM, "_bind_", FALSE); |
272 | 0 | continue; |
273 | 0 | } |
274 | | |
275 | | /* If the SocketBindTight directive is in effect, create a separate |
276 | | * listen socket for this address, and add it to the binding list. |
277 | | */ |
278 | 0 | if (SocketBindTight && |
279 | 0 | serv->ServerPort) { |
280 | 0 | listen_conn = pr_ipbind_get_listening_conn(serv, addr, serv->ServerPort); |
281 | 0 | if (listen_conn == NULL) { |
282 | 0 | return -1; |
283 | 0 | } |
284 | | |
285 | 0 | res = pr_ipbind_create(serv, addr, serv->ServerPort); |
286 | 0 | if (res < 0) { |
287 | 0 | pr_log_pri(PR_LOG_NOTICE, |
288 | 0 | "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__, |
289 | 0 | __LINE__, serv->ServerAddress, serv->ServerPort, strerror(errno)); |
290 | 0 | } |
291 | |
|
292 | 0 | res = pr_ipbind_open(addr, serv->ServerPort, listen_conn, FALSE, FALSE, |
293 | 0 | TRUE); |
294 | 0 | if (res < 0) { |
295 | 0 | pr_log_pri(PR_LOG_NOTICE, |
296 | 0 | "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__, |
297 | 0 | pr_netaddr_get_ipstr(addr), strerror(errno)); |
298 | 0 | } |
299 | |
|
300 | 0 | } else { |
301 | 0 | res = pr_ipbind_create(serv, addr, serv->ServerPort); |
302 | 0 | if (res < 0) { |
303 | 0 | pr_log_pri(PR_LOG_NOTICE, |
304 | 0 | "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__, |
305 | 0 | __LINE__, serv->ServerAddress, serv->ServerPort, strerror(errno)); |
306 | 0 | } |
307 | |
|
308 | 0 | res = pr_ipbind_open(addr, serv->ServerPort, serv->listen, FALSE, FALSE, |
309 | 0 | TRUE); |
310 | 0 | if (res < 0) { |
311 | 0 | pr_log_pri(PR_LOG_NOTICE, |
312 | 0 | "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__, |
313 | 0 | pr_netaddr_get_ipstr(addr), strerror(errno)); |
314 | 0 | } |
315 | 0 | } |
316 | | |
317 | 0 | c = find_config_next(c, c->next, CONF_PARAM, "_bind_", FALSE); |
318 | 0 | } |
319 | | |
320 | 0 | return 0; |
321 | 0 | } |
322 | | |
323 | | int pr_ipbind_close(const pr_netaddr_t *addr, unsigned int port, |
324 | 0 | unsigned char close_namebinds) { |
325 | 0 | register unsigned int i = 0; |
326 | |
|
327 | 0 | if (addr != NULL) { |
328 | 0 | pr_ipbind_t *ipbind = NULL; |
329 | 0 | unsigned char have_ipbind = FALSE; |
330 | |
|
331 | 0 | i = ipbind_hash_addr(addr, port); |
332 | |
|
333 | 0 | if (ipbind_table[i] == NULL) { |
334 | 0 | pr_log_pri(PR_LOG_NOTICE, "notice: no ipbind found for %s:%d", |
335 | 0 | pr_netaddr_get_ipstr(addr), port); |
336 | 0 | errno = ENOENT; |
337 | 0 | return -1; |
338 | 0 | } |
339 | | |
340 | 0 | for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) { |
341 | 0 | pr_signals_handle(); |
342 | |
|
343 | 0 | if (pr_netaddr_cmp(ipbind->ib_addr, addr) == 0 && |
344 | 0 | (!ipbind->ib_port || ipbind->ib_port == port)) { |
345 | 0 | have_ipbind = TRUE; |
346 | 0 | break; |
347 | 0 | } |
348 | 0 | } |
349 | |
|
350 | 0 | if (have_ipbind == FALSE) { |
351 | 0 | pr_log_pri(PR_LOG_NOTICE, "notice: no ipbind found for %s:%d", |
352 | 0 | pr_netaddr_get_ipstr(addr), port); |
353 | 0 | errno = ENOENT; |
354 | 0 | return -1; |
355 | 0 | } |
356 | | |
357 | | /* If already closed, exit now. */ |
358 | 0 | if (ipbind->ib_isactive == FALSE) { |
359 | 0 | errno = EPERM; |
360 | 0 | return -1; |
361 | 0 | } |
362 | | |
363 | | /* Close the ipbinding's listen connection, if present. The trick |
364 | | * here is determining whether this binding's listen member is |
365 | | * _the_ listening socket for the master daemon, or whether it's |
366 | | * been created for SocketBindTight, and can be closed. |
367 | | * |
368 | | * Actually, it's not that hard. It's only _the_ listening socket |
369 | | * for the master daemon in inetd mode, in which case virtual servers |
370 | | * can't be shutdown via ftpdctl, anyway. |
371 | | */ |
372 | 0 | if (SocketBindTight && |
373 | 0 | ipbind->ib_listener != NULL) { |
374 | 0 | pr_inet_close(ipbind->ib_server->pool, ipbind->ib_listener); |
375 | 0 | ipbind->ib_listener = ipbind->ib_server->listen = NULL; |
376 | 0 | } |
377 | | |
378 | | /* Mark this ipbind as inactive. For SocketBindTight sockets, the |
379 | | * closing of the listening connection will suffice, from the clients' |
380 | | * point of view. However, this covers the non-SocketBindTight case, |
381 | | * and will prevent this binding from returning its server_rec pointer |
382 | | * on future lookup requests via pr_ipbind_get_server(). |
383 | | */ |
384 | 0 | ipbind->ib_isactive = FALSE; |
385 | |
|
386 | 0 | if (close_namebinds == TRUE && |
387 | 0 | ipbind->ib_namebinds != NULL) { |
388 | 0 | register unsigned int j = 0; |
389 | 0 | pr_namebind_t **namebinds = NULL; |
390 | |
|
391 | 0 | namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts; |
392 | 0 | for (j = 0; j < ipbind->ib_namebinds->nelts; j++) { |
393 | 0 | pr_namebind_t *nb; |
394 | |
|
395 | 0 | pr_signals_handle(); |
396 | |
|
397 | 0 | nb = namebinds[j]; |
398 | 0 | if (pr_namebind_close(nb->nb_name, nb->nb_server->addr) < 0) { |
399 | 0 | pr_trace_msg(trace_channel, 7, |
400 | 0 | "notice: error closing namebind '%s' for address %s: %s", |
401 | 0 | nb->nb_name, pr_netaddr_get_ipstr(nb->nb_server->addr), |
402 | 0 | strerror(errno)); |
403 | 0 | } |
404 | 0 | } |
405 | 0 | } |
406 | |
|
407 | 0 | } else { |
408 | | /* A NULL addr has a special meaning: close _all_ ipbinds in the list. */ |
409 | 0 | for (i = 0; i < PR_BINDINGS_TABLE_SIZE; i++) { |
410 | 0 | pr_ipbind_t *ipbind = NULL; |
411 | |
|
412 | 0 | pr_signals_handle(); |
413 | |
|
414 | 0 | ipbind = ipbind_table[i]; |
415 | 0 | if (ipbind == NULL) { |
416 | 0 | continue; |
417 | 0 | } |
418 | | |
419 | 0 | pr_trace_msg(trace_channel, 22, |
420 | 0 | "found ipbind %p at index %u in table, closing", ipbind, i); |
421 | |
|
422 | 0 | for (; ipbind; ipbind = ipbind->ib_next) { |
423 | 0 | if (SocketBindTight && |
424 | 0 | ipbind->ib_listener != NULL) { |
425 | 0 | pr_inet_close(main_server->pool, ipbind->ib_listener); |
426 | 0 | ipbind->ib_listener = ipbind->ib_server->listen = NULL; |
427 | 0 | } |
428 | | |
429 | | /* Note: do not need to check if this ipbind was previously closed, |
430 | | * for the NULL addr is a request to shut down all ipbinds, |
431 | | * regardless of their current state. |
432 | | */ |
433 | 0 | ipbind->ib_isactive = FALSE; |
434 | |
|
435 | 0 | if (close_namebinds == TRUE && |
436 | 0 | ipbind->ib_namebinds != NULL) { |
437 | 0 | register unsigned int j = 0; |
438 | 0 | pr_namebind_t **namebinds = NULL; |
439 | |
|
440 | 0 | namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts; |
441 | 0 | for (j = 0; j < ipbind->ib_namebinds->nelts; j++) { |
442 | 0 | pr_namebind_t *nb; |
443 | |
|
444 | 0 | pr_signals_handle(); |
445 | |
|
446 | 0 | nb = namebinds[j]; |
447 | 0 | if (pr_namebind_close(nb->nb_name, nb->nb_server->addr) < 0) { |
448 | 0 | pr_trace_msg(trace_channel, 7, |
449 | 0 | "notice: error closing namebind '%s' for address %s: %s", |
450 | 0 | nb->nb_name, pr_netaddr_get_ipstr(nb->nb_server->addr), |
451 | 0 | strerror(errno)); |
452 | 0 | } |
453 | 0 | } |
454 | 0 | } |
455 | 0 | } |
456 | 0 | } |
457 | 0 | } |
458 | | |
459 | 0 | return 0; |
460 | 0 | } |
461 | | |
462 | | /* Need a way to close all listening fds in a child process. */ |
463 | 0 | int pr_ipbind_close_listeners(void) { |
464 | 0 | conn_t **listeners; |
465 | 0 | register unsigned int i = 0; |
466 | |
|
467 | 0 | if (listener_list == NULL || |
468 | 0 | listener_list->nelts == 0) { |
469 | 0 | return 0; |
470 | 0 | } |
471 | | |
472 | 0 | listeners = listener_list->elts; |
473 | 0 | for (i = 0; i < listener_list->nelts; i++) { |
474 | 0 | conn_t *listener; |
475 | |
|
476 | 0 | pr_signals_handle(); |
477 | |
|
478 | 0 | listener = listeners[i]; |
479 | 0 | if (listener->listen_fd != -1) { |
480 | 0 | (void) close(listener->listen_fd); |
481 | 0 | listener->listen_fd = -1; |
482 | 0 | } |
483 | 0 | } |
484 | |
|
485 | 0 | return 0; |
486 | 0 | } |
487 | | |
488 | | int pr_ipbind_create(server_rec *server, const pr_netaddr_t *addr, |
489 | 0 | unsigned int port) { |
490 | 0 | pr_ipbind_t *existing = NULL, *ipbind = NULL; |
491 | 0 | register unsigned int i = 0; |
492 | |
|
493 | 0 | if (server == NULL|| |
494 | 0 | addr == NULL) { |
495 | 0 | errno = EINVAL; |
496 | 0 | return -1; |
497 | 0 | } |
498 | | |
499 | | /* Ensure the ipbind table has been initialized. */ |
500 | 0 | init_ipbind_table(); |
501 | |
|
502 | 0 | i = ipbind_hash_addr(addr, port); |
503 | 0 | pr_trace_msg(trace_channel, 29, "hashed address '%s' to index %u", |
504 | 0 | pr_netaddr_get_ipstr(addr), i); |
505 | | |
506 | | /* Make sure the address is not already in use */ |
507 | 0 | for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) { |
508 | 0 | pr_signals_handle(); |
509 | |
|
510 | 0 | if (pr_netaddr_cmp(ipbind->ib_addr, addr) == 0 && |
511 | 0 | ipbind->ib_port == port) { |
512 | 0 | existing = ipbind; |
513 | 0 | break; |
514 | 0 | } |
515 | 0 | } |
516 | |
|
517 | 0 | if (existing != NULL) { |
518 | 0 | config_rec *c; |
519 | | |
520 | | /* If the given server config does not have any ServerAlias directives, |
521 | | * then this is an IP/port collision, and we need to return an error. |
522 | | * |
523 | | * However, if there are ServerAliases, this is a name-based config and |
524 | | * should be processed properly. |
525 | | */ |
526 | |
|
527 | 0 | c = find_config(server->conf, CONF_PARAM, "ServerAlias", FALSE); |
528 | 0 | if (c == NULL) { |
529 | 0 | pr_log_pri(PR_LOG_WARNING, "notice: '%s' (%s:%u) already bound to '%s'", |
530 | 0 | server->ServerName, pr_netaddr_get_ipstr(addr), port, |
531 | 0 | existing->ib_server->ServerName); |
532 | 0 | errno = EADDRINUSE; |
533 | 0 | return -1; |
534 | 0 | } |
535 | | |
536 | 0 | pr_log_debug(DEBUG9, "notice: '%s' (%s:%u) already bound to '%s'", |
537 | 0 | server->ServerName, pr_netaddr_get_ipstr(addr), port, |
538 | 0 | existing->ib_server->ServerName); |
539 | 0 | } |
540 | | |
541 | 0 | if (binding_pool == NULL) { |
542 | 0 | binding_pool = make_sub_pool(permanent_pool); |
543 | 0 | pr_pool_tag(binding_pool, "Bindings Pool"); |
544 | 0 | } |
545 | |
|
546 | 0 | ipbind = pcalloc(server->pool, sizeof(pr_ipbind_t)); |
547 | 0 | ipbind->ib_server = server; |
548 | 0 | ipbind->ib_addr = addr; |
549 | 0 | ipbind->ib_port = port; |
550 | 0 | ipbind->ib_namebinds = NULL; |
551 | 0 | ipbind->ib_isdefault = FALSE; |
552 | 0 | ipbind->ib_islocalhost = FALSE; |
553 | 0 | ipbind->ib_isactive = FALSE; |
554 | |
|
555 | 0 | pr_trace_msg(trace_channel, 8, "created ipbind %p for %s#%u, server %p (%s)", |
556 | 0 | ipbind, pr_netaddr_get_ipstr(ipbind->ib_addr), ipbind->ib_port, |
557 | 0 | ipbind->ib_server, ipbind->ib_server->ServerName); |
558 | | |
559 | | /* Add the ipbind to the table, maintaining insertion order. */ |
560 | 0 | if (existing != NULL) { |
561 | 0 | pr_trace_msg(trace_channel, 19, |
562 | 0 | "found existing ipbind %p (server %p) at index %u in iptable table, " |
563 | 0 | "adding to %p", existing, existing->ib_server, i, existing); |
564 | 0 | if (existing->ib_next != NULL) { |
565 | 0 | ipbind->ib_next = existing->ib_next; |
566 | 0 | } |
567 | 0 | existing->ib_next = ipbind; |
568 | |
|
569 | 0 | } else { |
570 | 0 | if (ipbind_table[i] != NULL) { |
571 | 0 | ipbind->ib_next = ipbind_table[i]; |
572 | 0 | } |
573 | 0 | ipbind_table[i] = ipbind; |
574 | 0 | } |
575 | |
|
576 | 0 | return 0; |
577 | 0 | } |
578 | | |
579 | | /* Returns the LAST matching ipbind for the given port, if any. If provided, |
580 | | * the match_count pointer will contain the number of ipbinds found that |
581 | | * matched that port. |
582 | | */ |
583 | | static pr_ipbind_t *ipbind_find_port(unsigned int port, |
584 | 0 | unsigned char skip_inactive, unsigned int *match_count) { |
585 | 0 | register unsigned int i; |
586 | 0 | pr_ipbind_t *matched_ipbind = NULL; |
587 | |
|
588 | 0 | for (i = 0; i < PR_BINDINGS_TABLE_SIZE; i++) { |
589 | 0 | pr_ipbind_t *ipbind; |
590 | |
|
591 | 0 | for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) { |
592 | 0 | pr_signals_handle(); |
593 | |
|
594 | 0 | if (skip_inactive && |
595 | 0 | !ipbind->ib_isactive) { |
596 | 0 | continue; |
597 | 0 | } |
598 | | |
599 | 0 | if (ipbind->ib_port == port) { |
600 | 0 | if (match_count != NULL) { |
601 | 0 | (*match_count)++; |
602 | 0 | } |
603 | |
|
604 | 0 | matched_ipbind = ipbind; |
605 | 0 | } |
606 | 0 | } |
607 | 0 | } |
608 | |
|
609 | 0 | if (matched_ipbind == NULL) { |
610 | 0 | errno = ENOENT; |
611 | 0 | return NULL; |
612 | 0 | } |
613 | | |
614 | 0 | return matched_ipbind; |
615 | 0 | } |
616 | | |
617 | | pr_ipbind_t *pr_ipbind_find(const pr_netaddr_t *addr, unsigned int port, |
618 | 0 | unsigned char skip_inactive) { |
619 | 0 | register unsigned int i; |
620 | 0 | pr_ipbind_t *ipbind = NULL; |
621 | |
|
622 | 0 | if (addr == NULL) { |
623 | 0 | errno = EINVAL; |
624 | 0 | return NULL; |
625 | 0 | } |
626 | | |
627 | | /* Ensure the ipbind table has been initialized. */ |
628 | 0 | init_ipbind_table(); |
629 | |
|
630 | 0 | i = ipbind_hash_addr(addr, port); |
631 | |
|
632 | 0 | for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) { |
633 | 0 | pr_signals_handle(); |
634 | |
|
635 | 0 | if (ipbind->ib_isactive == FALSE && |
636 | 0 | skip_inactive == TRUE) { |
637 | 0 | continue; |
638 | 0 | } |
639 | | |
640 | 0 | if (pr_netaddr_cmp(ipbind->ib_addr, addr) == 0) { |
641 | 0 | if (ipbind->ib_port == 0 || |
642 | 0 | port == 0 || |
643 | 0 | ipbind->ib_port == port) { |
644 | 0 | return ipbind; |
645 | 0 | } |
646 | 0 | } |
647 | 0 | } |
648 | | |
649 | 0 | errno = ENOENT; |
650 | 0 | return NULL; |
651 | 0 | } |
652 | | |
653 | 0 | pr_ipbind_t *pr_ipbind_get(pr_ipbind_t *prev) { |
654 | 0 | static unsigned int i = 0; |
655 | |
|
656 | 0 | if (prev != NULL) { |
657 | | /* If there's another ipbind in this chain, simply return that. */ |
658 | 0 | if (prev->ib_next != NULL) { |
659 | 0 | return prev->ib_next; |
660 | 0 | } |
661 | | |
662 | | /* If the increment is at the maximum size, return NULL (no more chains |
663 | | * to be examined). |
664 | | */ |
665 | 0 | if (i == PR_BINDINGS_TABLE_SIZE) { |
666 | 0 | errno = ENOENT; |
667 | 0 | return NULL; |
668 | 0 | } |
669 | | |
670 | | /* Increment the index. At this point, we know that the given pointer is |
671 | | * the last in the chain, and that there are more chains in the table |
672 | | * to be examined. |
673 | | */ |
674 | 0 | i++; |
675 | |
|
676 | 0 | } else { |
677 | | /* Reset the index if prev is NULL. */ |
678 | 0 | i = 0; |
679 | 0 | } |
680 | | |
681 | | /* Search for the next non-empty chain in the table. */ |
682 | 0 | for (; i < PR_BINDINGS_TABLE_SIZE; i++) { |
683 | 0 | pr_signals_handle(); |
684 | |
|
685 | 0 | if (ipbind_table[i] != NULL) { |
686 | 0 | return ipbind_table[i]; |
687 | 0 | } |
688 | 0 | } |
689 | | |
690 | 0 | errno = ENOENT; |
691 | 0 | return NULL; |
692 | 0 | } |
693 | | |
694 | 0 | server_rec *pr_ipbind_get_server(const pr_netaddr_t *addr, unsigned int port) { |
695 | 0 | pr_ipbind_t *ipbind = NULL; |
696 | 0 | pr_netaddr_t wildcard_addr; |
697 | 0 | int addr_family; |
698 | 0 | unsigned int match_count = 0; |
699 | | |
700 | | /* If we've got a binding configured for this exact address, return it |
701 | | * straight away. |
702 | | */ |
703 | 0 | ipbind = pr_ipbind_find(addr, port, TRUE); |
704 | 0 | if (ipbind != NULL) { |
705 | 0 | pr_trace_msg(trace_channel, 22, |
706 | 0 | "matching ipbind %p found for %s#%u, using '%s'", ipbind, |
707 | 0 | pr_netaddr_get_ipstr(addr), port, ipbind->ib_server->ServerName); |
708 | 0 | pr_log_debug(DEBUG7, "matching vhost found for %s#%u, using '%s'", |
709 | 0 | pr_netaddr_get_ipstr(addr), port, ipbind->ib_server->ServerName); |
710 | 0 | return ipbind->ib_server; |
711 | 0 | } |
712 | | |
713 | | /* Look for a vhost bound to the wildcard address (i.e. INADDR_ANY). |
714 | | * |
715 | | * This allows for "<VirtualHost 0.0.0.0>" configurations, where the |
716 | | * IP address to which the client might connect is not known at |
717 | | * configuration time. (Usually happens when the same config file |
718 | | * is deployed to multiple machines.) |
719 | | */ |
720 | | |
721 | 0 | addr_family = pr_netaddr_get_family(addr); |
722 | |
|
723 | 0 | pr_netaddr_clear(&wildcard_addr); |
724 | 0 | pr_netaddr_set_family(&wildcard_addr, addr_family); |
725 | 0 | pr_netaddr_set_sockaddr_any(&wildcard_addr); |
726 | |
|
727 | 0 | ipbind = pr_ipbind_find(&wildcard_addr, port, TRUE); |
728 | 0 | if (ipbind != NULL) { |
729 | 0 | pr_trace_msg(trace_channel, 22, "no matching ipbind found for %s#%u, using " |
730 | 0 | "'%s' listening on wildcard ipbind %p", pr_netaddr_get_ipstr(addr), port, |
731 | 0 | ipbind->ib_server->ServerName, ipbind); |
732 | 0 | pr_log_debug(DEBUG7, "no matching vhost found for %s#%u, using " |
733 | 0 | "'%s' listening on wildcard address", pr_netaddr_get_ipstr(addr), port, |
734 | 0 | ipbind->ib_server->ServerName); |
735 | 0 | return ipbind->ib_server; |
736 | 0 | } |
737 | | |
738 | 0 | #if defined(PR_USE_IPV6) |
739 | 0 | if (addr_family == AF_INET6 && |
740 | 0 | pr_netaddr_use_ipv6()) { |
741 | | |
742 | | /* The pr_ipbind_find() probably returned NULL because there aren't any |
743 | | * <VirtualHost> sections configured explicitly for the wildcard IPv6 |
744 | | * address of "::", just the IPv4 wildcard "0.0.0.0" address. |
745 | | * |
746 | | * So try the pr_ipbind_find() again, this time using the IPv4 |
747 | | * wildcard. |
748 | | */ |
749 | 0 | pr_netaddr_clear(&wildcard_addr); |
750 | 0 | pr_netaddr_set_family(&wildcard_addr, AF_INET); |
751 | 0 | pr_netaddr_set_sockaddr_any(&wildcard_addr); |
752 | |
|
753 | 0 | ipbind = pr_ipbind_find(&wildcard_addr, port, TRUE); |
754 | 0 | if (ipbind != NULL) { |
755 | 0 | pr_log_debug(DEBUG7, "no matching vhost found for %s#%u, using " |
756 | 0 | "'%s' listening on wildcard address", pr_netaddr_get_ipstr(addr), |
757 | 0 | port, ipbind->ib_server->ServerName); |
758 | 0 | return ipbind->ib_server; |
759 | 0 | } |
760 | 0 | } |
761 | 0 | #endif /* PR_USE_IPV6 */ |
762 | | |
763 | | /* Check for any bindings that match the port. IF there is only one ONE |
764 | | * vhost which matches the requested port, use that (Bug#4251). |
765 | | */ |
766 | 0 | ipbind = ipbind_find_port(port, TRUE, &match_count); |
767 | 0 | if (ipbind != NULL) { |
768 | 0 | pr_trace_msg(trace_channel, 18, "found %u possible %s for port %u", |
769 | 0 | match_count, match_count != 1 ? "ipbinds" : "ipbind", port); |
770 | 0 | if (match_count == 1) { |
771 | 0 | return ipbind->ib_server; |
772 | 0 | } |
773 | 0 | } |
774 | | |
775 | | /* Use the default server, if set. */ |
776 | 0 | if (ipbind_default_server != NULL && |
777 | 0 | ipbind_default_server->ib_isactive) { |
778 | 0 | pr_trace_msg(trace_channel, 22, "no matching ipbind found for %s#%u, using " |
779 | 0 | "DefaultServer '%s' (%p)", pr_netaddr_get_ipstr(addr), port, |
780 | 0 | ipbind_default_server->ib_server->ServerName, ipbind_default_server); |
781 | 0 | pr_log_debug(DEBUG7, "no matching vhost found for %s#%u, using " |
782 | 0 | "DefaultServer '%s'", pr_netaddr_get_ipstr(addr), port, |
783 | 0 | ipbind_default_server->ib_server->ServerName); |
784 | 0 | return ipbind_default_server->ib_server; |
785 | 0 | } |
786 | | |
787 | | /* Not found in binding list, and no DefaultServer, so see if it's the |
788 | | * loopback address |
789 | | */ |
790 | 0 | if (pr_netaddr_is_loopback(addr) && |
791 | 0 | ipbind_localhost_server != NULL && |
792 | 0 | ipbind_localhost_server->ib_isactive) { |
793 | 0 | pr_trace_msg(trace_channel, 22, "no matching ipbind found for %s#%u, using " |
794 | 0 | "loopback ipbind %p", pr_netaddr_get_ipstr(addr), port, |
795 | 0 | ipbind_localhost_server); |
796 | 0 | return ipbind_localhost_server->ib_server; |
797 | 0 | } |
798 | | |
799 | 0 | return NULL; |
800 | 0 | } |
801 | | |
802 | 0 | int pr_ipbind_listen(fd_set *readfds) { |
803 | 0 | int listen_flags = PR_INET_LISTEN_FL_FATAL_ON_ERROR, maxfd = 0; |
804 | 0 | register unsigned int i = 0; |
805 | | |
806 | | /* sanity check */ |
807 | 0 | if (readfds == NULL) { |
808 | 0 | errno = EINVAL; |
809 | 0 | return -1; |
810 | 0 | } |
811 | | |
812 | 0 | FD_ZERO(readfds); |
813 | |
|
814 | 0 | if (binding_pool == NULL) { |
815 | 0 | binding_pool = make_sub_pool(permanent_pool); |
816 | 0 | pr_pool_tag(binding_pool, "Bindings Pool"); |
817 | 0 | } |
818 | | |
819 | | /* Reset the listener list. */ |
820 | 0 | if (listener_list == NULL) { |
821 | 0 | listener_list = make_array(binding_pool, 1, sizeof(conn_t *)); |
822 | |
|
823 | 0 | } else { |
824 | | /* Nasty hack to "clear" the list by making it think it has no |
825 | | * elements. |
826 | | */ |
827 | 0 | listener_list->nelts = 0; |
828 | 0 | } |
829 | | |
830 | | /* Slower than the hash lookup, but...we have to check each and every |
831 | | * ipbind in the table. |
832 | | */ |
833 | 0 | for (i = 0; i < PR_BINDINGS_TABLE_SIZE; i++) { |
834 | 0 | pr_ipbind_t *ipbind = NULL; |
835 | |
|
836 | 0 | for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) { |
837 | 0 | pr_signals_handle(); |
838 | | |
839 | | /* Skip inactive bindings, but only if SocketBindTight is in effect. */ |
840 | 0 | if (SocketBindTight && |
841 | 0 | ipbind->ib_isactive == FALSE) { |
842 | 0 | continue; |
843 | 0 | } |
844 | | |
845 | 0 | if (ipbind->ib_listener != NULL) { |
846 | 0 | if (ipbind->ib_listener->mode == CM_NONE) { |
847 | 0 | pr_inet_listen(ipbind->ib_listener->pool, ipbind->ib_listener, |
848 | 0 | tcpBackLog, listen_flags); |
849 | 0 | } |
850 | |
|
851 | 0 | if (ipbind->ib_listener->mode == CM_ACCEPT) { |
852 | 0 | if (pr_inet_resetlisten(ipbind->ib_listener->pool, |
853 | 0 | ipbind->ib_listener) < 0) { |
854 | 0 | pr_trace_msg(trace_channel, 3, |
855 | 0 | "error resetting %s#%u for listening: %s", |
856 | 0 | pr_netaddr_get_ipstr(ipbind->ib_addr), ipbind->ib_port, |
857 | 0 | strerror(errno)); |
858 | 0 | } |
859 | 0 | } |
860 | |
|
861 | 0 | if (ipbind->ib_listener->mode == CM_LISTEN) { |
862 | 0 | FD_SET(ipbind->ib_listener->listen_fd, readfds); |
863 | 0 | if (ipbind->ib_listener->listen_fd > maxfd) { |
864 | 0 | maxfd = ipbind->ib_listener->listen_fd; |
865 | 0 | } |
866 | | |
867 | | /* Add this to the listener list as well. */ |
868 | 0 | *((conn_t **) push_array(listener_list)) = ipbind->ib_listener; |
869 | 0 | } |
870 | 0 | } |
871 | 0 | } |
872 | 0 | } |
873 | |
|
874 | 0 | return maxfd; |
875 | 0 | } |
876 | | |
877 | | int pr_ipbind_open(const pr_netaddr_t *addr, unsigned int port, |
878 | | conn_t *listen_conn, unsigned char isdefault, unsigned char islocalhost, |
879 | 0 | unsigned char open_namebinds) { |
880 | 0 | pr_ipbind_t *ipbind = NULL; |
881 | |
|
882 | 0 | if (addr == NULL) { |
883 | 0 | errno = EINVAL; |
884 | 0 | return -1; |
885 | 0 | } |
886 | | |
887 | | /* Find the binding for this server/address */ |
888 | 0 | ipbind = pr_ipbind_find(addr, port, FALSE); |
889 | 0 | if (ipbind == NULL) { |
890 | 0 | errno = ENOENT; |
891 | 0 | return -1; |
892 | 0 | } |
893 | | |
894 | 0 | if (listen_conn != NULL) { |
895 | 0 | listen_conn->next = NULL; |
896 | 0 | } |
897 | |
|
898 | 0 | ipbind->ib_listener = ipbind->ib_server->listen = listen_conn; |
899 | 0 | ipbind->ib_listener = listen_conn; |
900 | 0 | ipbind->ib_isdefault = isdefault; |
901 | 0 | ipbind->ib_islocalhost = islocalhost; |
902 | | |
903 | | /* Stash a pointer to this ipbind, since it is designated as the |
904 | | * default server (via the DefaultServer directive), for use in the |
905 | | * lookup functions. |
906 | | * |
907 | | * Stash pointers to this ipbind for use in the lookup functions if: |
908 | | * |
909 | | * - It's the default server (specified via the DefaultServer directive) |
910 | | * - It handles connections to the loopback interface |
911 | | */ |
912 | 0 | if (isdefault) { |
913 | 0 | ipbind_default_server = ipbind; |
914 | 0 | } |
915 | |
|
916 | 0 | if (islocalhost) { |
917 | 0 | ipbind_localhost_server = ipbind; |
918 | 0 | } |
919 | | |
920 | | /* If requested, look for any namebinds for this ipbind, and open them. */ |
921 | 0 | if (open_namebinds && |
922 | 0 | ipbind->ib_namebinds) { |
923 | 0 | register unsigned int i = 0; |
924 | 0 | pr_namebind_t **namebinds = NULL; |
925 | | |
926 | | /* NOTE: in the future, these namebinds may need to be stored/ |
927 | | * manipulated in hash tables themselves, but, for now, linked lists |
928 | | * should suffice. |
929 | | */ |
930 | 0 | namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts; |
931 | 0 | for (i = 0; i < ipbind->ib_namebinds->nelts; i++) { |
932 | 0 | int res; |
933 | 0 | pr_namebind_t *nb; |
934 | |
|
935 | 0 | pr_signals_handle(); |
936 | |
|
937 | 0 | nb = namebinds[i]; |
938 | 0 | res = pr_namebind_open(nb->nb_name, nb->nb_server->addr, |
939 | 0 | nb->nb_server_port); |
940 | 0 | if (res < 0) { |
941 | 0 | pr_trace_msg(trace_channel, 2, |
942 | 0 | "notice: unable to open namebind '%s': %s", nb->nb_name, |
943 | 0 | strerror(errno)); |
944 | 0 | } |
945 | 0 | } |
946 | 0 | } |
947 | | |
948 | | /* Mark this binding as now being active. */ |
949 | 0 | ipbind->ib_isactive = TRUE; |
950 | |
|
951 | 0 | return 0; |
952 | 0 | } |
953 | | |
954 | 0 | int pr_namebind_close(const char *name, const pr_netaddr_t *addr) { |
955 | 0 | pr_namebind_t *namebind = NULL; |
956 | 0 | unsigned int port; |
957 | |
|
958 | 0 | if (name == NULL|| |
959 | 0 | addr == NULL) { |
960 | 0 | errno = EINVAL; |
961 | 0 | return -1; |
962 | 0 | } |
963 | | |
964 | 0 | port = ntohs(pr_netaddr_get_port(addr)); |
965 | 0 | namebind = pr_namebind_find(name, addr, port, FALSE); |
966 | 0 | if (namebind == NULL) { |
967 | 0 | errno = ENOENT; |
968 | 0 | return -1; |
969 | 0 | } |
970 | | |
971 | 0 | namebind->nb_isactive = FALSE; |
972 | 0 | return 0; |
973 | 0 | } |
974 | | |
975 | | int pr_namebind_create(server_rec *server, const char *name, |
976 | 0 | pr_ipbind_t *ipbind, const pr_netaddr_t *addr, unsigned int server_port) { |
977 | 0 | pr_namebind_t *namebind = NULL, **namebinds = NULL; |
978 | 0 | unsigned int port; |
979 | |
|
980 | 0 | if (server == NULL || |
981 | 0 | name == NULL) { |
982 | 0 | errno = EINVAL; |
983 | 0 | return -1; |
984 | 0 | } |
985 | | |
986 | | /* First, find the ipbind to hold this namebind. */ |
987 | 0 | port = ntohs(pr_netaddr_get_port(addr)); |
988 | 0 | if (port == 0) { |
989 | 0 | port = server_port; |
990 | 0 | } |
991 | |
|
992 | 0 | if (ipbind == NULL) { |
993 | 0 | pr_netaddr_t wildcard_addr; |
994 | 0 | int addr_family; |
995 | | |
996 | | /* If not found, look for the wildcard address. */ |
997 | |
|
998 | 0 | addr_family = pr_netaddr_get_family(addr); |
999 | 0 | pr_netaddr_clear(&wildcard_addr); |
1000 | 0 | pr_netaddr_set_family(&wildcard_addr, addr_family); |
1001 | 0 | pr_netaddr_set_sockaddr_any(&wildcard_addr); |
1002 | |
|
1003 | 0 | ipbind = pr_ipbind_find(&wildcard_addr, port, FALSE); |
1004 | 0 | #if defined(PR_USE_IPV6) |
1005 | 0 | if (ipbind == FALSE && |
1006 | 0 | addr_family == AF_INET6 && |
1007 | 0 | pr_netaddr_use_ipv6()) { |
1008 | | |
1009 | | /* No IPv6 wildcard address found; try the IPv4 wildcard address. */ |
1010 | 0 | pr_netaddr_clear(&wildcard_addr); |
1011 | 0 | pr_netaddr_set_family(&wildcard_addr, AF_INET); |
1012 | 0 | pr_netaddr_set_sockaddr_any(&wildcard_addr); |
1013 | |
|
1014 | 0 | ipbind = pr_ipbind_find(&wildcard_addr, port, FALSE); |
1015 | 0 | } |
1016 | 0 | #endif /* PR_USE_IPV6 */ |
1017 | |
|
1018 | 0 | pr_trace_msg(trace_channel, 19, |
1019 | 0 | "found wildcard ipbind %p for namebind (name '%s', addr %s, port %u)", |
1020 | 0 | ipbind, name, pr_netaddr_get_ipstr(addr), port); |
1021 | 0 | } |
1022 | |
|
1023 | 0 | if (ipbind == NULL) { |
1024 | 0 | errno = ENOENT; |
1025 | 0 | return -1; |
1026 | 0 | } |
1027 | | |
1028 | | /* Make sure we can add this namebind. */ |
1029 | 0 | if (ipbind->ib_namebinds == NULL) { |
1030 | 0 | ipbind->ib_namebinds = make_array(binding_pool, 0, sizeof(pr_namebind_t *)); |
1031 | |
|
1032 | 0 | } else { |
1033 | 0 | register unsigned int i = 0; |
1034 | 0 | namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts; |
1035 | | |
1036 | | /* See if there is already a namebind for the given name. */ |
1037 | 0 | for (i = 0; i < ipbind->ib_namebinds->nelts; i++) { |
1038 | 0 | pr_signals_handle(); |
1039 | |
|
1040 | 0 | namebind = namebinds[i]; |
1041 | 0 | if (namebind != NULL && |
1042 | 0 | namebind->nb_name != NULL) { |
1043 | | |
1044 | | /* DNS names are case-insensitive, hence the case-insensitive check |
1045 | | * here. |
1046 | | * |
1047 | | * XXX Ideally, we should check whether any existing namebinds which |
1048 | | * are globs will match the newly added namebind as well. |
1049 | | */ |
1050 | |
|
1051 | 0 | if (strcasecmp(namebind->nb_name, name) == 0) { |
1052 | 0 | errno = EEXIST; |
1053 | 0 | return -1; |
1054 | 0 | } |
1055 | 0 | } |
1056 | 0 | } |
1057 | 0 | } |
1058 | | |
1059 | 0 | namebind = (pr_namebind_t *) pcalloc(server->pool, sizeof(pr_namebind_t)); |
1060 | 0 | namebind->nb_name = name; |
1061 | 0 | namebind->nb_server = server; |
1062 | 0 | namebind->nb_server_port = server_port; |
1063 | 0 | namebind->nb_isactive = FALSE; |
1064 | |
|
1065 | 0 | if (pr_str_is_fnmatch(name) == TRUE) { |
1066 | 0 | namebind->nb_iswildcard = TRUE; |
1067 | 0 | } |
1068 | |
|
1069 | 0 | pr_trace_msg(trace_channel, 8, |
1070 | 0 | "created namebind '%s' for %s#%u, server '%s' [%p]", name, |
1071 | 0 | pr_netaddr_get_ipstr(server->addr), server->ServerPort, server->ServerName, |
1072 | 0 | server); |
1073 | | |
1074 | | /* The given server should already have the following populated: |
1075 | | * |
1076 | | * server->ServerName |
1077 | | * server->ServerAddress |
1078 | | * server->ServerFQDN |
1079 | | */ |
1080 | |
|
1081 | 0 | *((pr_namebind_t **) push_array(ipbind->ib_namebinds)) = namebind; |
1082 | 0 | return 0; |
1083 | 0 | } |
1084 | | |
1085 | | pr_namebind_t *pr_namebind_find(const char *name, const pr_netaddr_t *addr, |
1086 | 0 | unsigned int port, unsigned char skip_inactive) { |
1087 | 0 | pr_ipbind_t *ipbind = NULL, *iter; |
1088 | |
|
1089 | 0 | if (name == NULL || |
1090 | 0 | addr == NULL) { |
1091 | 0 | errno = EINVAL; |
1092 | 0 | return NULL; |
1093 | 0 | } |
1094 | | |
1095 | | /* First, find an active ipbind for the given address. */ |
1096 | 0 | ipbind = pr_ipbind_find(addr, port, skip_inactive); |
1097 | 0 | if (ipbind == NULL) { |
1098 | 0 | pr_netaddr_t wildcard_addr; |
1099 | 0 | int addr_family; |
1100 | | |
1101 | | /* If not found, look for the wildcard address. */ |
1102 | |
|
1103 | 0 | addr_family = pr_netaddr_get_family(addr); |
1104 | 0 | pr_netaddr_clear(&wildcard_addr); |
1105 | 0 | pr_netaddr_set_family(&wildcard_addr, addr_family); |
1106 | 0 | pr_netaddr_set_sockaddr_any(&wildcard_addr); |
1107 | |
|
1108 | 0 | ipbind = pr_ipbind_find(&wildcard_addr, port, FALSE); |
1109 | 0 | #if defined(PR_USE_IPV6) |
1110 | 0 | if (ipbind == FALSE && |
1111 | 0 | addr_family == AF_INET6 && |
1112 | 0 | pr_netaddr_use_ipv6()) { |
1113 | | |
1114 | | /* No IPv6 wildcard address found; try the IPv4 wildcard address. */ |
1115 | 0 | pr_netaddr_clear(&wildcard_addr); |
1116 | 0 | pr_netaddr_set_family(&wildcard_addr, AF_INET); |
1117 | 0 | pr_netaddr_set_sockaddr_any(&wildcard_addr); |
1118 | |
|
1119 | 0 | ipbind = pr_ipbind_find(&wildcard_addr, port, FALSE); |
1120 | 0 | } |
1121 | 0 | #endif /* PR_USE_IPV6 */ |
1122 | 0 | } else { |
1123 | 0 | pr_trace_msg(trace_channel, 17, |
1124 | 0 | "found ipbind %p (server '%s' [%p]) for %s#%u", ipbind, |
1125 | 0 | ipbind->ib_server->ServerName, ipbind->ib_server, |
1126 | 0 | pr_netaddr_get_ipstr(addr), port); |
1127 | 0 | } |
1128 | |
|
1129 | 0 | if (ipbind == NULL) { |
1130 | 0 | errno = ENOENT; |
1131 | 0 | return NULL; |
1132 | 0 | } |
1133 | | |
1134 | | /* Now we need to search this ipbind list, to see if any of them have a |
1135 | | * matching namebind. |
1136 | | */ |
1137 | | |
1138 | 0 | for (iter = ipbind; iter; iter = iter->ib_next) { |
1139 | 0 | register unsigned int i = 0; |
1140 | 0 | pr_namebind_t *namebind = NULL, **namebinds = NULL; |
1141 | |
|
1142 | 0 | pr_signals_handle(); |
1143 | |
|
1144 | 0 | if (iter->ib_namebinds == NULL) { |
1145 | 0 | pr_trace_msg(trace_channel, 17, |
1146 | 0 | "ipbind %p (server %p) for %s#%u has no namebinds", iter, |
1147 | 0 | iter->ib_server, pr_netaddr_get_ipstr(addr), port); |
1148 | 0 | continue; |
1149 | 0 | } |
1150 | | |
1151 | 0 | namebinds = (pr_namebind_t **) iter->ib_namebinds->elts; |
1152 | 0 | pr_trace_msg(trace_channel, 17, |
1153 | 0 | "ipbind %p (server %p) for %s#%u has namebinds (%d)", iter, |
1154 | 0 | iter->ib_server, pr_netaddr_get_ipstr(addr), port, |
1155 | 0 | iter->ib_namebinds->nelts); |
1156 | |
|
1157 | 0 | for (i = 0; i < iter->ib_namebinds->nelts; i++) { |
1158 | 0 | pr_signals_handle(); |
1159 | |
|
1160 | 0 | namebind = namebinds[i]; |
1161 | 0 | if (namebind == NULL) { |
1162 | 0 | continue; |
1163 | 0 | } |
1164 | | |
1165 | | /* Skip inactive namebinds */ |
1166 | 0 | if (skip_inactive == TRUE && |
1167 | 0 | namebind->nb_isactive == FALSE) { |
1168 | 0 | pr_trace_msg(trace_channel, 17, |
1169 | 0 | "namebind #%u: %s is inactive, skipping", i, namebind->nb_name); |
1170 | 0 | continue; |
1171 | 0 | } |
1172 | | |
1173 | | /* At present, this looks for an exactly matching name. In the future, |
1174 | | * we may want to have something like Apache's matching scheme, which |
1175 | | * looks for the most specific domain to the most general. Note that |
1176 | | * that scheme, however, is specific to DNS; should any other naming |
1177 | | * scheme be desired, that sort of matching will be unnecessary. |
1178 | | */ |
1179 | 0 | if (namebind->nb_name != NULL) { |
1180 | 0 | pr_trace_msg(trace_channel, 17, |
1181 | 0 | "namebind #%u: %s (%s)", i, namebind->nb_name, |
1182 | 0 | namebind->nb_isactive ? "active" : "inactive"); |
1183 | |
|
1184 | 0 | if (namebind->nb_iswildcard == FALSE) { |
1185 | 0 | if (strcasecmp(namebind->nb_name, name) == 0) { |
1186 | 0 | return namebind; |
1187 | 0 | } |
1188 | |
|
1189 | 0 | } else { |
1190 | 0 | int match_flags = PR_FNM_NOESCAPE|PR_FNM_CASEFOLD; |
1191 | |
|
1192 | 0 | if (pr_fnmatch(namebind->nb_name, name, match_flags) == 0) { |
1193 | 0 | pr_trace_msg(trace_channel, 9, |
1194 | 0 | "matched name '%s' against pattern '%s'", name, |
1195 | 0 | namebind->nb_name); |
1196 | 0 | return namebind; |
1197 | 0 | } |
1198 | | |
1199 | 0 | pr_trace_msg(trace_channel, 9, |
1200 | 0 | "failed to match name '%s' against pattern '%s'", name, |
1201 | 0 | namebind->nb_name); |
1202 | 0 | } |
1203 | 0 | } |
1204 | 0 | } |
1205 | 0 | } |
1206 | | |
1207 | 0 | return NULL; |
1208 | 0 | } |
1209 | | |
1210 | | server_rec *pr_namebind_get_server(const char *name, const pr_netaddr_t *addr, |
1211 | 0 | unsigned int port) { |
1212 | 0 | pr_namebind_t *namebind = NULL; |
1213 | | |
1214 | | /* Basically, just a wrapper around pr_namebind_find() */ |
1215 | |
|
1216 | 0 | namebind = pr_namebind_find(name, addr, port, TRUE); |
1217 | 0 | if (namebind == NULL) { |
1218 | 0 | return NULL; |
1219 | 0 | } |
1220 | | |
1221 | 0 | return namebind->nb_server; |
1222 | 0 | } |
1223 | | |
1224 | 0 | unsigned int pr_namebind_count(server_rec *srv) { |
1225 | 0 | unsigned int count = 0; |
1226 | 0 | pr_ipbind_t *ipbind = NULL; |
1227 | |
|
1228 | 0 | if (srv == NULL) { |
1229 | 0 | return 0; |
1230 | 0 | } |
1231 | | |
1232 | 0 | ipbind = pr_ipbind_find(srv->addr, srv->ServerPort, FALSE); |
1233 | 0 | if (ipbind != NULL && |
1234 | 0 | ipbind->ib_namebinds != NULL) { |
1235 | 0 | count = ipbind->ib_namebinds->nelts; |
1236 | 0 | } |
1237 | |
|
1238 | 0 | return count; |
1239 | 0 | } |
1240 | | |
1241 | | int pr_namebind_open(const char *name, const pr_netaddr_t *addr, |
1242 | 0 | unsigned int server_port) { |
1243 | 0 | pr_namebind_t *namebind = NULL; |
1244 | 0 | unsigned int port; |
1245 | |
|
1246 | 0 | if (name == NULL || |
1247 | 0 | addr == NULL) { |
1248 | 0 | errno = EINVAL; |
1249 | 0 | return -1; |
1250 | 0 | } |
1251 | | |
1252 | 0 | port = ntohs(pr_netaddr_get_port(addr)); |
1253 | 0 | if (port == 0) { |
1254 | 0 | port = server_port; |
1255 | 0 | } |
1256 | |
|
1257 | 0 | namebind = pr_namebind_find(name, addr, port, FALSE); |
1258 | 0 | if (namebind == NULL) { |
1259 | 0 | errno = ENOENT; |
1260 | 0 | return -1; |
1261 | 0 | } |
1262 | | |
1263 | 0 | namebind->nb_isactive = TRUE; |
1264 | 0 | return 0; |
1265 | 0 | } |
1266 | | |
1267 | 0 | void free_bindings(void) { |
1268 | 0 | if (binding_pool) { |
1269 | 0 | destroy_pool(binding_pool); |
1270 | 0 | binding_pool = NULL; |
1271 | 0 | listener_list = NULL; |
1272 | 0 | } |
1273 | |
|
1274 | 0 | memset(ipbind_table, 0, sizeof(ipbind_table)); |
1275 | 0 | ipbind_table_initialized = FALSE; |
1276 | | |
1277 | | /* Mark all listening conns as "unclaimed"; any that remaining unclaimed |
1278 | | * after init_bindings() can be closed. |
1279 | | */ |
1280 | 0 | if (listening_conn_list != NULL) { |
1281 | 0 | struct listener_rec *lr; |
1282 | |
|
1283 | 0 | for (lr = (struct listener_rec *) listening_conn_list->xas_list; lr; lr = lr->next) { |
1284 | 0 | pr_signals_handle(); |
1285 | 0 | lr->claimed = FALSE; |
1286 | 0 | } |
1287 | 0 | } |
1288 | 0 | } |
1289 | | |
1290 | 0 | static int init_inetd_bindings(void) { |
1291 | 0 | int res = 0; |
1292 | 0 | server_rec *serv = NULL; |
1293 | 0 | unsigned char *default_server = NULL, is_default = FALSE; |
1294 | | |
1295 | | /* We explicitly do NOT use the get_listening_conn() function here, since |
1296 | | * inetd-run daemons will not a) handle restarts, and thus b) will not have |
1297 | | * already-open connections to choose from. |
1298 | | */ |
1299 | |
|
1300 | 0 | main_server->listen = pr_inet_create_conn(main_server->pool, STDIN_FILENO, |
1301 | 0 | NULL, INPORT_ANY, FALSE); |
1302 | 0 | if (main_server->listen == NULL) { |
1303 | 0 | return -1; |
1304 | 0 | } |
1305 | | |
1306 | | /* Note: Since we are being called via inetd/xinetd, any socket options |
1307 | | * which may be attempted by listeners for this event may not work. |
1308 | | */ |
1309 | 0 | pr_inet_generate_socket_event("core.ctrl-listen", main_server, |
1310 | 0 | main_server->addr, main_server->listen->listen_fd); |
1311 | | |
1312 | | /* Fill in all the important connection information. */ |
1313 | 0 | if (pr_inet_get_conn_info(main_server->listen, STDIN_FILENO) == -1) { |
1314 | 0 | int xerrno = errno; |
1315 | |
|
1316 | 0 | pr_log_pri(PR_LOG_WARNING, "fatal: unable to get connection info: %s", |
1317 | 0 | strerror(xerrno)); |
1318 | |
|
1319 | 0 | if (xerrno == ENOTSOCK) { |
1320 | 0 | pr_log_pri(PR_LOG_ERR, "(Running from command line? " |
1321 | 0 | "Use `ServerType standalone' in config file!)"); |
1322 | 0 | } |
1323 | |
|
1324 | 0 | exit(1); |
1325 | 0 | } |
1326 | | |
1327 | 0 | default_server = get_param_ptr(main_server->conf, "DefaultServer", FALSE); |
1328 | 0 | if (default_server != NULL && |
1329 | 0 | *default_server == TRUE) { |
1330 | 0 | is_default = TRUE; |
1331 | 0 | } |
1332 | |
|
1333 | 0 | res = pr_ipbind_create(main_server, main_server->addr, |
1334 | 0 | main_server->ServerPort); |
1335 | 0 | if (res < 0) { |
1336 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1337 | 0 | "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__, __LINE__, |
1338 | 0 | main_server->ServerAddress, main_server->ServerPort, strerror(errno)); |
1339 | 0 | } |
1340 | |
|
1341 | 0 | res = pr_ipbind_open(main_server->addr, main_server->ServerPort, |
1342 | 0 | main_server->listen, is_default, TRUE, TRUE); |
1343 | 0 | if (res < 0) { |
1344 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1345 | 0 | "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__, |
1346 | 0 | pr_netaddr_get_ipstr(main_server->addr), strerror(errno)); |
1347 | 0 | } |
1348 | |
|
1349 | 0 | res = pr_ipbind_add_binds(main_server); |
1350 | 0 | if (res < 0) { |
1351 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1352 | 0 | "%s:%d: notice: unable to add binds to ipbind '%s': %s", __FILE__, |
1353 | 0 | __LINE__, main_server->ServerAddress, strerror(errno)); |
1354 | 0 | } |
1355 | |
|
1356 | 0 | (void) process_serveralias(main_server); |
1357 | | |
1358 | | /* Now attach the faked connection to all virtual servers. */ |
1359 | 0 | for (serv = main_server->next; serv; serv = serv->next) { |
1360 | | |
1361 | | /* Because this server is sharing the connection with the |
1362 | | * main server, we need a cleanup handler to remove |
1363 | | * the server's reference when the original connection's |
1364 | | * pool is destroyed. |
1365 | | */ |
1366 | |
|
1367 | 0 | serv->listen = main_server->listen; |
1368 | 0 | register_cleanup2(serv->listen->pool, &serv->listen, server_cleanup_cb); |
1369 | |
|
1370 | 0 | is_default = FALSE; |
1371 | 0 | default_server = get_param_ptr(serv->conf, "DefaultServer", FALSE); |
1372 | 0 | if (default_server != NULL && |
1373 | 0 | *default_server == TRUE) { |
1374 | 0 | is_default = TRUE; |
1375 | 0 | } |
1376 | |
|
1377 | 0 | res = pr_ipbind_create(serv, serv->addr, serv->ServerPort); |
1378 | 0 | if (res < 0) { |
1379 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1380 | 0 | "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__, |
1381 | 0 | __LINE__, serv->ServerAddress, serv->ServerPort, strerror(errno)); |
1382 | 0 | } |
1383 | |
|
1384 | 0 | res = pr_ipbind_open(serv->addr, serv->ServerPort, serv->listen, |
1385 | 0 | is_default, FALSE, TRUE); |
1386 | 0 | if (res < 0) { |
1387 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1388 | 0 | "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__, |
1389 | 0 | pr_netaddr_get_ipstr(serv->addr), strerror(errno)); |
1390 | 0 | } |
1391 | |
|
1392 | 0 | res = pr_ipbind_add_binds(serv); |
1393 | 0 | if (res < 0) { |
1394 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1395 | 0 | "%s:%d: notice: unable to add binds to ipbind '%s': %s", __FILE__, |
1396 | 0 | __LINE__, serv->ServerAddress, strerror(errno)); |
1397 | 0 | } |
1398 | |
|
1399 | 0 | (void) process_serveralias(serv); |
1400 | 0 | } |
1401 | |
|
1402 | 0 | return 0; |
1403 | 0 | } |
1404 | | |
1405 | 0 | static array_header *find_server_ipbinds(pool *p, server_rec *s) { |
1406 | 0 | register unsigned int i; |
1407 | 0 | array_header *ipbinds = NULL; |
1408 | |
|
1409 | 0 | for (i = 0; i < PR_BINDINGS_TABLE_SIZE; i++) { |
1410 | 0 | pr_ipbind_t *ipbind; |
1411 | |
|
1412 | 0 | for (ipbind = ipbind_table[i]; ipbind != NULL; ipbind = ipbind->ib_next) { |
1413 | 0 | pr_signals_handle(); |
1414 | |
|
1415 | 0 | if (ipbind->ib_server == s) { |
1416 | 0 | if (ipbinds == NULL) { |
1417 | 0 | ipbinds = make_array(p, 16, sizeof(pr_ipbind_t *)); |
1418 | 0 | } |
1419 | |
|
1420 | 0 | *((pr_ipbind_t **) push_array(ipbinds)) = ipbind; |
1421 | 0 | } |
1422 | 0 | } |
1423 | 0 | } |
1424 | |
|
1425 | 0 | return ipbinds; |
1426 | 0 | } |
1427 | | |
1428 | 0 | static unsigned int process_serveralias(server_rec *s) { |
1429 | 0 | unsigned namebind_count = 0; |
1430 | 0 | config_rec *c; |
1431 | 0 | pr_ipbind_t *ipbind; |
1432 | 0 | array_header *ipbinds; |
1433 | 0 | pool *tmp_pool; |
1434 | | |
1435 | | /* If there is no ipbind already for this server, we cannot associate |
1436 | | * any ServerAlias-based namebinds to it. |
1437 | | * |
1438 | | * Keep in mind that there may be multiple ipbinds pointed at this server: |
1439 | | * |
1440 | | * <VirtualHost 1.2.3.4 5.6.7.8> |
1441 | | * ServerAlias alias |
1442 | | * </VirtualHost> |
1443 | | * |
1444 | | * And that multiple namebinds can point to the same ipbind for this server: |
1445 | | * |
1446 | | * <VirtualHost 1.2.3.4> |
1447 | | * ServerAlias first |
1448 | | * </VirtualHost> |
1449 | | * |
1450 | | * <VirtualHost 2.3.4.5> |
1451 | | * ServerAlias second |
1452 | | * </VirtualHost> |
1453 | | */ |
1454 | |
|
1455 | 0 | tmp_pool = make_sub_pool(s->pool); |
1456 | 0 | pr_pool_tag(tmp_pool, "ServerAlias Processing Pool"); |
1457 | | |
1458 | | /* Remember that this will return cases where port is zero, too. */ |
1459 | 0 | ipbind = pr_ipbind_find(s->addr, s->ServerPort, FALSE); |
1460 | 0 | if (ipbind != NULL && |
1461 | 0 | ipbind->ib_server->ServerPort == s->ServerPort) { |
1462 | 0 | ipbinds = make_array(tmp_pool, 1, sizeof(pr_ipbind_t *)); |
1463 | 0 | *((pr_ipbind_t **) push_array(ipbinds)) = ipbind; |
1464 | |
|
1465 | 0 | } else { |
1466 | 0 | ipbinds = find_server_ipbinds(tmp_pool, s); |
1467 | 0 | if (ipbinds == NULL) { |
1468 | 0 | destroy_pool(tmp_pool); |
1469 | 0 | return 0; |
1470 | 0 | } |
1471 | 0 | } |
1472 | | |
1473 | 0 | c = find_config(s->conf, CONF_PARAM, "ServerAlias", FALSE); |
1474 | 0 | while (c != NULL) { |
1475 | 0 | register unsigned int i; |
1476 | 0 | int res; |
1477 | 0 | pr_ipbind_t **elts; |
1478 | |
|
1479 | 0 | pr_signals_handle(); |
1480 | |
|
1481 | 0 | pr_trace_msg(trace_channel, 7, "handling ipbinds (%d) for ServerAlias '%s'", |
1482 | 0 | ipbinds->nelts, (char *) c->argv[0]); |
1483 | |
|
1484 | 0 | elts = ipbinds->elts; |
1485 | 0 | for (i = 0; i < ipbinds->nelts; i++) { |
1486 | 0 | pr_signals_handle(); |
1487 | |
|
1488 | 0 | ipbind = elts[i]; |
1489 | 0 | pr_trace_msg(trace_channel, 7, "adding ServerAlias '%s' to server '%s'", |
1490 | 0 | (char *) c->argv[0], s->ServerName); |
1491 | |
|
1492 | 0 | res = pr_namebind_create(s, c->argv[0], ipbind, s->addr, s->ServerPort); |
1493 | 0 | if (res == 0) { |
1494 | 0 | namebind_count++; |
1495 | |
|
1496 | 0 | res = pr_namebind_open(c->argv[0], ipbind->ib_addr, ipbind->ib_port); |
1497 | 0 | if (res < 0) { |
1498 | 0 | pr_trace_msg(trace_channel, 2, |
1499 | 0 | "notice: unable to open namebind '%s': %s", (char *) c->argv[0], |
1500 | 0 | strerror(errno)); |
1501 | 0 | } |
1502 | |
|
1503 | 0 | } else { |
1504 | 0 | if (errno != ENOENT) { |
1505 | 0 | pr_trace_msg(trace_channel, 3, |
1506 | 0 | "unable to create namebind for '%s' to %s#%u: %s", |
1507 | 0 | (char *) c->argv[0], pr_netaddr_get_ipstr(ipbind->ib_addr), |
1508 | 0 | ipbind->ib_port, strerror(errno)); |
1509 | 0 | } |
1510 | 0 | } |
1511 | 0 | } |
1512 | |
|
1513 | 0 | c = find_config_next(c, c->next, CONF_PARAM, "ServerAlias", FALSE); |
1514 | 0 | } |
1515 | |
|
1516 | 0 | destroy_pool(tmp_pool); |
1517 | 0 | return namebind_count; |
1518 | 0 | } |
1519 | | |
1520 | 0 | static void trace_ipbind_table(void) { |
1521 | 0 | register unsigned int i; |
1522 | |
|
1523 | 0 | if (pr_trace_get_level(trace_channel) < 25) { |
1524 | 0 | return; |
1525 | 0 | } |
1526 | | |
1527 | 0 | pr_trace_msg(trace_channel, 25, "displaying ipbind table:"); |
1528 | 0 | for (i = 0; i < PR_BINDINGS_TABLE_SIZE; i++) { |
1529 | 0 | pr_ipbind_t *ipbind; |
1530 | |
|
1531 | 0 | pr_signals_handle(); |
1532 | |
|
1533 | 0 | if (ipbind_table[i] == NULL) { |
1534 | 0 | continue; |
1535 | 0 | } |
1536 | | |
1537 | 0 | pr_trace_msg(trace_channel, 25, " index %u:", i); |
1538 | 0 | for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) { |
1539 | 0 | array_header *namebinds; |
1540 | |
|
1541 | 0 | pr_signals_handle(); |
1542 | 0 | namebinds = ipbind->ib_namebinds; |
1543 | |
|
1544 | 0 | pr_trace_msg(trace_channel, 25, " ipbind %p:", ipbind); |
1545 | 0 | pr_trace_msg(trace_channel, 25, " address: %s#%u", |
1546 | 0 | pr_netaddr_get_ipstr(ipbind->ib_addr), ipbind->ib_port); |
1547 | 0 | pr_trace_msg(trace_channel, 25, " server: %s (%p)", |
1548 | 0 | ipbind->ib_server->ServerName, ipbind->ib_server); |
1549 | 0 | pr_trace_msg(trace_channel, 25, " active: %s", |
1550 | 0 | ipbind->ib_isactive ? "TRUE" : "FALSE"); |
1551 | |
|
1552 | 0 | if (namebinds != NULL) { |
1553 | 0 | register unsigned int k; |
1554 | 0 | pr_namebind_t **elts; |
1555 | |
|
1556 | 0 | pr_trace_msg(trace_channel, 25, " namebinds:"); |
1557 | 0 | elts = namebinds->elts; |
1558 | 0 | for (k = 0; k < namebinds->nelts; k++) { |
1559 | 0 | pr_namebind_t *namebind; |
1560 | |
|
1561 | 0 | pr_signals_handle(); |
1562 | 0 | namebind = elts[k]; |
1563 | 0 | pr_trace_msg(trace_channel, 25, " #%u: %p", k+1, namebind); |
1564 | 0 | pr_trace_msg(trace_channel, 25, " name: %s", |
1565 | 0 | namebind->nb_server->ServerName); |
1566 | 0 | pr_trace_msg(trace_channel, 25, " alias: %s", |
1567 | 0 | namebind->nb_name); |
1568 | 0 | pr_trace_msg(trace_channel, 25, " server: %p", |
1569 | 0 | namebind->nb_server); |
1570 | 0 | pr_trace_msg(trace_channel, 25, " active: %s", |
1571 | 0 | namebind->nb_isactive ? "TRUE" : "FALSE"); |
1572 | 0 | } |
1573 | 0 | } |
1574 | 0 | } |
1575 | 0 | } |
1576 | 0 | } |
1577 | | |
1578 | 0 | static int init_standalone_bindings(void) { |
1579 | 0 | int res = 0; |
1580 | 0 | server_rec *serv = NULL; |
1581 | 0 | unsigned char *default_server = NULL, is_default = FALSE; |
1582 | | |
1583 | | /* Ensure the ipbind table has been initialized. */ |
1584 | 0 | init_ipbind_table(); |
1585 | | |
1586 | | /* If a port is set to zero, the address/port is not bound to a socket |
1587 | | * at all. |
1588 | | */ |
1589 | 0 | if (main_server->ServerPort > 0) { |
1590 | | /* If SocketBindTight is off, then pr_inet_create_conn() will |
1591 | | * create and bind to a wildcard socket. However, should it be an |
1592 | | * IPv4 or an IPv6 wildcard socket? |
1593 | | */ |
1594 | 0 | if (!SocketBindTight) { |
1595 | 0 | #if defined(PR_USE_IPV6) |
1596 | 0 | if (pr_netaddr_use_ipv6()) { |
1597 | 0 | pr_inet_set_default_family(NULL, AF_INET6); |
1598 | |
|
1599 | 0 | } else { |
1600 | 0 | int default_family; |
1601 | |
|
1602 | 0 | default_family = pr_netaddr_get_family(main_server->addr); |
1603 | 0 | pr_inet_set_default_family(NULL, default_family); |
1604 | 0 | } |
1605 | | #else |
1606 | | pr_inet_set_default_family(NULL, |
1607 | | pr_netaddr_get_family(main_server->addr)); |
1608 | | #endif /* PR_USE_IPV6 */ |
1609 | 0 | } |
1610 | |
|
1611 | 0 | main_server->listen = pr_ipbind_get_listening_conn(main_server, |
1612 | 0 | (SocketBindTight ? main_server->addr : NULL), main_server->ServerPort); |
1613 | 0 | if (main_server->listen == NULL) { |
1614 | 0 | return -1; |
1615 | 0 | } |
1616 | |
|
1617 | 0 | } else { |
1618 | 0 | main_server->listen = NULL; |
1619 | 0 | } |
1620 | | |
1621 | 0 | default_server = get_param_ptr(main_server->conf, "DefaultServer", FALSE); |
1622 | 0 | if (default_server != NULL && |
1623 | 0 | *default_server == TRUE) { |
1624 | 0 | is_default = TRUE; |
1625 | 0 | } |
1626 | |
|
1627 | 0 | if (main_server->ServerPort == 0) { |
1628 | | /* If there is no server port, then this vhost cannot be treated as the |
1629 | | * DefaultServer. |
1630 | | */ |
1631 | 0 | is_default = FALSE; |
1632 | 0 | } |
1633 | |
|
1634 | 0 | if (main_server->ServerPort > 0 || |
1635 | 0 | is_default == TRUE) { |
1636 | |
|
1637 | 0 | res = pr_ipbind_create(main_server, main_server->addr, |
1638 | 0 | main_server->ServerPort); |
1639 | 0 | if (res < 0) { |
1640 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1641 | 0 | "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__, |
1642 | 0 | __LINE__, main_server->ServerAddress, main_server->ServerPort, |
1643 | 0 | strerror(errno)); |
1644 | 0 | } |
1645 | |
|
1646 | 0 | res = pr_ipbind_open(main_server->addr, main_server->ServerPort, |
1647 | 0 | main_server->listen, is_default, TRUE, TRUE); |
1648 | 0 | if (res < 0) { |
1649 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1650 | 0 | "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__, |
1651 | 0 | pr_netaddr_get_ipstr(main_server->addr), strerror(errno)); |
1652 | 0 | } |
1653 | |
|
1654 | 0 | res = pr_ipbind_add_binds(main_server); |
1655 | 0 | if (res < 0) { |
1656 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1657 | 0 | "%s:%d: notice: unable to add binds to ipbind '%s': %s", __FILE__, |
1658 | 0 | __LINE__, main_server->ServerAddress, strerror(errno)); |
1659 | 0 | } |
1660 | |
|
1661 | 0 | (void) process_serveralias(main_server); |
1662 | 0 | } |
1663 | |
|
1664 | 0 | for (serv = main_server->next; serv; serv = serv->next) { |
1665 | 0 | unsigned int namebind_count; |
1666 | |
|
1667 | 0 | pr_signals_handle(); |
1668 | |
|
1669 | 0 | namebind_count = process_serveralias(serv); |
1670 | 0 | if (namebind_count > 0) { |
1671 | | /* If we successfully added ServerAlias namebinds, move on to the next |
1672 | | * server. |
1673 | | */ |
1674 | 0 | continue; |
1675 | 0 | } |
1676 | | |
1677 | 0 | if (serv->ServerPort != main_server->ServerPort || |
1678 | 0 | SocketBindTight || |
1679 | 0 | !main_server->listen) { |
1680 | 0 | is_default = FALSE; |
1681 | |
|
1682 | 0 | default_server = get_param_ptr(serv->conf, "DefaultServer", FALSE); |
1683 | 0 | if (default_server != NULL && |
1684 | 0 | *default_server == TRUE) { |
1685 | 0 | is_default = TRUE; |
1686 | 0 | } |
1687 | |
|
1688 | 0 | if (serv->ServerPort == 0) { |
1689 | | /* If there is no server port, then this vhost cannot be treated as the |
1690 | | * DefaultServer. |
1691 | | */ |
1692 | 0 | is_default = FALSE; |
1693 | 0 | } |
1694 | |
|
1695 | 0 | if (serv->ServerPort > 0) { |
1696 | 0 | if (SocketBindTight == FALSE) { |
1697 | 0 | #if defined(PR_USE_IPV6) |
1698 | 0 | if (pr_netaddr_use_ipv6()) { |
1699 | 0 | pr_inet_set_default_family(NULL, AF_INET6); |
1700 | |
|
1701 | 0 | } else { |
1702 | 0 | pr_inet_set_default_family(NULL, pr_netaddr_get_family(serv->addr)); |
1703 | 0 | } |
1704 | | #else |
1705 | | pr_inet_set_default_family(NULL, pr_netaddr_get_family(serv->addr)); |
1706 | | #endif /* PR_USE_IPV6 */ |
1707 | 0 | } |
1708 | |
|
1709 | 0 | serv->listen = pr_ipbind_get_listening_conn(serv, |
1710 | 0 | (SocketBindTight ? serv->addr : NULL), serv->ServerPort); |
1711 | 0 | if (serv->listen == NULL) { |
1712 | 0 | return -1; |
1713 | 0 | } |
1714 | | |
1715 | 0 | res = pr_ipbind_create(serv, serv->addr, serv->ServerPort); |
1716 | 0 | if (res < 0) { |
1717 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1718 | 0 | "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__, |
1719 | 0 | __LINE__, serv->ServerAddress, serv->ServerPort, strerror(errno)); |
1720 | 0 | } |
1721 | |
|
1722 | 0 | res = pr_ipbind_open(serv->addr, serv->ServerPort, serv->listen, |
1723 | 0 | is_default, FALSE, TRUE); |
1724 | 0 | if (res < 0) { |
1725 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1726 | 0 | "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__, |
1727 | 0 | pr_netaddr_get_ipstr(serv->addr), strerror(errno)); |
1728 | 0 | } |
1729 | |
|
1730 | 0 | res = pr_ipbind_add_binds(serv); |
1731 | 0 | if (res < 0) { |
1732 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1733 | 0 | "%s:%d: notice: unable to add binds to ipbind '%s': %s", __FILE__, |
1734 | 0 | __LINE__, serv->ServerAddress, strerror(errno)); |
1735 | 0 | } |
1736 | |
|
1737 | 0 | } else if (is_default == TRUE) { |
1738 | 0 | serv->listen = NULL; |
1739 | |
|
1740 | 0 | res = pr_ipbind_create(serv, serv->addr, serv->ServerPort); |
1741 | 0 | if (res < 0) { |
1742 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1743 | 0 | "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__, |
1744 | 0 | __LINE__, serv->ServerAddress, serv->ServerPort, strerror(errno)); |
1745 | 0 | } |
1746 | |
|
1747 | 0 | res = pr_ipbind_open(serv->addr, serv->ServerPort, serv->listen, |
1748 | 0 | is_default, FALSE, TRUE); |
1749 | 0 | if (res < 0) { |
1750 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1751 | 0 | "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__, |
1752 | 0 | pr_netaddr_get_ipstr(serv->addr), strerror(errno)); |
1753 | 0 | } |
1754 | |
|
1755 | 0 | res = pr_ipbind_add_binds(serv); |
1756 | 0 | if (res < 0) { |
1757 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1758 | 0 | "%s:%d: notice: unable to add binds to ipbind '%s': %s", __FILE__, |
1759 | 0 | __LINE__, serv->ServerAddress, strerror(errno)); |
1760 | 0 | } |
1761 | |
|
1762 | 0 | } else { |
1763 | 0 | serv->listen = NULL; |
1764 | 0 | } |
1765 | |
|
1766 | 0 | } else { |
1767 | | /* Because this server is sharing the connection with the main server, |
1768 | | * we need a cleanup handler to remove the server's reference when the |
1769 | | * original connection's pool is destroyed. |
1770 | | */ |
1771 | |
|
1772 | 0 | is_default = FALSE; |
1773 | 0 | default_server = get_param_ptr(serv->conf, "DefaultServer", FALSE); |
1774 | 0 | if (default_server != NULL && |
1775 | 0 | *default_server == TRUE) { |
1776 | 0 | is_default = TRUE; |
1777 | 0 | } |
1778 | |
|
1779 | 0 | serv->listen = main_server->listen; |
1780 | 0 | register_cleanup2(serv->listen->pool, &serv->listen, server_cleanup_cb); |
1781 | |
|
1782 | 0 | res = pr_ipbind_create(serv, serv->addr, serv->ServerPort); |
1783 | 0 | if (res < 0) { |
1784 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1785 | 0 | "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__, |
1786 | 0 | __LINE__, serv->ServerAddress, serv->ServerPort, strerror(errno)); |
1787 | 0 | } |
1788 | |
|
1789 | 0 | res = pr_ipbind_open(serv->addr, serv->ServerPort, NULL, is_default, |
1790 | 0 | FALSE, TRUE); |
1791 | 0 | if (res < 0) { |
1792 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1793 | 0 | "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__, |
1794 | 0 | pr_netaddr_get_ipstr(serv->addr), strerror(errno)); |
1795 | 0 | } |
1796 | |
|
1797 | 0 | res = pr_ipbind_add_binds(serv); |
1798 | 0 | if (res < 0) { |
1799 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1800 | 0 | "%s:%d: notice: unable to add binds to ipbind '%s': %s", __FILE__, |
1801 | 0 | __LINE__, serv->ServerAddress, strerror(errno)); |
1802 | 0 | } |
1803 | 0 | } |
1804 | | |
1805 | | /* Process any ServerAlias directives AFTER the server's ipbind has been |
1806 | | * created/opened (Issue #932). |
1807 | | */ |
1808 | 0 | process_serveralias(serv); |
1809 | 0 | } |
1810 | | |
1811 | 0 | trace_ipbind_table(); |
1812 | | |
1813 | | /* Any "unclaimed" listening conns can be removed and closed. */ |
1814 | 0 | if (listening_conn_list != NULL) { |
1815 | 0 | struct listener_rec *lr, *lrn; |
1816 | |
|
1817 | 0 | for (lr = (struct listener_rec *) listening_conn_list->xas_list; lr; lr = lrn) { |
1818 | 0 | pr_signals_handle(); |
1819 | |
|
1820 | 0 | lrn = lr->next; |
1821 | 0 | if (lr->claimed == FALSE) { |
1822 | 0 | xaset_remove(listening_conn_list, (xasetmember_t *) lr); |
1823 | 0 | destroy_pool(lr->pool); |
1824 | 0 | } |
1825 | 0 | } |
1826 | 0 | } |
1827 | |
|
1828 | 0 | return 0; |
1829 | 0 | } |
1830 | | |
1831 | 0 | void init_bindings(void) { |
1832 | 0 | int res = 0; |
1833 | |
|
1834 | 0 | #if defined(PR_USE_IPV6) |
1835 | 0 | int sock; |
1836 | | |
1837 | | /* Check to see whether we can actually create an IPv6 socket. */ |
1838 | 0 | sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); |
1839 | 0 | if (sock < 0) { |
1840 | 0 | pr_netaddr_disable_ipv6(); |
1841 | |
|
1842 | 0 | } else { |
1843 | 0 | (void) close(sock); |
1844 | 0 | } |
1845 | 0 | #endif /* PR_USE_IPV6 */ |
1846 | |
|
1847 | 0 | if (ServerType == SERVER_INETD) { |
1848 | 0 | res = init_inetd_bindings(); |
1849 | |
|
1850 | 0 | } else if (ServerType == SERVER_STANDALONE) { |
1851 | 0 | res = init_standalone_bindings(); |
1852 | 0 | } |
1853 | |
|
1854 | 0 | if (res < 0) { |
1855 | 0 | pr_log_pri(PR_LOG_ERR, "%s", |
1856 | 0 | "Unable to start proftpd; check logs for more details"); |
1857 | 0 | exit(1); |
1858 | 0 | } |
1859 | 0 | } |