1 /*
2  * Copyright (c) 1998-1999 peter memishian (meem), meem@gnu.org
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  */
14 
15 #pragma ident "@(#)rlprd.c	1.1	99/09/16 meem"
16 
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <sys/wait.h>
20 #include <sys/time.h>		/* select() */
21 #include <sys/stat.h>		/* umask() */
22 #include <unistd.h>
23 #include <lib.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <signal.h>
31 #include <config.h>
32 #include R_MAXHOSTNAMELEN_HDR
33 
34 #ifdef	 HAVE_SYS_SELECT_H
35 #include <sys/select.h>
36 #endif
37 
38 #include "intl.h"
39 #include "util.h"
40 #include "msg.h"
41 #include "rlprd.h"
42 #include "rfc1179.h"
43 
44 static struct component *components[] =
45 {
46     &comp_msg,
47     &comp_component,
48     &comp_rlprd,
49     0
50 };
51 
52 const char		 *program_name;
53 const char		 *bsd_program_name = 0;
54 
55 static struct rlpr_rlprd *rlpr_rlprd;
56 
57 static int		converse(int, int);
58 static int		process_client(int, struct sockaddr_in *);
59 static int		register_sigchld(void);
60 static int		daemonize(void);
61 static RETSIGTYPE	catch_sigchld(int);
62 static RETSIGTYPE	graceful_shutdown(int);
63 
64 int
main(int argc,char * argv[])65 main(int argc, char *argv[])
66 {
67     struct component   *comp;
68     int			listen_fd, client_fd;
69     struct sockaddr_in	sin_rlprd, sin_client;
70     int			unused, on = 1;
71 
72     program_name = argv[0];
73     toggle_root();
74 
75     setlocale(LC_ALL, "");
76     bindtextdomain(PACKAGE, LOCALEDIR);
77     textdomain(PACKAGE);
78 
79     if ((comp = component_init(components, argc, argv)) != 0)
80 	msg(R_FATAL, 0, "component `%s': init() failed!", comp->name);
81 
82     if (geteuid() != 0)
83 	msg(R_FATAL, 0, "must be run as root or setuid root");
84 
85     if (register_sigchld() == -1)
86 	msg(R_FATAL, errno, "register_sigchld");
87 
88     signal(SIGHUP, SIG_IGN);
89     signal(SIGQUIT, SIG_IGN);
90     signal(SIGTERM, graceful_shutdown);
91 
92     if (rlpr_rlprd->no_daemon == 0 && daemonize() == 0)
93 	msg(R_FATAL, 0, "daemonizing failed");
94 
95     /*
96      * open ourselves for business
97      */
98 
99     listen_fd = socket(AF_INET, SOCK_STREAM, 0);
100     if (listen_fd == -1)
101 	msg(R_FATAL, errno, "socket() for listening socket");
102 
103     setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (caddr_t)&on, sizeof on);
104 
105     init_sockaddr_in(&sin_rlprd, 0, rlpr_rlprd->listen_port);
106 
107     if (bind(listen_fd, (struct sockaddr *)&sin_rlprd, sizeof sin_rlprd) == -1)
108 	msg(R_FATAL, errno, "bind() on listening socket");
109 
110     if (listen(listen_fd, 5) == -1)
111 	msg(R_FATAL, errno, "listen() on listening socket");
112 
113     msg(R_DEBUG, 0, "listening for incoming connections on port %hu",
114 	rlpr_rlprd->listen_port);
115 
116     /*
117      * main loop - accept incoming requests, calling process_client() on each
118      */
119 
120     for (;;) {
121 
122 	client_fd = accept(listen_fd, (struct sockaddr *)&sin_client, &unused);
123 	if (client_fd == -1) {
124 	    if (errno != EINTR)
125 		msg(R_ERROR, errno, "accept");
126 	    continue;
127 	}
128 
129 	switch (fork()) {
130 
131 	case  0:
132 	    if (process_client(client_fd, &sin_client) == 0)
133 		exit(EXIT_FAILURE);
134 
135 	    exit(EXIT_SUCCESS);
136 	    break;
137 
138 	case -1:
139 	    msg(R_ERROR, errno, "fork");
140 	    break;
141 
142 	default:
143 	    close(client_fd);
144 	    break;
145 	}
146     }
147 
148     /* NOTREACHED */
149     return EXIT_SUCCESS;
150 }
151 
152 static int
process_client(int client_fd,struct sockaddr_in * sin_client)153 process_client(int client_fd, struct sockaddr_in *sin_client)
154 {
155     char		printhost[MAXHOSTNAMELEN + 1];
156     int			lpd_fd;
157     unsigned int	i;
158     struct sockaddr_in	sin_local;
159     struct sockaddr_in	sin_lpd;
160 
161     msg(R_DEBUG, 0, "process_client: connection from %s:%i",
162 	inet_ntoa(sin_client->sin_addr), ntohs(sin_client->sin_port));
163 
164     /*
165      * read in the hostname
166      */
167 
168     for (i = 0; i < MAXHOSTNAMELEN; i++) {
169 	full_read_timed(client_fd, &printhost[i], 1, rlpr_rlprd->timeout, 0, 0);
170 	if (printhost[i] == '\n')
171 	    break;
172     }
173     printhost[i] = '\0';
174     msg(R_DEBUG, 0, "process_client: request for host `%s'", printhost);
175 
176     /*
177      * process the request
178      */
179 
180     init_sockaddr_in(&sin_local, 0, 0);
181     if (init_sockaddr_in(&sin_lpd, printhost, R_LPD_DST_PORT) == 0)
182 	return 0;
183 
184     toggle_root();			/* get root */
185 
186     lpd_fd = socket(AF_INET, SOCK_STREAM, 0);
187     if (lpd_fd == -1) {
188 	msg(R_ERROR, errno, "process_client: socket");
189 	toggle_root();
190 	return 0;
191     }
192 
193     if (bind_try_range(&sin_local, lpd_fd, R_LPD_SRC_PORT_LOW,
194 		       R_LPD_SRC_PORT_HIGH) == 0) {
195 	msg(R_ERROR, errno, "process_client: bind");
196 	toggle_root();
197 	return 0;
198     }
199 
200     toggle_root();
201 
202     if (connect_timed(lpd_fd, &sin_lpd, rlpr_rlprd->timeout) == -1) {
203 	msg(R_ERROR, errno, "process_client: connect to lpd");
204 	return 0;
205     }
206 
207     msg(R_DEBUG, 0, "process_client: connected from %s:%i to %s:%i",
208 	inet_ntoa(sin_client->sin_addr), ntohs(sin_client->sin_port),
209 	inet_ntoa(sin_lpd.sin_addr), ntohs(sin_lpd.sin_port));
210 
211     if (converse(client_fd, lpd_fd) == 0) {
212 	msg(R_ERROR, 0, "process_client: errors in conversing with lpd");
213 	return 0;
214     }
215 
216     msg(R_DEBUG, 0, "process client: transaction completed");
217     return 1;
218 }
219 
220 static int
converse(int client_fd,int server_fd)221 converse(int client_fd, int server_fd)
222 {
223     int			maxfdp1 = MAX(client_fd, server_fd) + 1;
224     char		buffer[R_BUFMAX];
225     fd_set		fds;
226     int			read_fd, write_fd;
227     ssize_t		n_read;
228 
229     for (;;) {
230 
231 	FD_ZERO(&fds);
232 	FD_SET(client_fd, &fds);
233 	FD_SET(server_fd, &fds);
234 
235 	if (select(maxfdp1, &fds, 0, 0, 0) <= 0) {
236 	    msg(R_ERROR, errno, "converse: select");
237 	    return 0;
238 	}
239 
240 	read_fd	 = FD_ISSET(client_fd, &fds) ? client_fd : server_fd;
241 	write_fd = FD_ISSET(client_fd, &fds) ? server_fd : client_fd;
242 
243 	n_read = read(read_fd, buffer, sizeof buffer);
244 	if (n_read == 0)
245 	    return 1;
246 
247 	full_write_timed(write_fd, buffer, n_read, rlpr_rlprd->timeout, 0, 0);
248     }
249 }
250 
251 /* ARGSUSED */
252 static RETSIGTYPE
catch_sigchld(int unused)253 catch_sigchld(int unused)
254 {
255     while (waitpid(-1, 0, WNOHANG) > 0)
256 	;
257 }
258 
259 /* ARGSUSED */
260 static RETSIGTYPE
graceful_shutdown(int unused)261 graceful_shutdown(int unused)
262 {
263     struct component	*comp;
264 
265     if ((comp = component_fini(components)) != 0)
266 	msg(R_FATAL, 0, "component `%s': fini() failed!", comp->name);
267 
268     exit(EXIT_SUCCESS);
269 }
270 
271 static int
register_sigchld(void)272 register_sigchld(void)
273 {
274     struct sigaction	sa = { 0 };
275 
276     sa.sa_handler = catch_sigchld;
277 #ifdef	SA_RESTART
278     sa.sa_flags	  = SA_RESTART;
279 #endif
280     sigemptyset(&sa.sa_mask);
281 
282     return sigaction(SIGCHLD, &sa, 0);
283 }
284 
285 static int
daemonize(void)286 daemonize(void)
287 {
288     msg_use_syslog(1);
289 
290     switch (fork()) {
291 
292     case -1:
293 
294 	msg(R_ERROR, errno, "fork while daemonizing");
295 	break;
296 
297     case  0:
298 
299 	/*
300 	 * start our new session, fork() again to lose process group leader
301 	 */
302 
303 	setsid();
304 	switch (fork()) {
305 
306 	case 0:
307 
308 	    chdir("/");
309 	    umask(0);
310 	    return 1;
311 
312 	default:
313 	    break;
314 	}
315 
316 	/* FALLTHRU into default */
317 
318     default:
319 	exit(EXIT_SUCCESS);
320     }
321 
322     return 0;
323 }
324 
325 static int
rlprd_init(void)326 rlprd_init(void)
327 {
328     struct rlpr_rlprd	rlpr_rlprd_tmpl = { 0, R_RLPRD_LISTEN_PORT };
329 
330     rlpr_rlprd	= xmalloc(sizeof (struct rlpr_rlprd));
331     *rlpr_rlprd = rlpr_rlprd_tmpl;
332 
333     rlpr_rlprd->timeout = R_TIMEOUT_DEFAULT;
334     return 1;
335 }
336 
337 static int
rlprd_parse_args(int opt)338 rlprd_parse_args(int opt)
339 {
340     switch (opt) {
341 
342     case 'n':
343 	rlpr_rlprd->no_daemon++;
344 	break;
345 
346     case 'p':
347 	rlpr_rlprd->listen_port = strtoul(optarg, 0, 0);
348 	break;
349 
350     case -700:
351 	msg(R_STDOUT, 0, "usage: %s [-nqV] [-pport] [--debug]"
352 	    " \nplease see the manpage for detailed help", program_name);
353 	exit(EXIT_SUCCESS);
354 	break;
355 
356     case -701:
357 	rlpr_rlprd->timeout = strtol(optarg, 0, 0);
358 	break;
359 
360     case 'V':
361 	msg(R_STDOUT, 0, "version "VERSION" from "__DATE__" "__TIME__
362 	    " -- meem@gnu.org");
363 	exit(EXIT_SUCCESS);
364 	break;
365 
366     case EOF:
367 	break;
368     }
369 
370     return 1;
371 }
372 
373 static struct option rlprd_opts[] = {
374     { "help",		0, 0, -700  },
375     { "no-daemon",	0, 0, 'n'   },
376     { "port",		1, 0, 'p'   },
377     { "timeout",	1, 0, -701  },
378     { "version",	0, 0, 'V'   },
379     { 0,		0, 0,  0    }
380 };
381 
382 static const char rlprd_opt_list[] = "npV";
383 
384 struct component comp_rlprd = {
385     "rlprd", rlprd_init, 0,
386     { { rlprd_opts, rlprd_opt_list, rlprd_parse_args } }
387 };
388