1 |
|
|
/* $OpenBSD: inetd.c,v 1.158 2017/10/04 23:56:48 jca Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 1983,1991 The Regents of the University of California. |
5 |
|
|
* All rights reserved. |
6 |
|
|
* |
7 |
|
|
* Redistribution and use in source and binary forms, with or without |
8 |
|
|
* modification, are permitted provided that the following conditions |
9 |
|
|
* are met: |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer in the |
14 |
|
|
* documentation and/or other materials provided with the distribution. |
15 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
16 |
|
|
* may be used to endorse or promote products derived from this software |
17 |
|
|
* without specific prior written permission. |
18 |
|
|
* |
19 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
20 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
23 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
25 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
26 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
27 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
28 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 |
|
|
* SUCH DAMAGE. |
30 |
|
|
*/ |
31 |
|
|
|
32 |
|
|
/* |
33 |
|
|
* Inetd - Internet super-server |
34 |
|
|
* |
35 |
|
|
* This program invokes all internet services as needed. |
36 |
|
|
* connection-oriented services are invoked each time a |
37 |
|
|
* connection is made, by creating a process. This process |
38 |
|
|
* is passed the connection as file descriptor 0 and is |
39 |
|
|
* expected to do a getpeername to find out the source host |
40 |
|
|
* and port. |
41 |
|
|
* |
42 |
|
|
* Datagram oriented services are invoked when a datagram |
43 |
|
|
* arrives; a process is created and passed a pending message |
44 |
|
|
* on file descriptor 0. Datagram servers may either connect |
45 |
|
|
* to their peer, freeing up the original socket for inetd |
46 |
|
|
* to receive further messages on, or ``take over the socket'', |
47 |
|
|
* processing all arriving datagrams and, eventually, timing |
48 |
|
|
* out. The first type of server is said to be ``multi-threaded''; |
49 |
|
|
* the second type of server ``single-threaded''. |
50 |
|
|
* |
51 |
|
|
* Inetd uses a configuration file which is read at startup |
52 |
|
|
* and, possibly, at some later time in response to a hangup signal. |
53 |
|
|
* The configuration file is ``free format'' with fields given in the |
54 |
|
|
* order shown below. Continuation lines for an entry must begin with |
55 |
|
|
* a space or tab. All fields must be present in each entry. |
56 |
|
|
* |
57 |
|
|
* service name must be in /etc/services |
58 |
|
|
* socket type stream/dgram |
59 |
|
|
* protocol must be in /etc/protocols |
60 |
|
|
* wait/nowait[.max] single-threaded/multi-threaded, max # |
61 |
|
|
* user[.group] or user[:group] user/group to run daemon as |
62 |
|
|
* server program full path name |
63 |
|
|
* server program arguments maximum of MAXARGS (20) |
64 |
|
|
* |
65 |
|
|
* For RPC services |
66 |
|
|
* service name/version must be in /etc/rpc |
67 |
|
|
* socket type stream/dgram |
68 |
|
|
* protocol must be in /etc/protocols |
69 |
|
|
* wait/nowait[.max] single-threaded/multi-threaded |
70 |
|
|
* user[.group] or user[:group] user to run daemon as |
71 |
|
|
* server program full path name |
72 |
|
|
* server program arguments maximum of MAXARGS (20) |
73 |
|
|
* |
74 |
|
|
* For non-RPC services, the "service name" can be of the form |
75 |
|
|
* hostaddress:servicename, in which case the hostaddress is used |
76 |
|
|
* as the host portion of the address to listen on. If hostaddress |
77 |
|
|
* consists of a single `*' character, INADDR_ANY is used. |
78 |
|
|
* |
79 |
|
|
* A line can also consist of just |
80 |
|
|
* hostaddress: |
81 |
|
|
* where hostaddress is as in the preceding paragraph. Such a line must |
82 |
|
|
* have no further fields; the specified hostaddress is remembered and |
83 |
|
|
* used for all further lines that have no hostaddress specified, |
84 |
|
|
* until the next such line (or EOF). (This is why * is provided to |
85 |
|
|
* allow explicit specification of INADDR_ANY.) A line |
86 |
|
|
* *: |
87 |
|
|
* is implicitly in effect at the beginning of the file. |
88 |
|
|
* |
89 |
|
|
* The hostaddress specifier may (and often will) contain dots; |
90 |
|
|
* the service name must not. |
91 |
|
|
* |
92 |
|
|
* For RPC services, host-address specifiers are accepted and will |
93 |
|
|
* work to some extent; however, because of limitations in the |
94 |
|
|
* portmapper interface, it will not work to try to give more than |
95 |
|
|
* one line for any given RPC service, even if the host-address |
96 |
|
|
* specifiers are different. |
97 |
|
|
* |
98 |
|
|
* Comment lines are indicated by a `#' in column 1. |
99 |
|
|
*/ |
100 |
|
|
|
101 |
|
|
/* |
102 |
|
|
* Here's the scoop concerning the user[.:]group feature: |
103 |
|
|
* |
104 |
|
|
* 1) set-group-option off. |
105 |
|
|
* |
106 |
|
|
* a) user = root: NO setuid() or setgid() is done |
107 |
|
|
* |
108 |
|
|
* b) other: setgid(primary group as found in passwd) |
109 |
|
|
* initgroups(name, primary group) |
110 |
|
|
* setuid() |
111 |
|
|
* |
112 |
|
|
* 2) set-group-option on. |
113 |
|
|
* |
114 |
|
|
* a) user = root: setgid(specified group) |
115 |
|
|
* NO initgroups() |
116 |
|
|
* NO setuid() |
117 |
|
|
* |
118 |
|
|
* b) other: setgid(specified group) |
119 |
|
|
* initgroups(name, specified group) |
120 |
|
|
* setuid() |
121 |
|
|
* |
122 |
|
|
*/ |
123 |
|
|
|
124 |
|
|
#include <sys/stat.h> |
125 |
|
|
#include <sys/ioctl.h> |
126 |
|
|
#include <sys/socket.h> |
127 |
|
|
#include <sys/un.h> |
128 |
|
|
#include <sys/file.h> |
129 |
|
|
#include <sys/wait.h> |
130 |
|
|
#include <sys/time.h> |
131 |
|
|
#include <sys/resource.h> |
132 |
|
|
|
133 |
|
|
#include <net/if.h> |
134 |
|
|
#include <netinet/in.h> |
135 |
|
|
#include <arpa/inet.h> |
136 |
|
|
|
137 |
|
|
#include <err.h> |
138 |
|
|
#include <errno.h> |
139 |
|
|
#include <ctype.h> |
140 |
|
|
#include <signal.h> |
141 |
|
|
#include <netdb.h> |
142 |
|
|
#include <syslog.h> |
143 |
|
|
#include <pwd.h> |
144 |
|
|
#include <grp.h> |
145 |
|
|
#include <stdio.h> |
146 |
|
|
#include <stdlib.h> |
147 |
|
|
#include <unistd.h> |
148 |
|
|
#include <limits.h> |
149 |
|
|
#include <string.h> |
150 |
|
|
#include <login_cap.h> |
151 |
|
|
#include <ifaddrs.h> |
152 |
|
|
#include <rpc/rpc.h> |
153 |
|
|
#include <rpc/pmap_clnt.h> |
154 |
|
|
#include <rpcsvc/nfs_prot.h> |
155 |
|
|
#include <event.h> |
156 |
|
|
#include "pathnames.h" |
157 |
|
|
|
158 |
|
|
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) |
159 |
|
|
|
160 |
|
|
#define TOOMANY 256 /* don't start more than TOOMANY */ |
161 |
|
|
#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ |
162 |
|
|
#define RETRYTIME (60*10) /* retry after bind or server fail */ |
163 |
|
|
|
164 |
|
|
int debug = 0; |
165 |
|
|
int maxsock; |
166 |
|
|
int toomany = TOOMANY; |
167 |
|
|
int timingout; |
168 |
|
|
struct servent *sp; |
169 |
|
|
uid_t uid; |
170 |
|
|
|
171 |
|
|
#ifndef OPEN_MAX |
172 |
|
|
#define OPEN_MAX 64 |
173 |
|
|
#endif |
174 |
|
|
|
175 |
|
|
/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */ |
176 |
|
|
#define FD_MARGIN (8) |
177 |
|
|
rlim_t rlim_nofile_cur = OPEN_MAX; |
178 |
|
|
|
179 |
|
|
struct rlimit rlim_nofile; |
180 |
|
|
|
181 |
|
|
struct servtab { |
182 |
|
|
char *se_hostaddr; /* host address to listen on */ |
183 |
|
|
char *se_service; /* name of service */ |
184 |
|
|
int se_socktype; /* type of socket to use */ |
185 |
|
|
int se_family; /* address family */ |
186 |
|
|
char *se_proto; /* protocol used */ |
187 |
|
|
int se_rpcprog; /* rpc program number */ |
188 |
|
|
int se_rpcversl; /* rpc program lowest version */ |
189 |
|
|
int se_rpcversh; /* rpc program highest version */ |
190 |
|
|
#define isrpcservice(sep) ((sep)->se_rpcversl != 0) |
191 |
|
|
pid_t se_wait; /* single threaded server */ |
192 |
|
|
short se_checked; /* looked at during merge */ |
193 |
|
|
char *se_user; /* user name to run as */ |
194 |
|
|
char *se_group; /* group name to run as */ |
195 |
|
|
struct biltin *se_bi; /* if built-in, description */ |
196 |
|
|
char *se_server; /* server program */ |
197 |
|
|
#define MAXARGV 20 |
198 |
|
|
char *se_argv[MAXARGV+1]; /* program arguments */ |
199 |
|
|
int se_fd; /* open descriptor */ |
200 |
|
|
union { |
201 |
|
|
struct sockaddr se_un_ctrladdr; |
202 |
|
|
struct sockaddr_in se_un_ctrladdr_in; |
203 |
|
|
struct sockaddr_in6 se_un_ctrladdr_in6; |
204 |
|
|
struct sockaddr_un se_un_ctrladdr_un; |
205 |
|
|
struct sockaddr_storage se_un_ctrladdr_storage; |
206 |
|
|
} se_un; /* bound address */ |
207 |
|
|
#define se_ctrladdr se_un.se_un_ctrladdr |
208 |
|
|
#define se_ctrladdr_in se_un.se_un_ctrladdr_in |
209 |
|
|
#define se_ctrladdr_in6 se_un.se_un_ctrladdr_in6 |
210 |
|
|
#define se_ctrladdr_un se_un.se_un_ctrladdr_un |
211 |
|
|
#define se_ctrladdr_storage se_un.se_un_ctrladdr_storage |
212 |
|
|
int se_ctrladdr_size; |
213 |
|
|
int se_max; /* max # of instances of this service */ |
214 |
|
|
int se_count; /* number started since se_time */ |
215 |
|
|
struct timeval se_time; /* start of se_count */ |
216 |
|
|
struct servtab *se_next; |
217 |
|
|
struct event se_event; |
218 |
|
|
} *servtab; |
219 |
|
|
|
220 |
|
|
void echo_stream(int, struct servtab *); |
221 |
|
|
void discard_stream(int, struct servtab *); |
222 |
|
|
void machtime_stream(int, struct servtab *); |
223 |
|
|
void daytime_stream(int, struct servtab *); |
224 |
|
|
void chargen_stream(int, struct servtab *); |
225 |
|
|
void echo_dg(int, struct servtab *); |
226 |
|
|
void discard_dg(int, struct servtab *); |
227 |
|
|
void machtime_dg(int, struct servtab *); |
228 |
|
|
void daytime_dg(int, struct servtab *); |
229 |
|
|
void chargen_dg(int, struct servtab *); |
230 |
|
|
|
231 |
|
|
struct biltin { |
232 |
|
|
char *bi_service; /* internally provided service name */ |
233 |
|
|
int bi_socktype; /* type of socket supported */ |
234 |
|
|
short bi_fork; /* 1 if should fork before call */ |
235 |
|
|
short bi_wait; /* 1 if should wait for child */ |
236 |
|
|
void (*bi_fn)(int, struct servtab *); |
237 |
|
|
} biltins[] = { |
238 |
|
|
/* Echo received data */ |
239 |
|
|
{ "echo", SOCK_STREAM, 1, 0, echo_stream }, |
240 |
|
|
{ "echo", SOCK_DGRAM, 0, 0, echo_dg }, |
241 |
|
|
|
242 |
|
|
/* Internet /dev/null */ |
243 |
|
|
{ "discard", SOCK_STREAM, 1, 0, discard_stream }, |
244 |
|
|
{ "discard", SOCK_DGRAM, 0, 0, discard_dg }, |
245 |
|
|
|
246 |
|
|
/* Return 32 bit time since 1900 */ |
247 |
|
|
{ "time", SOCK_STREAM, 0, 0, machtime_stream }, |
248 |
|
|
{ "time", SOCK_DGRAM, 0, 0, machtime_dg }, |
249 |
|
|
|
250 |
|
|
/* Return human-readable time */ |
251 |
|
|
{ "daytime", SOCK_STREAM, 0, 0, daytime_stream }, |
252 |
|
|
{ "daytime", SOCK_DGRAM, 0, 0, daytime_dg }, |
253 |
|
|
|
254 |
|
|
/* Familiar character generator */ |
255 |
|
|
{ "chargen", SOCK_STREAM, 1, 0, chargen_stream }, |
256 |
|
|
{ "chargen", SOCK_DGRAM, 0, 0, chargen_dg }, |
257 |
|
|
|
258 |
|
|
{ 0 } |
259 |
|
|
}; |
260 |
|
|
|
261 |
|
|
struct event evsig_alrm; |
262 |
|
|
struct event evsig_hup; |
263 |
|
|
struct event evsig_chld; |
264 |
|
|
struct event evsig_term; |
265 |
|
|
struct event evsig_int; |
266 |
|
|
|
267 |
|
|
void config(int, short, void *); |
268 |
|
|
void reap(int, short, void *); |
269 |
|
|
void retry(int, short, void *); |
270 |
|
|
void die(int, short, void *); |
271 |
|
|
|
272 |
|
|
void spawn(int, short, void *); |
273 |
|
|
void gettcp(int, short, void *); |
274 |
|
|
int setconfig(void); |
275 |
|
|
void endconfig(void); |
276 |
|
|
void register_rpc(struct servtab *); |
277 |
|
|
void unregister_rpc(struct servtab *); |
278 |
|
|
void freeconfig(struct servtab *); |
279 |
|
|
void print_service(char *, struct servtab *); |
280 |
|
|
void setup(struct servtab *); |
281 |
|
|
struct servtab *getconfigent(void); |
282 |
|
|
int bump_nofile(void); |
283 |
|
|
struct servtab *enter(struct servtab *); |
284 |
|
|
int matchconf(struct servtab *, struct servtab *); |
285 |
|
|
int dg_broadcast(struct in_addr *in); |
286 |
|
|
|
287 |
|
|
#define NUMINT (sizeof(intab) / sizeof(struct inent)) |
288 |
|
|
char *CONFIG = _PATH_INETDCONF; |
289 |
|
|
|
290 |
|
|
int dg_badinput(struct sockaddr *sa); |
291 |
|
|
void inetd_setproctitle(char *a, int s); |
292 |
|
|
void initring(void); |
293 |
|
|
u_int32_t machtime(void); |
294 |
|
|
|
295 |
|
|
int |
296 |
|
|
main(int argc, char *argv[]) |
297 |
|
|
{ |
298 |
|
|
int ch; |
299 |
|
|
|
300 |
|
|
while ((ch = getopt(argc, argv, "dR:")) != -1) |
301 |
|
|
switch (ch) { |
302 |
|
|
case 'd': |
303 |
|
|
debug = 1; |
304 |
|
|
break; |
305 |
|
|
case 'R': { /* invocation rate */ |
306 |
|
|
char *p; |
307 |
|
|
int val; |
308 |
|
|
|
309 |
|
|
val = strtoul(optarg, &p, 0); |
310 |
|
|
if (val >= 1 && *p == '\0') { |
311 |
|
|
toomany = val; |
312 |
|
|
break; |
313 |
|
|
} |
314 |
|
|
syslog(LOG_ERR, |
315 |
|
|
"-R %s: bad value for service invocation rate", |
316 |
|
|
optarg); |
317 |
|
|
break; |
318 |
|
|
} |
319 |
|
|
case '?': |
320 |
|
|
default: |
321 |
|
|
fprintf(stderr, |
322 |
|
|
"usage: inetd [-d] [-R rate] [configuration_file]\n"); |
323 |
|
|
exit(1); |
324 |
|
|
} |
325 |
|
|
argc -= optind; |
326 |
|
|
argv += optind; |
327 |
|
|
|
328 |
|
|
uid = getuid(); |
329 |
|
|
if (uid != 0) |
330 |
|
|
CONFIG = NULL; |
331 |
|
|
if (argc > 0) |
332 |
|
|
CONFIG = argv[0]; |
333 |
|
|
if (CONFIG == NULL) { |
334 |
|
|
fprintf(stderr, "inetd: non-root must specify a config file\n"); |
335 |
|
|
exit(1); |
336 |
|
|
} |
337 |
|
|
if (argc > 1) { |
338 |
|
|
fprintf(stderr, "inetd: more than one argument specified\n"); |
339 |
|
|
exit(1); |
340 |
|
|
} |
341 |
|
|
|
342 |
|
|
umask(022); |
343 |
|
|
if (debug == 0) { |
344 |
|
|
daemon(0, 0); |
345 |
|
|
if (uid == 0) |
346 |
|
|
(void) setlogin(""); |
347 |
|
|
} |
348 |
|
|
|
349 |
|
|
if (pledge("stdio rpath cpath getpw dns inet unix proc exec id flock wpath", NULL) == -1) |
350 |
|
|
err(1, "pledge"); |
351 |
|
|
|
352 |
|
|
if (uid == 0) { |
353 |
|
|
gid_t gid = getgid(); |
354 |
|
|
|
355 |
|
|
/* If run by hand, ensure groups vector gets trashed */ |
356 |
|
|
setgroups(1, &gid); |
357 |
|
|
} |
358 |
|
|
|
359 |
|
|
openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); |
360 |
|
|
|
361 |
|
|
if (getrlimit(RLIMIT_NOFILE, &rlim_nofile) < 0) { |
362 |
|
|
syslog(LOG_ERR, "getrlimit: %m"); |
363 |
|
|
} else { |
364 |
|
|
rlim_nofile_cur = rlim_nofile.rlim_cur; |
365 |
|
|
if (rlim_nofile_cur == RLIM_INFINITY) /* ! */ |
366 |
|
|
rlim_nofile_cur = OPEN_MAX; |
367 |
|
|
} |
368 |
|
|
|
369 |
|
|
event_init(); |
370 |
|
|
|
371 |
|
|
signal_set(&evsig_alrm, SIGALRM, retry, NULL); |
372 |
|
|
signal_add(&evsig_alrm, NULL); |
373 |
|
|
|
374 |
|
|
config(0, 0, NULL); |
375 |
|
|
|
376 |
|
|
signal_set(&evsig_hup, SIGHUP, config, NULL); |
377 |
|
|
signal_add(&evsig_hup, NULL); |
378 |
|
|
signal_set(&evsig_chld, SIGCHLD, reap, NULL); |
379 |
|
|
signal_add(&evsig_chld, NULL); |
380 |
|
|
signal_set(&evsig_term, SIGTERM, die, NULL); |
381 |
|
|
signal_add(&evsig_term, NULL); |
382 |
|
|
signal_set(&evsig_int, SIGINT, die, NULL); |
383 |
|
|
signal_add(&evsig_int, NULL); |
384 |
|
|
|
385 |
|
|
signal(SIGPIPE, SIG_IGN); |
386 |
|
|
|
387 |
|
|
event_dispatch(); |
388 |
|
|
|
389 |
|
|
return (0); |
390 |
|
|
} |
391 |
|
|
|
392 |
|
|
void |
393 |
|
|
gettcp(int fd, short events, void *xsep) |
394 |
|
|
{ |
395 |
|
|
struct servtab *sep = xsep; |
396 |
|
|
int ctrl; |
397 |
|
|
|
398 |
|
|
if (debug) |
399 |
|
|
fprintf(stderr, "someone wants %s\n", sep->se_service); |
400 |
|
|
|
401 |
|
|
ctrl = accept(sep->se_fd, NULL, NULL); |
402 |
|
|
if (debug) |
403 |
|
|
fprintf(stderr, "accept, ctrl %d\n", ctrl); |
404 |
|
|
if (ctrl < 0) { |
405 |
|
|
if (errno != EWOULDBLOCK && errno != EINTR && |
406 |
|
|
errno != ECONNABORTED) |
407 |
|
|
syslog(LOG_WARNING, "accept (for %s): %m", |
408 |
|
|
sep->se_service); |
409 |
|
|
return; |
410 |
|
|
} |
411 |
|
|
if ((sep->se_family == AF_INET || sep->se_family == AF_INET6) && |
412 |
|
|
sep->se_socktype == SOCK_STREAM) { |
413 |
|
|
struct sockaddr_storage peer; |
414 |
|
|
socklen_t plen = sizeof(peer); |
415 |
|
|
char sbuf[NI_MAXSERV]; |
416 |
|
|
|
417 |
|
|
if (getpeername(ctrl, (struct sockaddr *)&peer, &plen) < 0) { |
418 |
|
|
syslog(LOG_WARNING, "could not getpeername"); |
419 |
|
|
close(ctrl); |
420 |
|
|
return; |
421 |
|
|
} |
422 |
|
|
if (getnameinfo((struct sockaddr *)&peer, plen, NULL, 0, |
423 |
|
|
sbuf, sizeof(sbuf), NI_NUMERICSERV) == 0 && |
424 |
|
|
strtonum(sbuf, 1, USHRT_MAX, NULL) == 20) { |
425 |
|
|
/* |
426 |
|
|
* ignore things that look like ftp bounce |
427 |
|
|
*/ |
428 |
|
|
close(ctrl); |
429 |
|
|
return; |
430 |
|
|
} |
431 |
|
|
} |
432 |
|
|
|
433 |
|
|
spawn(ctrl, 0, sep); |
434 |
|
|
} |
435 |
|
|
|
436 |
|
|
int |
437 |
|
|
dg_badinput(struct sockaddr *sa) |
438 |
|
|
{ |
439 |
|
|
struct in_addr in; |
440 |
|
|
struct in6_addr *in6; |
441 |
|
|
u_int16_t port; |
442 |
|
|
|
443 |
|
|
switch (sa->sa_family) { |
444 |
|
|
case AF_INET: |
445 |
|
|
in.s_addr = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr); |
446 |
|
|
port = ntohs(((struct sockaddr_in *)sa)->sin_port); |
447 |
|
|
if (IN_MULTICAST(in.s_addr)) |
448 |
|
|
goto bad; |
449 |
|
|
switch ((in.s_addr & 0xff000000) >> 24) { |
450 |
|
|
case 0: case 127: case 255: |
451 |
|
|
goto bad; |
452 |
|
|
} |
453 |
|
|
if (dg_broadcast(&in)) |
454 |
|
|
goto bad; |
455 |
|
|
break; |
456 |
|
|
case AF_INET6: |
457 |
|
|
in6 = &((struct sockaddr_in6 *)sa)->sin6_addr; |
458 |
|
|
port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port); |
459 |
|
|
if (IN6_IS_ADDR_MULTICAST(in6) || IN6_IS_ADDR_UNSPECIFIED(in6)) |
460 |
|
|
goto bad; |
461 |
|
|
/* |
462 |
|
|
* OpenBSD does not support IPv4-mapped and |
463 |
|
|
* IPv4-compatible IPv6 addresses (RFC2553). We should |
464 |
|
|
* drop the packet. |
465 |
|
|
*/ |
466 |
|
|
if (IN6_IS_ADDR_V4MAPPED(in6) || IN6_IS_ADDR_V4COMPAT(in6)) |
467 |
|
|
goto bad; |
468 |
|
|
break; |
469 |
|
|
default: |
470 |
|
|
/* Unsupported AF */ |
471 |
|
|
goto bad; |
472 |
|
|
} |
473 |
|
|
|
474 |
|
|
if (port < IPPORT_RESERVED || port == NFS_PORT) |
475 |
|
|
goto bad; |
476 |
|
|
|
477 |
|
|
return (0); |
478 |
|
|
|
479 |
|
|
bad: |
480 |
|
|
return (1); |
481 |
|
|
} |
482 |
|
|
|
483 |
|
|
int |
484 |
|
|
dg_broadcast(struct in_addr *in) |
485 |
|
|
{ |
486 |
|
|
struct ifaddrs *ifa, *ifap; |
487 |
|
|
struct sockaddr_in *sin; |
488 |
|
|
|
489 |
|
|
if (getifaddrs(&ifap) < 0) |
490 |
|
|
return (0); |
491 |
|
|
for (ifa = ifap; ifa; ifa = ifa->ifa_next) { |
492 |
|
|
if (ifa->ifa_addr->sa_family != AF_INET || |
493 |
|
|
(ifa->ifa_flags & IFF_BROADCAST) == 0) |
494 |
|
|
continue; |
495 |
|
|
sin = (struct sockaddr_in *)ifa->ifa_broadaddr; |
496 |
|
|
if (sin->sin_addr.s_addr == in->s_addr) { |
497 |
|
|
freeifaddrs(ifap); |
498 |
|
|
return (1); |
499 |
|
|
} |
500 |
|
|
} |
501 |
|
|
freeifaddrs(ifap); |
502 |
|
|
return (0); |
503 |
|
|
} |
504 |
|
|
|
505 |
|
|
void |
506 |
|
|
reap(int sig, short event, void *arg) |
507 |
|
|
{ |
508 |
|
|
struct servtab *sep; |
509 |
|
|
int status; |
510 |
|
|
pid_t pid; |
511 |
|
|
|
512 |
|
|
if (debug) |
513 |
|
|
fprintf(stderr, "reaping asked for\n"); |
514 |
|
|
|
515 |
|
|
for (;;) { |
516 |
|
|
if ((pid = wait3(&status, WNOHANG, NULL)) <= 0) { |
517 |
|
|
if (pid == -1 && errno == EINTR) |
518 |
|
|
continue; |
519 |
|
|
break; |
520 |
|
|
} |
521 |
|
|
if (debug) |
522 |
|
|
fprintf(stderr, "%ld reaped, status %x\n", |
523 |
|
|
(long)pid, status); |
524 |
|
|
for (sep = servtab; sep; sep = sep->se_next) |
525 |
|
|
if (sep->se_wait == pid) { |
526 |
|
|
if (WIFEXITED(status) && WEXITSTATUS(status)) |
527 |
|
|
syslog(LOG_WARNING, |
528 |
|
|
"%s: exit status %d", |
529 |
|
|
sep->se_server, WEXITSTATUS(status)); |
530 |
|
|
else if (WIFSIGNALED(status)) |
531 |
|
|
syslog(LOG_WARNING, |
532 |
|
|
"%s: exit signal %d", |
533 |
|
|
sep->se_server, WTERMSIG(status)); |
534 |
|
|
sep->se_wait = 1; |
535 |
|
|
event_add(&sep->se_event, NULL); |
536 |
|
|
if (debug) |
537 |
|
|
fprintf(stderr, "restored %s, fd %d\n", |
538 |
|
|
sep->se_service, sep->se_fd); |
539 |
|
|
} |
540 |
|
|
} |
541 |
|
|
} |
542 |
|
|
|
543 |
|
|
void |
544 |
|
|
config(int sig, short event, void *arg) |
545 |
|
|
{ |
546 |
|
|
struct servtab *sep, *cp, **sepp; |
547 |
|
|
int add; |
548 |
|
|
char protoname[11]; |
549 |
|
|
|
550 |
|
|
if (!setconfig()) { |
551 |
|
|
syslog(LOG_ERR, "%s: %m", CONFIG); |
552 |
|
|
exit(1); |
553 |
|
|
} |
554 |
|
|
for (sep = servtab; sep; sep = sep->se_next) |
555 |
|
|
sep->se_checked = 0; |
556 |
|
|
cp = getconfigent(); |
557 |
|
|
while (cp != NULL) { |
558 |
|
|
for (sep = servtab; sep; sep = sep->se_next) |
559 |
|
|
if (matchconf(sep, cp)) |
560 |
|
|
break; |
561 |
|
|
add = 0; |
562 |
|
|
if (sep != NULL) { |
563 |
|
|
int i; |
564 |
|
|
|
565 |
|
|
#define SWAP(type, a, b) {type c=(type)a; a=(type)b; b=(type)c;} |
566 |
|
|
|
567 |
|
|
/* |
568 |
|
|
* sep->se_wait may be holding the pid of a daemon |
569 |
|
|
* that we're waiting for. If so, don't overwrite |
570 |
|
|
* it unless the config file explicitly says don't |
571 |
|
|
* wait. |
572 |
|
|
*/ |
573 |
|
|
if (cp->se_bi == 0 && |
574 |
|
|
(sep->se_wait == 1 || cp->se_wait == 0)) |
575 |
|
|
sep->se_wait = cp->se_wait; |
576 |
|
|
SWAP(int, cp->se_max, sep->se_max); |
577 |
|
|
SWAP(char *, sep->se_user, cp->se_user); |
578 |
|
|
SWAP(char *, sep->se_group, cp->se_group); |
579 |
|
|
SWAP(char *, sep->se_server, cp->se_server); |
580 |
|
|
for (i = 0; i < MAXARGV; i++) |
581 |
|
|
SWAP(char *, sep->se_argv[i], cp->se_argv[i]); |
582 |
|
|
#undef SWAP |
583 |
|
|
if (isrpcservice(sep)) |
584 |
|
|
unregister_rpc(sep); |
585 |
|
|
sep->se_rpcversl = cp->se_rpcversl; |
586 |
|
|
sep->se_rpcversh = cp->se_rpcversh; |
587 |
|
|
freeconfig(cp); |
588 |
|
|
add = 1; |
589 |
|
|
} else { |
590 |
|
|
sep = enter(cp); |
591 |
|
|
} |
592 |
|
|
sep->se_checked = 1; |
593 |
|
|
|
594 |
|
|
switch (sep->se_family) { |
595 |
|
|
case AF_UNIX: |
596 |
|
|
if (sep->se_fd != -1) |
597 |
|
|
break; |
598 |
|
|
sep->se_ctrladdr_size = |
599 |
|
|
strlcpy(sep->se_ctrladdr_un.sun_path, |
600 |
|
|
sep->se_service, |
601 |
|
|
sizeof sep->se_ctrladdr_un.sun_path); |
602 |
|
|
if (sep->se_ctrladdr_size >= |
603 |
|
|
sizeof sep->se_ctrladdr_un.sun_path) { |
604 |
|
|
syslog(LOG_WARNING, "%s/%s: UNIX domain socket " |
605 |
|
|
"path too long", sep->se_service, |
606 |
|
|
sep->se_proto); |
607 |
|
|
goto serv_unknown; |
608 |
|
|
} |
609 |
|
|
sep->se_ctrladdr_un.sun_family = AF_UNIX; |
610 |
|
|
sep->se_ctrladdr_size += |
611 |
|
|
1 + sizeof sep->se_ctrladdr_un.sun_family; |
612 |
|
|
(void)unlink(sep->se_service); |
613 |
|
|
setup(sep); |
614 |
|
|
break; |
615 |
|
|
case AF_INET: |
616 |
|
|
sep->se_ctrladdr_in.sin_family = AF_INET; |
617 |
|
|
/* se_ctrladdr_in was set in getconfigent */ |
618 |
|
|
sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in; |
619 |
|
|
|
620 |
|
|
if (isrpcservice(sep)) { |
621 |
|
|
struct rpcent *rp; |
622 |
|
|
|
623 |
|
|
sep->se_rpcprog = strtonum(sep->se_service, |
624 |
|
|
1, USHRT_MAX, NULL); |
625 |
|
|
if (sep->se_rpcprog == 0) { |
626 |
|
|
rp = getrpcbyname(sep->se_service); |
627 |
|
|
if (rp == 0) { |
628 |
|
|
syslog(LOG_ERR, |
629 |
|
|
"%s: unknown rpc service", |
630 |
|
|
sep->se_service); |
631 |
|
|
goto serv_unknown; |
632 |
|
|
} |
633 |
|
|
sep->se_rpcprog = rp->r_number; |
634 |
|
|
} |
635 |
|
|
if (sep->se_fd == -1) |
636 |
|
|
setup(sep); |
637 |
|
|
if (sep->se_fd != -1) |
638 |
|
|
register_rpc(sep); |
639 |
|
|
} else { |
640 |
|
|
u_short port = htons(strtonum(sep->se_service, |
641 |
|
|
1, USHRT_MAX, NULL)); |
642 |
|
|
|
643 |
|
|
if (!port) { |
644 |
|
|
(void)strlcpy(protoname, sep->se_proto, |
645 |
|
|
sizeof(protoname)); |
646 |
|
|
if (isdigit((unsigned char) |
647 |
|
|
protoname[strlen(protoname) - 1])) |
648 |
|
|
protoname[strlen(protoname) - 1] = '\0'; |
649 |
|
|
sp = getservbyname(sep->se_service, |
650 |
|
|
protoname); |
651 |
|
|
if (sp == 0) { |
652 |
|
|
syslog(LOG_ERR, |
653 |
|
|
"%s/%s: unknown service", |
654 |
|
|
sep->se_service, sep->se_proto); |
655 |
|
|
goto serv_unknown; |
656 |
|
|
} |
657 |
|
|
port = sp->s_port; |
658 |
|
|
} |
659 |
|
|
if (port != sep->se_ctrladdr_in.sin_port) { |
660 |
|
|
sep->se_ctrladdr_in.sin_port = port; |
661 |
|
|
if (sep->se_fd != -1) { |
662 |
|
|
event_del(&sep->se_event); |
663 |
|
|
(void) close(sep->se_fd); |
664 |
|
|
} |
665 |
|
|
sep->se_fd = -1; |
666 |
|
|
} |
667 |
|
|
if (sep->se_fd == -1) |
668 |
|
|
setup(sep); |
669 |
|
|
} |
670 |
|
|
break; |
671 |
|
|
case AF_INET6: |
672 |
|
|
sep->se_ctrladdr_in6.sin6_family = AF_INET6; |
673 |
|
|
/* se_ctrladdr_in was set in getconfigent */ |
674 |
|
|
sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in6; |
675 |
|
|
|
676 |
|
|
if (isrpcservice(sep)) { |
677 |
|
|
struct rpcent *rp; |
678 |
|
|
|
679 |
|
|
sep->se_rpcprog = strtonum(sep->se_service, |
680 |
|
|
1, USHRT_MAX, NULL); |
681 |
|
|
if (sep->se_rpcprog == 0) { |
682 |
|
|
rp = getrpcbyname(sep->se_service); |
683 |
|
|
if (rp == 0) { |
684 |
|
|
syslog(LOG_ERR, |
685 |
|
|
"%s: unknown rpc service", |
686 |
|
|
sep->se_service); |
687 |
|
|
goto serv_unknown; |
688 |
|
|
} |
689 |
|
|
sep->se_rpcprog = rp->r_number; |
690 |
|
|
} |
691 |
|
|
if (sep->se_fd == -1) |
692 |
|
|
setup(sep); |
693 |
|
|
if (sep->se_fd != -1) |
694 |
|
|
register_rpc(sep); |
695 |
|
|
} else { |
696 |
|
|
u_short port = htons(strtonum(sep->se_service, |
697 |
|
|
1, USHRT_MAX, NULL)); |
698 |
|
|
|
699 |
|
|
if (!port) { |
700 |
|
|
(void)strlcpy(protoname, sep->se_proto, |
701 |
|
|
sizeof(protoname)); |
702 |
|
|
if (isdigit((unsigned char) |
703 |
|
|
protoname[strlen(protoname) - 1])) |
704 |
|
|
protoname[strlen(protoname) - 1] = '\0'; |
705 |
|
|
sp = getservbyname(sep->se_service, |
706 |
|
|
protoname); |
707 |
|
|
if (sp == 0) { |
708 |
|
|
syslog(LOG_ERR, |
709 |
|
|
"%s/%s: unknown service", |
710 |
|
|
sep->se_service, sep->se_proto); |
711 |
|
|
goto serv_unknown; |
712 |
|
|
} |
713 |
|
|
port = sp->s_port; |
714 |
|
|
} |
715 |
|
|
if (port != sep->se_ctrladdr_in6.sin6_port) { |
716 |
|
|
sep->se_ctrladdr_in6.sin6_port = port; |
717 |
|
|
if (sep->se_fd != -1) { |
718 |
|
|
event_del(&sep->se_event); |
719 |
|
|
(void) close(sep->se_fd); |
720 |
|
|
} |
721 |
|
|
sep->se_fd = -1; |
722 |
|
|
} |
723 |
|
|
if (sep->se_fd == -1) |
724 |
|
|
setup(sep); |
725 |
|
|
} |
726 |
|
|
break; |
727 |
|
|
} |
728 |
|
|
serv_unknown: |
729 |
|
|
if (cp->se_next != NULL) { |
730 |
|
|
struct servtab *tmp = cp; |
731 |
|
|
|
732 |
|
|
cp = cp->se_next; |
733 |
|
|
free(tmp); |
734 |
|
|
} else { |
735 |
|
|
free(cp); |
736 |
|
|
cp = getconfigent(); |
737 |
|
|
} |
738 |
|
|
if (debug) |
739 |
|
|
print_service(add ? "REDO" : "ADD", sep); |
740 |
|
|
} |
741 |
|
|
endconfig(); |
742 |
|
|
/* |
743 |
|
|
* Purge anything not looked at above. |
744 |
|
|
*/ |
745 |
|
|
sepp = &servtab; |
746 |
|
|
while ((sep = *sepp)) { |
747 |
|
|
if (sep->se_checked) { |
748 |
|
|
sepp = &sep->se_next; |
749 |
|
|
continue; |
750 |
|
|
} |
751 |
|
|
*sepp = sep->se_next; |
752 |
|
|
if (sep->se_fd != -1) { |
753 |
|
|
event_del(&sep->se_event); |
754 |
|
|
(void) close(sep->se_fd); |
755 |
|
|
} |
756 |
|
|
if (isrpcservice(sep)) |
757 |
|
|
unregister_rpc(sep); |
758 |
|
|
if (sep->se_family == AF_UNIX) |
759 |
|
|
(void)unlink(sep->se_service); |
760 |
|
|
if (debug) |
761 |
|
|
print_service("FREE", sep); |
762 |
|
|
freeconfig(sep); |
763 |
|
|
free(sep); |
764 |
|
|
} |
765 |
|
|
} |
766 |
|
|
|
767 |
|
|
void |
768 |
|
|
retry(int sig, short events, void *arg) |
769 |
|
|
{ |
770 |
|
|
struct servtab *sep; |
771 |
|
|
|
772 |
|
|
timingout = 0; |
773 |
|
|
for (sep = servtab; sep; sep = sep->se_next) { |
774 |
|
|
if (sep->se_fd == -1) { |
775 |
|
|
switch (sep->se_family) { |
776 |
|
|
case AF_UNIX: |
777 |
|
|
case AF_INET: |
778 |
|
|
case AF_INET6: |
779 |
|
|
setup(sep); |
780 |
|
|
if (sep->se_fd != -1 && isrpcservice(sep)) |
781 |
|
|
register_rpc(sep); |
782 |
|
|
break; |
783 |
|
|
} |
784 |
|
|
} |
785 |
|
|
} |
786 |
|
|
} |
787 |
|
|
|
788 |
|
|
void |
789 |
|
|
die(int sig, short events, void *arg) |
790 |
|
|
{ |
791 |
|
|
struct servtab *sep; |
792 |
|
|
|
793 |
|
|
for (sep = servtab; sep; sep = sep->se_next) { |
794 |
|
|
if (sep->se_fd == -1) |
795 |
|
|
continue; |
796 |
|
|
|
797 |
|
|
switch (sep->se_family) { |
798 |
|
|
case AF_UNIX: |
799 |
|
|
(void)unlink(sep->se_service); |
800 |
|
|
break; |
801 |
|
|
case AF_INET: |
802 |
|
|
case AF_INET6: |
803 |
|
|
if (sep->se_wait == 1 && isrpcservice(sep)) |
804 |
|
|
unregister_rpc(sep); |
805 |
|
|
break; |
806 |
|
|
} |
807 |
|
|
(void)close(sep->se_fd); |
808 |
|
|
} |
809 |
|
|
exit(0); |
810 |
|
|
} |
811 |
|
|
|
812 |
|
|
void |
813 |
|
|
setup(struct servtab *sep) |
814 |
|
|
{ |
815 |
|
|
int on = 1; |
816 |
|
|
int r; |
817 |
|
|
mode_t mask = 0; |
818 |
|
|
|
819 |
|
|
if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) { |
820 |
|
|
syslog(LOG_ERR, "%s/%s: socket: %m", |
821 |
|
|
sep->se_service, sep->se_proto); |
822 |
|
|
return; |
823 |
|
|
} |
824 |
|
|
#define turnon(fd, opt) \ |
825 |
|
|
setsockopt(fd, SOL_SOCKET, opt, &on, sizeof (on)) |
826 |
|
|
if (strncmp(sep->se_proto, "tcp", 3) == 0 && debug && |
827 |
|
|
turnon(sep->se_fd, SO_DEBUG) < 0) |
828 |
|
|
syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); |
829 |
|
|
if (turnon(sep->se_fd, SO_REUSEADDR) < 0) |
830 |
|
|
syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); |
831 |
|
|
#undef turnon |
832 |
|
|
if (isrpcservice(sep)) { |
833 |
|
|
struct passwd *pwd; |
834 |
|
|
|
835 |
|
|
/* |
836 |
|
|
* for RPC services, attempt to use a reserved port |
837 |
|
|
* if they are going to be running as root. |
838 |
|
|
* |
839 |
|
|
* Also, zero out the port for all RPC services; let bind() |
840 |
|
|
* find one. |
841 |
|
|
*/ |
842 |
|
|
sep->se_ctrladdr_in.sin_port = 0; |
843 |
|
|
if (sep->se_user && (pwd = getpwnam(sep->se_user)) && |
844 |
|
|
pwd->pw_uid == 0 && uid == 0) |
845 |
|
|
r = bindresvport(sep->se_fd, &sep->se_ctrladdr_in); |
846 |
|
|
else { |
847 |
|
|
r = bind(sep->se_fd, &sep->se_ctrladdr, |
848 |
|
|
sep->se_ctrladdr_size); |
849 |
|
|
if (r == 0) { |
850 |
|
|
socklen_t len = sep->se_ctrladdr_size; |
851 |
|
|
int saveerrno = errno; |
852 |
|
|
|
853 |
|
|
/* update se_ctrladdr_in.sin_port */ |
854 |
|
|
r = getsockname(sep->se_fd, &sep->se_ctrladdr, |
855 |
|
|
&len); |
856 |
|
|
if (r <= 0) |
857 |
|
|
errno = saveerrno; |
858 |
|
|
} |
859 |
|
|
} |
860 |
|
|
} else { |
861 |
|
|
if (sep->se_family == AF_UNIX) |
862 |
|
|
mask = umask(0111); |
863 |
|
|
r = bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size); |
864 |
|
|
if (sep->se_family == AF_UNIX) |
865 |
|
|
umask(mask); |
866 |
|
|
} |
867 |
|
|
if (r < 0) { |
868 |
|
|
syslog(LOG_ERR, "%s/%s: bind: %m", |
869 |
|
|
sep->se_service, sep->se_proto); |
870 |
|
|
(void) close(sep->se_fd); |
871 |
|
|
sep->se_fd = -1; |
872 |
|
|
if (!timingout) { |
873 |
|
|
timingout = 1; |
874 |
|
|
alarm(RETRYTIME); |
875 |
|
|
} |
876 |
|
|
return; |
877 |
|
|
} |
878 |
|
|
if (sep->se_socktype == SOCK_STREAM) |
879 |
|
|
listen(sep->se_fd, 10); |
880 |
|
|
|
881 |
|
|
if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { |
882 |
|
|
event_set(&sep->se_event, sep->se_fd, EV_READ|EV_PERSIST, |
883 |
|
|
gettcp, sep); |
884 |
|
|
} else { |
885 |
|
|
event_set(&sep->se_event, sep->se_fd, EV_READ|EV_PERSIST, |
886 |
|
|
spawn, sep); |
887 |
|
|
} |
888 |
|
|
|
889 |
|
|
event_add(&sep->se_event, NULL); |
890 |
|
|
|
891 |
|
|
if (sep->se_fd > maxsock) { |
892 |
|
|
maxsock = sep->se_fd; |
893 |
|
|
if (maxsock > rlim_nofile_cur - FD_MARGIN) |
894 |
|
|
bump_nofile(); |
895 |
|
|
} |
896 |
|
|
} |
897 |
|
|
|
898 |
|
|
void |
899 |
|
|
register_rpc(struct servtab *sep) |
900 |
|
|
{ |
901 |
|
|
socklen_t n; |
902 |
|
|
struct sockaddr_in sin; |
903 |
|
|
struct protoent *pp; |
904 |
|
|
|
905 |
|
|
if ((pp = getprotobyname(sep->se_proto+4)) == NULL) { |
906 |
|
|
syslog(LOG_ERR, "%s: getproto: %m", |
907 |
|
|
sep->se_proto); |
908 |
|
|
return; |
909 |
|
|
} |
910 |
|
|
n = sizeof sin; |
911 |
|
|
if (getsockname(sep->se_fd, (struct sockaddr *)&sin, &n) < 0) { |
912 |
|
|
syslog(LOG_ERR, "%s/%s: getsockname: %m", |
913 |
|
|
sep->se_service, sep->se_proto); |
914 |
|
|
return; |
915 |
|
|
} |
916 |
|
|
|
917 |
|
|
for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) { |
918 |
|
|
if (debug) |
919 |
|
|
fprintf(stderr, "pmap_set: %u %u %u %u\n", |
920 |
|
|
sep->se_rpcprog, n, pp->p_proto, |
921 |
|
|
ntohs(sin.sin_port)); |
922 |
|
|
(void)pmap_unset(sep->se_rpcprog, n); |
923 |
|
|
if (!pmap_set(sep->se_rpcprog, n, pp->p_proto, ntohs(sin.sin_port))) |
924 |
|
|
syslog(LOG_ERR, "%s %s: pmap_set: %u %u %u %u: %m", |
925 |
|
|
sep->se_service, sep->se_proto, |
926 |
|
|
sep->se_rpcprog, n, pp->p_proto, |
927 |
|
|
ntohs(sin.sin_port)); |
928 |
|
|
} |
929 |
|
|
} |
930 |
|
|
|
931 |
|
|
void |
932 |
|
|
unregister_rpc(struct servtab *sep) |
933 |
|
|
{ |
934 |
|
|
int n; |
935 |
|
|
|
936 |
|
|
for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) { |
937 |
|
|
if (debug) |
938 |
|
|
fprintf(stderr, "pmap_unset(%u, %u)\n", |
939 |
|
|
sep->se_rpcprog, n); |
940 |
|
|
if (!pmap_unset(sep->se_rpcprog, n)) |
941 |
|
|
syslog(LOG_ERR, "pmap_unset(%u, %u)", |
942 |
|
|
sep->se_rpcprog, n); |
943 |
|
|
} |
944 |
|
|
} |
945 |
|
|
|
946 |
|
|
|
947 |
|
|
struct servtab * |
948 |
|
|
enter(struct servtab *cp) |
949 |
|
|
{ |
950 |
|
|
struct servtab *sep; |
951 |
|
|
|
952 |
|
|
sep = malloc(sizeof (*sep)); |
953 |
|
|
if (sep == NULL) { |
954 |
|
|
syslog(LOG_ERR, "Out of memory."); |
955 |
|
|
exit(1); |
956 |
|
|
} |
957 |
|
|
*sep = *cp; |
958 |
|
|
sep->se_fd = -1; |
959 |
|
|
sep->se_rpcprog = -1; |
960 |
|
|
sep->se_next = servtab; |
961 |
|
|
servtab = sep; |
962 |
|
|
return (sep); |
963 |
|
|
} |
964 |
|
|
|
965 |
|
|
int |
966 |
|
|
matchconf(struct servtab *old, struct servtab *new) |
967 |
|
|
{ |
968 |
|
|
if (strcmp(old->se_service, new->se_service) != 0) |
969 |
|
|
return (0); |
970 |
|
|
|
971 |
|
|
if (strcmp(old->se_hostaddr, new->se_hostaddr) != 0) |
972 |
|
|
return (0); |
973 |
|
|
|
974 |
|
|
if (strcmp(old->se_proto, new->se_proto) != 0) |
975 |
|
|
return (0); |
976 |
|
|
|
977 |
|
|
/* |
978 |
|
|
* If the new servtab is bound to a specific address, check that the |
979 |
|
|
* old servtab is bound to the same entry. If the new service is not |
980 |
|
|
* bound to a specific address then the check of se_hostaddr above |
981 |
|
|
* is sufficient. |
982 |
|
|
*/ |
983 |
|
|
|
984 |
|
|
if (old->se_family == AF_INET && new->se_family == AF_INET && |
985 |
|
|
bcmp(&old->se_ctrladdr_in.sin_addr, |
986 |
|
|
&new->se_ctrladdr_in.sin_addr, |
987 |
|
|
sizeof(new->se_ctrladdr_in.sin_addr)) != 0) |
988 |
|
|
return (0); |
989 |
|
|
|
990 |
|
|
if (old->se_family == AF_INET6 && new->se_family == AF_INET6 && |
991 |
|
|
bcmp(&old->se_ctrladdr_in6.sin6_addr, |
992 |
|
|
&new->se_ctrladdr_in6.sin6_addr, |
993 |
|
|
sizeof(new->se_ctrladdr_in6.sin6_addr)) != 0) |
994 |
|
|
return (0); |
995 |
|
|
if (old->se_family == AF_INET6 && new->se_family == AF_INET6 && |
996 |
|
|
old->se_ctrladdr_in6.sin6_scope_id != |
997 |
|
|
new->se_ctrladdr_in6.sin6_scope_id) |
998 |
|
|
return (0); |
999 |
|
|
|
1000 |
|
|
return (1); |
1001 |
|
|
} |
1002 |
|
|
|
1003 |
|
|
FILE *fconfig = NULL; |
1004 |
|
|
char line[1024]; |
1005 |
|
|
char *defhost; |
1006 |
|
|
char *skip(char **, int); |
1007 |
|
|
char *nextline(FILE *); |
1008 |
|
|
char *newstr(char *); |
1009 |
|
|
struct servtab *dupconfig(struct servtab *); |
1010 |
|
|
|
1011 |
|
|
int |
1012 |
|
|
setconfig(void) |
1013 |
|
|
{ |
1014 |
|
|
free(defhost); |
1015 |
|
|
defhost = newstr("*"); |
1016 |
|
|
if (fconfig != NULL) { |
1017 |
|
|
fseek(fconfig, 0L, SEEK_SET); |
1018 |
|
|
return (1); |
1019 |
|
|
} |
1020 |
|
|
fconfig = fopen(CONFIG, "r"); |
1021 |
|
|
return (fconfig != NULL); |
1022 |
|
|
} |
1023 |
|
|
|
1024 |
|
|
void |
1025 |
|
|
endconfig(void) |
1026 |
|
|
{ |
1027 |
|
|
if (fconfig) { |
1028 |
|
|
(void) fclose(fconfig); |
1029 |
|
|
fconfig = NULL; |
1030 |
|
|
} |
1031 |
|
|
if (defhost) { |
1032 |
|
|
free(defhost); |
1033 |
|
|
defhost = 0; |
1034 |
|
|
} |
1035 |
|
|
} |
1036 |
|
|
|
1037 |
|
|
struct servtab * |
1038 |
|
|
getconfigent(void) |
1039 |
|
|
{ |
1040 |
|
|
struct servtab *sep, *tsep; |
1041 |
|
|
char *arg, *cp, *hostdelim, *s; |
1042 |
|
|
int argc; |
1043 |
|
|
|
1044 |
|
|
sep = calloc(1, sizeof(struct servtab)); |
1045 |
|
|
if (sep == NULL) { |
1046 |
|
|
syslog(LOG_ERR, "calloc: %m"); |
1047 |
|
|
exit(1); |
1048 |
|
|
} |
1049 |
|
|
more: |
1050 |
|
|
freeconfig(sep); |
1051 |
|
|
|
1052 |
|
|
while ((cp = nextline(fconfig)) && *cp == '#') |
1053 |
|
|
; |
1054 |
|
|
if (cp == NULL) { |
1055 |
|
|
free(sep); |
1056 |
|
|
return (NULL); |
1057 |
|
|
} |
1058 |
|
|
|
1059 |
|
|
memset(sep, 0, sizeof *sep); |
1060 |
|
|
arg = skip(&cp, 0); |
1061 |
|
|
if (arg == NULL) { |
1062 |
|
|
/* A blank line. */ |
1063 |
|
|
goto more; |
1064 |
|
|
} |
1065 |
|
|
|
1066 |
|
|
/* Check for a host name. */ |
1067 |
|
|
hostdelim = strrchr(arg, ':'); |
1068 |
|
|
if (hostdelim) { |
1069 |
|
|
*hostdelim = '\0'; |
1070 |
|
|
if (arg[0] == '[' && hostdelim > arg && hostdelim[-1] == ']') { |
1071 |
|
|
hostdelim[-1] = '\0'; |
1072 |
|
|
sep->se_hostaddr = newstr(arg + 1); |
1073 |
|
|
} else if (hostdelim == arg) |
1074 |
|
|
sep->se_hostaddr = newstr("*"); |
1075 |
|
|
else |
1076 |
|
|
sep->se_hostaddr = newstr(arg); |
1077 |
|
|
arg = hostdelim + 1; |
1078 |
|
|
/* |
1079 |
|
|
* If the line is of the form `host:', then just change the |
1080 |
|
|
* default host for the following lines. |
1081 |
|
|
*/ |
1082 |
|
|
if (*arg == '\0') { |
1083 |
|
|
arg = skip(&cp, 0); |
1084 |
|
|
if (cp == NULL) { |
1085 |
|
|
free(defhost); |
1086 |
|
|
defhost = newstr(sep->se_hostaddr); |
1087 |
|
|
goto more; |
1088 |
|
|
} |
1089 |
|
|
} |
1090 |
|
|
} else |
1091 |
|
|
sep->se_hostaddr = newstr(defhost); |
1092 |
|
|
|
1093 |
|
|
sep->se_service = newstr(arg); |
1094 |
|
|
if ((arg = skip(&cp, 1)) == NULL) |
1095 |
|
|
goto more; |
1096 |
|
|
|
1097 |
|
|
if (strcmp(arg, "stream") == 0) |
1098 |
|
|
sep->se_socktype = SOCK_STREAM; |
1099 |
|
|
else if (strcmp(arg, "dgram") == 0) |
1100 |
|
|
sep->se_socktype = SOCK_DGRAM; |
1101 |
|
|
else |
1102 |
|
|
sep->se_socktype = -1; |
1103 |
|
|
|
1104 |
|
|
if ((arg = skip(&cp, 1)) == NULL) |
1105 |
|
|
goto more; |
1106 |
|
|
|
1107 |
|
|
sep->se_proto = newstr(arg); |
1108 |
|
|
|
1109 |
|
|
if (strcmp(sep->se_proto, "unix") == 0) { |
1110 |
|
|
sep->se_family = AF_UNIX; |
1111 |
|
|
} else { |
1112 |
|
|
int s; |
1113 |
|
|
|
1114 |
|
|
sep->se_family = AF_INET; |
1115 |
|
|
if (sep->se_proto[strlen(sep->se_proto) - 1] == '6') |
1116 |
|
|
sep->se_family = AF_INET6; |
1117 |
|
|
|
1118 |
|
|
/* check if the family is supported */ |
1119 |
|
|
s = socket(sep->se_family, SOCK_DGRAM, 0); |
1120 |
|
|
if (s < 0) { |
1121 |
|
|
syslog(LOG_WARNING, "%s/%s: %s: the address family is " |
1122 |
|
|
"not supported by the kernel", sep->se_service, |
1123 |
|
|
sep->se_proto, sep->se_hostaddr); |
1124 |
|
|
goto more; |
1125 |
|
|
} |
1126 |
|
|
close(s); |
1127 |
|
|
|
1128 |
|
|
if (strncmp(sep->se_proto, "rpc/", 4) == 0) { |
1129 |
|
|
char *cp, *ccp; |
1130 |
|
|
long l; |
1131 |
|
|
|
1132 |
|
|
cp = strchr(sep->se_service, '/'); |
1133 |
|
|
if (cp == 0) { |
1134 |
|
|
syslog(LOG_ERR, "%s: no rpc version", |
1135 |
|
|
sep->se_service); |
1136 |
|
|
goto more; |
1137 |
|
|
} |
1138 |
|
|
*cp++ = '\0'; |
1139 |
|
|
l = strtol(cp, &ccp, 0); |
1140 |
|
|
if (ccp == cp || l < 0 || l > INT_MAX) { |
1141 |
|
|
badafterall: |
1142 |
|
|
syslog(LOG_ERR, "%s/%s: bad rpc version", |
1143 |
|
|
sep->se_service, cp); |
1144 |
|
|
goto more; |
1145 |
|
|
} |
1146 |
|
|
sep->se_rpcversl = sep->se_rpcversh = l; |
1147 |
|
|
if (*ccp == '-') { |
1148 |
|
|
cp = ccp + 1; |
1149 |
|
|
l = strtol(cp, &ccp, 0); |
1150 |
|
|
if (ccp == cp || l < 0 || l > INT_MAX || |
1151 |
|
|
l < sep->se_rpcversl || *ccp) |
1152 |
|
|
goto badafterall; |
1153 |
|
|
sep->se_rpcversh = l; |
1154 |
|
|
} else if (*ccp != '\0') |
1155 |
|
|
goto badafterall; |
1156 |
|
|
} |
1157 |
|
|
} |
1158 |
|
|
arg = skip(&cp, 1); |
1159 |
|
|
if (arg == NULL) |
1160 |
|
|
goto more; |
1161 |
|
|
|
1162 |
|
|
s = strchr(arg, '.'); |
1163 |
|
|
if (s) { |
1164 |
|
|
char *p; |
1165 |
|
|
|
1166 |
|
|
*s++ = '\0'; |
1167 |
|
|
sep->se_max = strtoul(s, &p, 0); |
1168 |
|
|
if (sep->se_max < 1 || *p) { |
1169 |
|
|
syslog(LOG_ERR, |
1170 |
|
|
"%s: illegal max field \"%s\", setting to %d", |
1171 |
|
|
sep->se_service, s, toomany); |
1172 |
|
|
sep->se_max = toomany; |
1173 |
|
|
} |
1174 |
|
|
} else |
1175 |
|
|
sep->se_max = toomany; |
1176 |
|
|
|
1177 |
|
|
sep->se_wait = strcmp(arg, "wait") == 0; |
1178 |
|
|
if ((arg = skip(&cp, 1)) == NULL) |
1179 |
|
|
goto more; |
1180 |
|
|
sep->se_user = newstr(arg); |
1181 |
|
|
arg = strchr(sep->se_user, '.'); |
1182 |
|
|
if (arg == NULL) |
1183 |
|
|
arg = strchr(sep->se_user, ':'); |
1184 |
|
|
if (arg) { |
1185 |
|
|
*arg++ = '\0'; |
1186 |
|
|
sep->se_group = newstr(arg); |
1187 |
|
|
} |
1188 |
|
|
if ((arg = skip(&cp, 1)) == NULL) |
1189 |
|
|
goto more; |
1190 |
|
|
|
1191 |
|
|
sep->se_server = newstr(arg); |
1192 |
|
|
if (strcmp(sep->se_server, "internal") == 0) { |
1193 |
|
|
struct biltin *bi; |
1194 |
|
|
|
1195 |
|
|
for (bi = biltins; bi->bi_service; bi++) |
1196 |
|
|
if (bi->bi_socktype == sep->se_socktype && |
1197 |
|
|
strcmp(bi->bi_service, sep->se_service) == 0) |
1198 |
|
|
break; |
1199 |
|
|
if (bi->bi_service == 0) { |
1200 |
|
|
syslog(LOG_ERR, "internal service %s unknown", |
1201 |
|
|
sep->se_service); |
1202 |
|
|
goto more; |
1203 |
|
|
} |
1204 |
|
|
sep->se_bi = bi; |
1205 |
|
|
sep->se_wait = bi->bi_wait; |
1206 |
|
|
} else |
1207 |
|
|
sep->se_bi = NULL; |
1208 |
|
|
argc = 0; |
1209 |
|
|
for (arg = skip(&cp, 0); cp; arg = skip(&cp, 0)) { |
1210 |
|
|
if (argc < MAXARGV) |
1211 |
|
|
sep->se_argv[argc++] = newstr(arg); |
1212 |
|
|
} |
1213 |
|
|
if (argc == 0 && sep->se_bi == NULL) { |
1214 |
|
|
if ((arg = strrchr(sep->se_server, '/')) != NULL) |
1215 |
|
|
arg++; |
1216 |
|
|
else |
1217 |
|
|
arg = sep->se_server; |
1218 |
|
|
sep->se_argv[argc++] = newstr(arg); |
1219 |
|
|
} |
1220 |
|
|
while (argc <= MAXARGV) |
1221 |
|
|
sep->se_argv[argc++] = NULL; |
1222 |
|
|
|
1223 |
|
|
/* |
1224 |
|
|
* Resolve each hostname in the se_hostaddr list (if any) |
1225 |
|
|
* and create a new entry for each resolved address. |
1226 |
|
|
*/ |
1227 |
|
|
if (sep->se_hostaddr != NULL && strcmp(sep->se_proto, "unix") != 0) { |
1228 |
|
|
struct addrinfo hints, *res0, *res; |
1229 |
|
|
char *host, *hostlist0, *hostlist, *port; |
1230 |
|
|
int error; |
1231 |
|
|
|
1232 |
|
|
hostlist = hostlist0 = sep->se_hostaddr; |
1233 |
|
|
sep->se_hostaddr = NULL; |
1234 |
|
|
sep->se_checked = -1; |
1235 |
|
|
while ((host = strsep(&hostlist, ",")) != NULL) { |
1236 |
|
|
if (*host == '\0') |
1237 |
|
|
continue; |
1238 |
|
|
|
1239 |
|
|
memset(&hints, 0, sizeof(hints)); |
1240 |
|
|
hints.ai_family = sep->se_family; |
1241 |
|
|
hints.ai_socktype = sep->se_socktype; |
1242 |
|
|
hints.ai_flags = AI_PASSIVE; |
1243 |
|
|
port = "0"; |
1244 |
|
|
error = getaddrinfo(strcmp(host, "*") ? host : NULL, |
1245 |
|
|
port, &hints, &res0); |
1246 |
|
|
if (error) { |
1247 |
|
|
syslog(LOG_ERR, "%s/%s: %s: %s", |
1248 |
|
|
sep->se_service, sep->se_proto, |
1249 |
|
|
host, gai_strerror(error)); |
1250 |
|
|
continue; |
1251 |
|
|
} |
1252 |
|
|
for (res = res0; res; res = res->ai_next) { |
1253 |
|
|
if (res->ai_addrlen > |
1254 |
|
|
sizeof(sep->se_ctrladdr_storage)) |
1255 |
|
|
continue; |
1256 |
|
|
/* |
1257 |
|
|
* If sep is unused, store host in there. |
1258 |
|
|
* Otherwise, dup a new entry and prepend it. |
1259 |
|
|
*/ |
1260 |
|
|
if (sep->se_checked == -1) { |
1261 |
|
|
sep->se_checked = 0; |
1262 |
|
|
} else { |
1263 |
|
|
tsep = dupconfig(sep); |
1264 |
|
|
tsep->se_next = sep; |
1265 |
|
|
sep = tsep; |
1266 |
|
|
} |
1267 |
|
|
sep->se_hostaddr = newstr(host); |
1268 |
|
|
memcpy(&sep->se_ctrladdr_storage, |
1269 |
|
|
res->ai_addr, res->ai_addrlen); |
1270 |
|
|
sep->se_ctrladdr_size = res->ai_addrlen; |
1271 |
|
|
} |
1272 |
|
|
freeaddrinfo(res0); |
1273 |
|
|
} |
1274 |
|
|
free(hostlist0); |
1275 |
|
|
if (sep->se_checked == -1) |
1276 |
|
|
goto more; /* no resolvable names/addresses */ |
1277 |
|
|
} |
1278 |
|
|
|
1279 |
|
|
return (sep); |
1280 |
|
|
} |
1281 |
|
|
|
1282 |
|
|
void |
1283 |
|
|
freeconfig(struct servtab *cp) |
1284 |
|
|
{ |
1285 |
|
|
int i; |
1286 |
|
|
|
1287 |
|
|
free(cp->se_hostaddr); |
1288 |
|
|
cp->se_hostaddr = NULL; |
1289 |
|
|
free(cp->se_service); |
1290 |
|
|
cp->se_service = NULL; |
1291 |
|
|
free(cp->se_proto); |
1292 |
|
|
cp->se_proto = NULL; |
1293 |
|
|
free(cp->se_user); |
1294 |
|
|
cp->se_user = NULL; |
1295 |
|
|
free(cp->se_group); |
1296 |
|
|
cp->se_group = NULL; |
1297 |
|
|
free(cp->se_server); |
1298 |
|
|
cp->se_server = NULL; |
1299 |
|
|
for (i = 0; i < MAXARGV; i++) { |
1300 |
|
|
free(cp->se_argv[i]); |
1301 |
|
|
cp->se_argv[i] = NULL; |
1302 |
|
|
} |
1303 |
|
|
} |
1304 |
|
|
|
1305 |
|
|
char * |
1306 |
|
|
skip(char **cpp, int report) |
1307 |
|
|
{ |
1308 |
|
|
char *cp = *cpp; |
1309 |
|
|
char *start; |
1310 |
|
|
|
1311 |
|
|
erp: |
1312 |
|
|
if (*cpp == NULL) { |
1313 |
|
|
if (report) |
1314 |
|
|
syslog(LOG_ERR, "syntax error in inetd config file"); |
1315 |
|
|
return (NULL); |
1316 |
|
|
} |
1317 |
|
|
|
1318 |
|
|
again: |
1319 |
|
|
while (*cp == ' ' || *cp == '\t') |
1320 |
|
|
cp++; |
1321 |
|
|
if (*cp == '\0') { |
1322 |
|
|
int c; |
1323 |
|
|
|
1324 |
|
|
c = getc(fconfig); |
1325 |
|
|
(void) ungetc(c, fconfig); |
1326 |
|
|
if (c == ' ' || c == '\t') |
1327 |
|
|
if ((cp = nextline(fconfig))) |
1328 |
|
|
goto again; |
1329 |
|
|
*cpp = NULL; |
1330 |
|
|
goto erp; |
1331 |
|
|
} |
1332 |
|
|
start = cp; |
1333 |
|
|
while (*cp && *cp != ' ' && *cp != '\t') |
1334 |
|
|
cp++; |
1335 |
|
|
if (*cp != '\0') |
1336 |
|
|
*cp++ = '\0'; |
1337 |
|
|
if ((*cpp = cp) == NULL) |
1338 |
|
|
goto erp; |
1339 |
|
|
|
1340 |
|
|
return (start); |
1341 |
|
|
} |
1342 |
|
|
|
1343 |
|
|
char * |
1344 |
|
|
nextline(FILE *fd) |
1345 |
|
|
{ |
1346 |
|
|
if (fgets(line, sizeof (line), fd) == NULL) |
1347 |
|
|
return (NULL); |
1348 |
|
|
line[strcspn(line, "\n")] = '\0'; |
1349 |
|
|
return (line); |
1350 |
|
|
} |
1351 |
|
|
|
1352 |
|
|
char * |
1353 |
|
|
newstr(char *cp) |
1354 |
|
|
{ |
1355 |
|
|
if ((cp = strdup(cp ? cp : ""))) |
1356 |
|
|
return(cp); |
1357 |
|
|
syslog(LOG_ERR, "strdup: %m"); |
1358 |
|
|
exit(1); |
1359 |
|
|
} |
1360 |
|
|
|
1361 |
|
|
struct servtab * |
1362 |
|
|
dupconfig(struct servtab *sep) |
1363 |
|
|
{ |
1364 |
|
|
struct servtab *newtab; |
1365 |
|
|
int argc; |
1366 |
|
|
|
1367 |
|
|
newtab = calloc(1, sizeof(struct servtab)); |
1368 |
|
|
|
1369 |
|
|
if (newtab == NULL) { |
1370 |
|
|
syslog(LOG_ERR, "calloc: %m"); |
1371 |
|
|
exit(1); |
1372 |
|
|
} |
1373 |
|
|
|
1374 |
|
|
newtab->se_service = sep->se_service ? newstr(sep->se_service) : NULL; |
1375 |
|
|
newtab->se_socktype = sep->se_socktype; |
1376 |
|
|
newtab->se_family = sep->se_family; |
1377 |
|
|
newtab->se_proto = sep->se_proto ? newstr(sep->se_proto) : NULL; |
1378 |
|
|
newtab->se_rpcprog = sep->se_rpcprog; |
1379 |
|
|
newtab->se_rpcversl = sep->se_rpcversl; |
1380 |
|
|
newtab->se_rpcversh = sep->se_rpcversh; |
1381 |
|
|
newtab->se_wait = sep->se_wait; |
1382 |
|
|
newtab->se_user = sep->se_user ? newstr(sep->se_user) : NULL; |
1383 |
|
|
newtab->se_group = sep->se_group ? newstr(sep->se_group) : NULL; |
1384 |
|
|
newtab->se_bi = sep->se_bi; |
1385 |
|
|
newtab->se_server = sep->se_server ? newstr(sep->se_server) : 0; |
1386 |
|
|
|
1387 |
|
|
for (argc = 0; argc <= MAXARGV; argc++) |
1388 |
|
|
newtab->se_argv[argc] = sep->se_argv[argc] ? |
1389 |
|
|
newstr(sep->se_argv[argc]) : NULL; |
1390 |
|
|
newtab->se_max = sep->se_max; |
1391 |
|
|
|
1392 |
|
|
return (newtab); |
1393 |
|
|
} |
1394 |
|
|
|
1395 |
|
|
void |
1396 |
|
|
inetd_setproctitle(char *a, int s) |
1397 |
|
|
{ |
1398 |
|
|
socklen_t size; |
1399 |
|
|
struct sockaddr_storage ss; |
1400 |
|
|
char hbuf[NI_MAXHOST]; |
1401 |
|
|
|
1402 |
|
|
size = sizeof(ss); |
1403 |
|
|
if (getpeername(s, (struct sockaddr *)&ss, &size) == 0) { |
1404 |
|
|
if (getnameinfo((struct sockaddr *)&ss, size, hbuf, |
1405 |
|
|
sizeof(hbuf), NULL, 0, NI_NUMERICHOST) == 0) |
1406 |
|
|
setproctitle("-%s [%s]", a, hbuf); |
1407 |
|
|
else |
1408 |
|
|
setproctitle("-%s [?]", a); |
1409 |
|
|
} else |
1410 |
|
|
setproctitle("-%s", a); |
1411 |
|
|
} |
1412 |
|
|
|
1413 |
|
|
int |
1414 |
|
|
bump_nofile(void) |
1415 |
|
|
{ |
1416 |
|
|
#define FD_CHUNK 32 |
1417 |
|
|
|
1418 |
|
|
struct rlimit rl; |
1419 |
|
|
|
1420 |
|
|
if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { |
1421 |
|
|
syslog(LOG_ERR, "getrlimit: %m"); |
1422 |
|
|
return -1; |
1423 |
|
|
} |
1424 |
|
|
rl.rlim_cur = MINIMUM(rl.rlim_max, rl.rlim_cur + FD_CHUNK); |
1425 |
|
|
rl.rlim_cur = MINIMUM(FD_SETSIZE, rl.rlim_cur + FD_CHUNK); |
1426 |
|
|
if (rl.rlim_cur <= rlim_nofile_cur) { |
1427 |
|
|
syslog(LOG_ERR, |
1428 |
|
|
"bump_nofile: cannot extend file limit, max = %d", |
1429 |
|
|
(int)rl.rlim_cur); |
1430 |
|
|
return -1; |
1431 |
|
|
} |
1432 |
|
|
|
1433 |
|
|
if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { |
1434 |
|
|
syslog(LOG_ERR, "setrlimit: %m"); |
1435 |
|
|
return -1; |
1436 |
|
|
} |
1437 |
|
|
|
1438 |
|
|
rlim_nofile_cur = rl.rlim_cur; |
1439 |
|
|
return 0; |
1440 |
|
|
} |
1441 |
|
|
|
1442 |
|
|
/* |
1443 |
|
|
* Internet services provided internally by inetd: |
1444 |
|
|
*/ |
1445 |
|
|
#define BUFSIZE 4096 |
1446 |
|
|
|
1447 |
|
|
void |
1448 |
|
|
echo_stream(int s, struct servtab *sep) |
1449 |
|
|
{ |
1450 |
|
|
char buffer[BUFSIZE]; |
1451 |
|
|
int i; |
1452 |
|
|
|
1453 |
|
|
inetd_setproctitle(sep->se_service, s); |
1454 |
|
|
while ((i = read(s, buffer, sizeof(buffer))) > 0 && |
1455 |
|
|
write(s, buffer, i) > 0) |
1456 |
|
|
; |
1457 |
|
|
exit(0); |
1458 |
|
|
} |
1459 |
|
|
|
1460 |
|
|
void |
1461 |
|
|
echo_dg(int s, struct servtab *sep) |
1462 |
|
|
{ |
1463 |
|
|
char buffer[BUFSIZE]; |
1464 |
|
|
int i; |
1465 |
|
|
socklen_t size; |
1466 |
|
|
struct sockaddr_storage ss; |
1467 |
|
|
|
1468 |
|
|
size = sizeof(ss); |
1469 |
|
|
if ((i = recvfrom(s, buffer, sizeof(buffer), 0, |
1470 |
|
|
(struct sockaddr *)&ss, &size)) < 0) |
1471 |
|
|
return; |
1472 |
|
|
if (dg_badinput((struct sockaddr *)&ss)) |
1473 |
|
|
return; |
1474 |
|
|
(void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size); |
1475 |
|
|
} |
1476 |
|
|
|
1477 |
|
|
void |
1478 |
|
|
discard_stream(int s, struct servtab *sep) |
1479 |
|
|
{ |
1480 |
|
|
char buffer[BUFSIZE]; |
1481 |
|
|
|
1482 |
|
|
inetd_setproctitle(sep->se_service, s); |
1483 |
|
|
while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) || |
1484 |
|
|
errno == EINTR) |
1485 |
|
|
; |
1486 |
|
|
exit(0); |
1487 |
|
|
} |
1488 |
|
|
|
1489 |
|
|
void |
1490 |
|
|
discard_dg(int s, struct servtab *sep) |
1491 |
|
|
{ |
1492 |
|
|
char buffer[BUFSIZE]; |
1493 |
|
|
|
1494 |
|
|
(void) read(s, buffer, sizeof(buffer)); |
1495 |
|
|
} |
1496 |
|
|
|
1497 |
|
|
#include <ctype.h> |
1498 |
|
|
#define LINESIZ 72 |
1499 |
|
|
char ring[128]; |
1500 |
|
|
char *endring; |
1501 |
|
|
|
1502 |
|
|
void |
1503 |
|
|
initring(void) |
1504 |
|
|
{ |
1505 |
|
|
int i; |
1506 |
|
|
|
1507 |
|
|
endring = ring; |
1508 |
|
|
|
1509 |
|
|
for (i = 0; i <= sizeof ring; ++i) |
1510 |
|
|
if (isprint((unsigned char)i)) |
1511 |
|
|
*endring++ = i; |
1512 |
|
|
} |
1513 |
|
|
|
1514 |
|
|
void |
1515 |
|
|
chargen_stream(int s, struct servtab *sep) |
1516 |
|
|
{ |
1517 |
|
|
char *rs; |
1518 |
|
|
int len; |
1519 |
|
|
char text[LINESIZ+2]; |
1520 |
|
|
|
1521 |
|
|
inetd_setproctitle(sep->se_service, s); |
1522 |
|
|
|
1523 |
|
|
if (!endring) { |
1524 |
|
|
initring(); |
1525 |
|
|
rs = ring; |
1526 |
|
|
} |
1527 |
|
|
|
1528 |
|
|
text[LINESIZ] = '\r'; |
1529 |
|
|
text[LINESIZ + 1] = '\n'; |
1530 |
|
|
for (rs = ring;;) { |
1531 |
|
|
if ((len = endring - rs) >= LINESIZ) |
1532 |
|
|
memmove(text, rs, LINESIZ); |
1533 |
|
|
else { |
1534 |
|
|
memmove(text, rs, len); |
1535 |
|
|
memmove(text + len, ring, LINESIZ - len); |
1536 |
|
|
} |
1537 |
|
|
if (++rs == endring) |
1538 |
|
|
rs = ring; |
1539 |
|
|
if (write(s, text, sizeof(text)) != sizeof(text)) |
1540 |
|
|
break; |
1541 |
|
|
} |
1542 |
|
|
exit(0); |
1543 |
|
|
} |
1544 |
|
|
|
1545 |
|
|
void |
1546 |
|
|
chargen_dg(int s, struct servtab *sep) |
1547 |
|
|
{ |
1548 |
|
|
struct sockaddr_storage ss; |
1549 |
|
|
static char *rs; |
1550 |
|
|
int len; |
1551 |
|
|
socklen_t size; |
1552 |
|
|
char text[LINESIZ+2]; |
1553 |
|
|
|
1554 |
|
|
if (endring == 0) { |
1555 |
|
|
initring(); |
1556 |
|
|
rs = ring; |
1557 |
|
|
} |
1558 |
|
|
|
1559 |
|
|
size = sizeof(ss); |
1560 |
|
|
if (recvfrom(s, text, sizeof(text), 0, (struct sockaddr *)&ss, |
1561 |
|
|
&size) < 0) |
1562 |
|
|
return; |
1563 |
|
|
if (dg_badinput((struct sockaddr *)&ss)) |
1564 |
|
|
return; |
1565 |
|
|
|
1566 |
|
|
if ((len = endring - rs) >= LINESIZ) |
1567 |
|
|
memmove(text, rs, LINESIZ); |
1568 |
|
|
else { |
1569 |
|
|
memmove(text, rs, len); |
1570 |
|
|
memmove(text + len, ring, LINESIZ - len); |
1571 |
|
|
} |
1572 |
|
|
if (++rs == endring) |
1573 |
|
|
rs = ring; |
1574 |
|
|
text[LINESIZ] = '\r'; |
1575 |
|
|
text[LINESIZ + 1] = '\n'; |
1576 |
|
|
(void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size); |
1577 |
|
|
} |
1578 |
|
|
|
1579 |
|
|
/* |
1580 |
|
|
* Return a machine readable date and time, in the form of the |
1581 |
|
|
* number of seconds since midnight, Jan 1, 1900. Since gettimeofday |
1582 |
|
|
* returns the number of seconds since midnight, Jan 1, 1970, |
1583 |
|
|
* we must add 2208988800 seconds to this figure to make up for |
1584 |
|
|
* some seventy years Bell Labs was asleep. |
1585 |
|
|
*/ |
1586 |
|
|
u_int32_t |
1587 |
|
|
machtime(void) |
1588 |
|
|
{ |
1589 |
|
|
struct timeval tv; |
1590 |
|
|
|
1591 |
|
|
if (gettimeofday(&tv, NULL) < 0) |
1592 |
|
|
return (0L); |
1593 |
|
|
|
1594 |
|
|
return (htonl((u_int32_t)tv.tv_sec + 2208988800UL)); |
1595 |
|
|
} |
1596 |
|
|
|
1597 |
|
|
void |
1598 |
|
|
machtime_stream(int s, struct servtab *sep) |
1599 |
|
|
{ |
1600 |
|
|
u_int32_t result; |
1601 |
|
|
|
1602 |
|
|
result = machtime(); |
1603 |
|
|
(void) write(s, &result, sizeof(result)); |
1604 |
|
|
} |
1605 |
|
|
|
1606 |
|
|
void |
1607 |
|
|
machtime_dg(int s, struct servtab *sep) |
1608 |
|
|
{ |
1609 |
|
|
u_int32_t result; |
1610 |
|
|
struct sockaddr_storage ss; |
1611 |
|
|
socklen_t size; |
1612 |
|
|
|
1613 |
|
|
size = sizeof(ss); |
1614 |
|
|
if (recvfrom(s, &result, sizeof(result), 0, |
1615 |
|
|
(struct sockaddr *)&ss, &size) < 0) |
1616 |
|
|
return; |
1617 |
|
|
if (dg_badinput((struct sockaddr *)&ss)) |
1618 |
|
|
return; |
1619 |
|
|
result = machtime(); |
1620 |
|
|
(void) sendto(s, &result, sizeof(result), 0, |
1621 |
|
|
(struct sockaddr *)&ss, size); |
1622 |
|
|
} |
1623 |
|
|
|
1624 |
|
|
/* Return human-readable time of day */ |
1625 |
|
|
void |
1626 |
|
|
daytime_stream(int s, struct servtab *sep) |
1627 |
|
|
{ |
1628 |
|
|
char buffer[256]; |
1629 |
|
|
time_t clock; |
1630 |
|
|
|
1631 |
|
|
clock = time(NULL); |
1632 |
|
|
|
1633 |
|
|
(void) snprintf(buffer, sizeof buffer, "%.24s\r\n", ctime(&clock)); |
1634 |
|
|
(void) write(s, buffer, strlen(buffer)); |
1635 |
|
|
} |
1636 |
|
|
|
1637 |
|
|
/* Return human-readable time of day */ |
1638 |
|
|
void |
1639 |
|
|
daytime_dg(int s, struct servtab *sep) |
1640 |
|
|
{ |
1641 |
|
|
char buffer[256]; |
1642 |
|
|
time_t clock; |
1643 |
|
|
struct sockaddr_storage ss; |
1644 |
|
|
socklen_t size; |
1645 |
|
|
|
1646 |
|
|
clock = time(NULL); |
1647 |
|
|
|
1648 |
|
|
size = sizeof(ss); |
1649 |
|
|
if (recvfrom(s, buffer, sizeof(buffer), 0, (struct sockaddr *)&ss, |
1650 |
|
|
&size) < 0) |
1651 |
|
|
return; |
1652 |
|
|
if (dg_badinput((struct sockaddr *)&ss)) |
1653 |
|
|
return; |
1654 |
|
|
(void) snprintf(buffer, sizeof buffer, "%.24s\r\n", ctime(&clock)); |
1655 |
|
|
(void) sendto(s, buffer, strlen(buffer), 0, (struct sockaddr *)&ss, |
1656 |
|
|
size); |
1657 |
|
|
} |
1658 |
|
|
|
1659 |
|
|
/* |
1660 |
|
|
* print_service: |
1661 |
|
|
* Dump relevant information to stderr |
1662 |
|
|
*/ |
1663 |
|
|
void |
1664 |
|
|
print_service(char *action, struct servtab *sep) |
1665 |
|
|
{ |
1666 |
|
|
if (strcmp(sep->se_hostaddr, "*") == 0) |
1667 |
|
|
fprintf(stderr, "%s: %s ", action, sep->se_service); |
1668 |
|
|
else |
1669 |
|
|
fprintf(stderr, "%s: %s:%s ", action, sep->se_hostaddr, |
1670 |
|
|
sep->se_service); |
1671 |
|
|
|
1672 |
|
|
if (isrpcservice(sep)) |
1673 |
|
|
fprintf(stderr, "rpcprog=%d, rpcvers=%d/%d, proto=%s,", |
1674 |
|
|
sep->se_rpcprog, sep->se_rpcversh, |
1675 |
|
|
sep->se_rpcversl, sep->se_proto); |
1676 |
|
|
else |
1677 |
|
|
fprintf(stderr, "proto=%s,", sep->se_proto); |
1678 |
|
|
|
1679 |
|
|
fprintf(stderr, |
1680 |
|
|
" wait.max=%d.%d user:group=%s:%s builtin=%lx server=%s\n", |
1681 |
|
|
sep->se_wait, sep->se_max, sep->se_user, |
1682 |
|
|
sep->se_group ? sep->se_group : "wheel", |
1683 |
|
|
(long)sep->se_bi, sep->se_server); |
1684 |
|
|
} |
1685 |
|
|
|
1686 |
|
|
void |
1687 |
|
|
spawn(int ctrl, short events, void *xsep) |
1688 |
|
|
{ |
1689 |
|
|
struct servtab *sep = xsep; |
1690 |
|
|
struct passwd *pwd; |
1691 |
|
|
int tmpint, dofork; |
1692 |
|
|
struct group *grp = NULL; |
1693 |
|
|
char buf[50]; |
1694 |
|
|
pid_t pid; |
1695 |
|
|
|
1696 |
|
|
if (debug) |
1697 |
|
|
fprintf(stderr, "someone wants %s\n", sep->se_service); |
1698 |
|
|
|
1699 |
|
|
pid = 0; |
1700 |
|
|
dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork); |
1701 |
|
|
if (dofork) { |
1702 |
|
|
if (sep->se_count++ == 0) |
1703 |
|
|
(void)gettimeofday(&sep->se_time, NULL); |
1704 |
|
|
else if (sep->se_count >= sep->se_max) { |
1705 |
|
|
struct timeval now; |
1706 |
|
|
|
1707 |
|
|
(void)gettimeofday(&now, NULL); |
1708 |
|
|
if (now.tv_sec - sep->se_time.tv_sec > |
1709 |
|
|
CNT_INTVL) { |
1710 |
|
|
sep->se_time = now; |
1711 |
|
|
sep->se_count = 1; |
1712 |
|
|
} else { |
1713 |
|
|
if (!sep->se_wait && |
1714 |
|
|
sep->se_socktype == SOCK_STREAM) |
1715 |
|
|
close(ctrl); |
1716 |
|
|
if (sep->se_family == AF_INET && |
1717 |
|
|
ntohs(sep->se_ctrladdr_in.sin_port) >= |
1718 |
|
|
IPPORT_RESERVED) { |
1719 |
|
|
/* |
1720 |
|
|
* Cannot close it -- there are |
1721 |
|
|
* thieves on the system. |
1722 |
|
|
* Simply ignore the connection. |
1723 |
|
|
*/ |
1724 |
|
|
--sep->se_count; |
1725 |
|
|
return; |
1726 |
|
|
} |
1727 |
|
|
syslog(LOG_ERR, |
1728 |
|
|
"%s/%s server failing (looping), service terminated", |
1729 |
|
|
sep->se_service, sep->se_proto); |
1730 |
|
|
if (!sep->se_wait && |
1731 |
|
|
sep->se_socktype == SOCK_STREAM) |
1732 |
|
|
close(ctrl); |
1733 |
|
|
event_del(&sep->se_event); |
1734 |
|
|
(void) close(sep->se_fd); |
1735 |
|
|
|
1736 |
|
|
sep->se_fd = -1; |
1737 |
|
|
sep->se_count = 0; |
1738 |
|
|
if (!timingout) { |
1739 |
|
|
timingout = 1; |
1740 |
|
|
alarm(RETRYTIME); |
1741 |
|
|
} |
1742 |
|
|
return; |
1743 |
|
|
} |
1744 |
|
|
} |
1745 |
|
|
pid = fork(); |
1746 |
|
|
} |
1747 |
|
|
if (pid < 0) { |
1748 |
|
|
syslog(LOG_ERR, "fork: %m"); |
1749 |
|
|
if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) |
1750 |
|
|
close(ctrl); |
1751 |
|
|
sleep(1); |
1752 |
|
|
return; |
1753 |
|
|
} |
1754 |
|
|
|
1755 |
|
|
if (pid && sep->se_wait) { |
1756 |
|
|
sep->se_wait = pid; |
1757 |
|
|
event_del(&sep->se_event); |
1758 |
|
|
} |
1759 |
|
|
if (pid == 0) { |
1760 |
|
|
if (sep->se_bi) { |
1761 |
|
|
if (dofork && pledge("stdio inet flock rpath cpath wpath", NULL) == -1) |
1762 |
|
|
err(1, "pledge"); |
1763 |
|
|
(*sep->se_bi->bi_fn)(ctrl, sep); |
1764 |
|
|
} else { |
1765 |
|
|
if ((pwd = getpwnam(sep->se_user)) == NULL) { |
1766 |
|
|
syslog(LOG_ERR, |
1767 |
|
|
"getpwnam: %s: No such user", |
1768 |
|
|
sep->se_user); |
1769 |
|
|
if (sep->se_socktype != SOCK_STREAM) |
1770 |
|
|
recv(0, buf, sizeof (buf), 0); |
1771 |
|
|
exit(1); |
1772 |
|
|
} |
1773 |
|
|
if (setsid() <0) |
1774 |
|
|
syslog(LOG_ERR, "%s: setsid: %m", |
1775 |
|
|
sep->se_service); |
1776 |
|
|
if (sep->se_group && |
1777 |
|
|
(grp = getgrnam(sep->se_group)) == NULL) { |
1778 |
|
|
syslog(LOG_ERR, |
1779 |
|
|
"getgrnam: %s: No such group", |
1780 |
|
|
sep->se_group); |
1781 |
|
|
if (sep->se_socktype != SOCK_STREAM) |
1782 |
|
|
recv(0, buf, sizeof (buf), 0); |
1783 |
|
|
exit(1); |
1784 |
|
|
} |
1785 |
|
|
if (uid != 0) { |
1786 |
|
|
/* a user running private inetd */ |
1787 |
|
|
if (uid != pwd->pw_uid) |
1788 |
|
|
exit(1); |
1789 |
|
|
} else { |
1790 |
|
|
tmpint = LOGIN_SETALL & |
1791 |
|
|
~(LOGIN_SETGROUP|LOGIN_SETLOGIN); |
1792 |
|
|
if (pwd->pw_uid) |
1793 |
|
|
tmpint |= LOGIN_SETGROUP|LOGIN_SETLOGIN; |
1794 |
|
|
if (sep->se_group) { |
1795 |
|
|
pwd->pw_gid = grp->gr_gid; |
1796 |
|
|
tmpint |= LOGIN_SETGROUP; |
1797 |
|
|
} |
1798 |
|
|
if (setusercontext(NULL, pwd, pwd->pw_uid, |
1799 |
|
|
tmpint) < 0) { |
1800 |
|
|
syslog(LOG_ERR, |
1801 |
|
|
"%s/%s: setusercontext: %m", |
1802 |
|
|
sep->se_service, sep->se_proto); |
1803 |
|
|
exit(1); |
1804 |
|
|
} |
1805 |
|
|
} |
1806 |
|
|
if (debug) |
1807 |
|
|
fprintf(stderr, "%ld execv %s\n", |
1808 |
|
|
(long)getpid(), sep->se_server); |
1809 |
|
|
if (ctrl != STDIN_FILENO) { |
1810 |
|
|
dup2(ctrl, STDIN_FILENO); |
1811 |
|
|
close(ctrl); |
1812 |
|
|
} |
1813 |
|
|
dup2(STDIN_FILENO, STDOUT_FILENO); |
1814 |
|
|
dup2(STDIN_FILENO, STDERR_FILENO); |
1815 |
|
|
closelog(); |
1816 |
|
|
closefrom(3); |
1817 |
|
|
signal(SIGPIPE, SIG_DFL); |
1818 |
|
|
execv(sep->se_server, sep->se_argv); |
1819 |
|
|
if (sep->se_socktype != SOCK_STREAM) |
1820 |
|
|
recv(0, buf, sizeof (buf), 0); |
1821 |
|
|
syslog(LOG_ERR, "execv %s: %m", sep->se_server); |
1822 |
|
|
exit(1); |
1823 |
|
|
} |
1824 |
|
|
} |
1825 |
|
|
if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) |
1826 |
|
|
close(ctrl); |
1827 |
|
|
} |