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