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