Remove crufty CVS $Id$ tags.
[fwd] / privconn.c
CommitLineData
ee599f55 1/* -*-c-*-
2 *
ee599f55 3 * Making privileged connections
4 *
5 * (c) 2003 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the `fw' port forwarder.
11 *
12 * `fw' is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * `fw' is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with `fw'; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
ee599f55 27/*----- Header files ------------------------------------------------------*/
28
29#include <assert.h>
30#include <errno.h>
31#include <signal.h>
32#include <string.h>
33
34#include <sys/types.h>
35#include <unistd.h>
36
37#include <sys/socket.h>
38#include <arpa/inet.h>
39#include <netinet/in.h>
40
41#include <mLib/conn.h>
42#include <mLib/darray.h>
43#include <mLib/fdflags.h>
44#include <mLib/fdpass.h>
45#include <mLib/report.h>
46#include <mLib/sel.h>
47
48#include "privconn.h"
49
50/*----- Data structures ---------------------------------------------------*/
51
52typedef struct connrec {
53 struct in_addr peer;
54 unsigned port;
55} connrec;
56
57typedef struct connrq {
58 int i;
59 struct in_addr bind;
60} connrq;
61
62DA_DECL(connrec_v, connrec);
63
64/*----- Static variables --------------------------------------------------*/
65
66static connrec_v cv = DA_INIT;
67static conn *qhead = 0, **qtail = &qhead;
68static int kidfd = -1;
69static sel_file sf;
70
71/*----- Main code ---------------------------------------------------------*/
72
73/* --- @doconn@ --- *
74 *
75 * Arguments: @const connrq *rq@ = index of connection record
76 *
77 * Returns: Connected file descriptor, or @-1@.
78 *
79 * Use: Main privileged connection thing.
80 */
81
82static int doconn(const connrq *rq)
83{
84 struct sockaddr_in sin_bind;
85 struct sockaddr_in sin_peer;
86 int fd;
87 int i;
88 connrec *c;
89
90 /* --- Check the argument --- */
91
92 if (rq->i < 0 || rq->i >= DA_LEN(&cv)) {
93 errno = EINVAL;
94 goto fail_0;
95 }
96 c = &DA(&cv)[rq->i];
97
98 /* --- Make a new socket --- */
99
100 if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
101 goto fail_0;
102
103 /* --- Bind it to a low-numbered port --- */
104
105 memset(&sin_bind, 0, sizeof(sin_bind));
106 sin_bind.sin_family = AF_INET;
107 sin_bind.sin_addr = rq->bind;
108 for (i = 1023; i >= 512; i--) {
109 sin_bind.sin_port = htons(i);
110 if (!bind(fd, (struct sockaddr *)&sin_bind, sizeof(sin_bind)))
111 goto bound;
112 if (errno != EADDRINUSE)
113 goto fail_1;
114 }
115 goto fail_1;
116
117 /* --- Connect to the peer --- *
118 *
119 * We can find out whether it's connected later, so there's no need to
120 * distinguish these cases.
121 */
122
123bound:
124 memset(&sin_peer, 0, sizeof(sin_peer));
125 sin_peer.sin_family = AF_INET;
126 sin_peer.sin_addr = c->peer;
127 sin_peer.sin_port = c->port;
128 fdflags(fd, O_NONBLOCK, O_NONBLOCK, 0, 0);
129 if (connect(fd, (struct sockaddr *)&sin_peer, sizeof(sin_peer)) < 0 &&
130 errno != EINPROGRESS)
131 goto fail_1;
132 return (fd);
133
134 /* --- Tidy up on errors --- */
135
136fail_1:
137 close(fd);
138fail_0:
139 return (-1);
140}
141
142/* --- @dochild@ --- *
143 *
144 * Arguments: @int fd@ = my file descriptor
145 *
146 * Returns: Never.
147 *
148 * Use: Child process for making privileged connections, separated
149 * from main process after initialization.
150 */
151
152static void dochild(int fd)
153{
154 int i;
155 connrq rq;
156 int nfd;
157 ssize_t sz;
158#if defined(_SC_OPEN_MAX)
159 int maxfd = sysconf(_SC_OPEN_MAX);
160#elif defined(OPEN_MAX)
161 int maxfd = OPEN_MAX;
162#else
163 int maxfd = -1;
164#endif
165 struct sigaction sa;
166 struct sigaction sa_dfl;
167
168 /* --- Clear out unnecessary file descriptors --- */
169
170 if (maxfd < 0)
171 maxfd = 256;
172 for (i = 3; i < maxfd; i++)
173 if (i != fd) close(i);
174
175 /* --- Close off signal handlers --- */
176
177 sa_dfl.sa_handler = SIG_DFL;
178 sigemptyset(&sa_dfl.sa_mask);
179 sa_dfl.sa_flags = 0;
180 for (i = 0; i < 256; i++) {
181 if (sigaction(i, 0, &sa))
182 break;
183 if (sa.sa_handler != SIG_DFL && sa.sa_handler != SIG_IGN)
184 sigaction(i, &sa_dfl, 0);
185 }
186
187 /* --- Main loop --- */
188
189 for (;;) {
190 sz = read(fd, &rq, sizeof(rq));
191 if (!sz)
192 break;
193 if (sz < 0)
194 die(1, "read error in privconn child: %s", strerror(errno));
195 if ((nfd = doconn(&rq)) < 0)
196 goto err;
197 i = 0;
198 sz = fdpass_send(fd, nfd, &i, sizeof(i));
199 if (sz < 0)
200 goto err;
201 if (sz < sizeof(i))
202 die(1, "short write in privconn child");
203 continue;
204
205 err:
206 if (write(fd, &errno, sizeof(errno)) < 0)
207 die(1, "write error in privconn child: %s", strerror(errno));
208 }
209 _exit(0);
210}
211
212/* --- @dorecvfd@ --- *
213 *
214 * Arguments: @int fd@ = file descriptor (@== kidfd@)
215 * @unsigned mode@ = what's happening (@== SEL_READ@)
216 * @void *p@ = uninteresting (@== 0@)
217 *
218 * Returns: ---
219 *
220 * Use: Receives a file descriptor from the privileged part.
221 */
222
223void dorecvfd(int fd, unsigned mode, void *p)
224{
225 conn *c, *cc;
226 ssize_t n;
227 int e;
228
229 n = fdpass_recv(kidfd, &fd, &e, sizeof(e));
230 if (!n)
231 goto close;
232 assert(qhead);
233 c = qhead;
234 qhead = (conn *)c->writer.next;
235 if (!qhead) qtail = &qhead;
236 if (n < 0 || (errno = e) != 0)
237 goto fail;
238 if (fd == -1) {
239 errno = EIO;
240 goto fail;
241 }
242 conn_fd(c, c->writer.s, fd, c->func, c->p);
243 return;
244
245fail:
246 c->func(-1, c->p);
247 return;
248
249close:
250 close(kidfd);
251 kidfd = 0;
252 errno = EIO;
253 sel_rmfile(&sf);
254 for (c = qhead; c; c = cc) {
255 cc = (conn *)c->writer.next;
256 c->func(-1, c->p);
257 }
258 qhead = 0;
259 qtail = &qhead;
260 return;
261}
262
263/* --- @privconn_split@ --- *
264 *
265 * Arguments: @sel_state *s@ = select state
266 *
267 * Returns: ---
268 *
269 * Use: Splits off the privileged binding code into a separate
270 * process.
271 */
272
273void privconn_split(sel_state *s)
274{
275 pid_t kid;
276 int fd[2];
277
092f3088 278 if (kidfd != -1 || DA_LEN(&cv) == 0)
ee599f55 279 return;
280 if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd) < 0)
281 die(1, "couldn't create privconn socketpair: %s", strerror(errno));
282 kidfd = fd[0];
283 if ((kid = fork()) < 0)
284 die(1, "couldn't fork privconn child: %s", strerror(errno));
285 if (!kid) {
286 close(kidfd);
287 dochild(fd[1]);
288 _exit(127);
289 }
290 close(fd[1]);
291 fdflags(kidfd, 0, 0, FD_CLOEXEC, FD_CLOEXEC);
292 sel_initfile(s, &sf, kidfd, SEL_READ, dorecvfd, 0);
293 sel_addfile(&sf);
294}
295
296/* --- @privconn_adddest@ --- *
297 *
298 * Arguments: @struct in_addr peer@ = address to connect to
299 * @unsigned port@ = port to connect to
300 *
301 * Returns: Index for this destination address, or @-1@ if not
302 * available.
303 *
304 * Use: Adds a valid destination for a privileged connection.
305 */
306
307int privconn_adddest(struct in_addr peer, unsigned port)
308{
309 int i;
310 struct connrec *c;
311
312 if (kidfd != -1)
313 return (-1);
314 for (i = 0; i < DA_LEN(&cv); i++) {
315 c = &DA(&cv)[i];
316 if (peer.s_addr == c->peer.s_addr && port == c->port)
317 return (i);
318 }
319 DA_ENSURE(&cv, 1);
320 DA_EXTEND(&cv, 1);
321 c = &DA(&cv)[i];
322 c->peer = peer;
323 c->port = port;
324 return (i);
325}
326
327/* --- @privconn_connect@ --- *
328 *
329 * Arguments: @conn *c@ = connection structure to fill in
330 * @sel_state *s@ = pointer to select state to attach to
331 * @int i@ = address index to connect to
332 * @struct in_addr bind@ = address to bind to
333 * @void (*func)(int, void *)@ = function to call on connect
334 * @void *p@ = argument for the function
335 *
336 * Returns: Zero on success, @-1@ on failure.
337 *
338 * Use: Sets up a privileged connection job.
339 */
340
341int privconn_connect(conn *c, sel_state *s, int i, struct in_addr bind,
342 void (*func)(int, void *), void *p)
343{
344 int fd;
345 connrq rq;
346 ssize_t n;
347
348 rq.i = i;
349 rq.bind = bind;
350 if (kidfd == -1) {
351 if ((fd = doconn(&rq)) < 0)
352 return (-1);
353 conn_fd(c, s, fd, func, p);
354 return (0);
355 }
356
357 n = write(kidfd, &rq, sizeof(rq));
358 if (n < 0)
359 return (-1);
360 c->writer.fd = -1;
361 c->writer.s = s;
362 c->writer.next = 0;
363 c->func = func;
364 c->p = p;
365 *qtail = c;
366 qtail = (conn **)&c->writer.next;
367 return (0);
368}
369
370/*----- That's all, folks -------------------------------------------------*/