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