3 * $Id: privconn.c,v 1.2 2003/11/29 20:43:01 mdw Exp $
5 * Making privileged connections
7 * (c) 2003 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of the `fw' port forwarder.
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.
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.
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.
29 /*----- Revision history --------------------------------------------------*
31 * $Log: privconn.c,v $
32 * Revision 1.2 2003/11/29 20:43:01 mdw
33 * Don't do priv separation if no priv connections to make.
35 * Revision 1.1 2003/11/29 20:36:07 mdw
36 * Privileged outgoing connections.
40 /*----- Header files ------------------------------------------------------*/
47 #include <sys/types.h>
50 #include <sys/socket.h>
51 #include <arpa/inet.h>
52 #include <netinet/in.h>
54 #include <mLib/conn.h>
55 #include <mLib/darray.h>
56 #include <mLib/fdflags.h>
57 #include <mLib/fdpass.h>
58 #include <mLib/report.h>
63 /*----- Data structures ---------------------------------------------------*/
65 typedef struct connrec
{
70 typedef struct connrq
{
75 DA_DECL(connrec_v
, connrec
);
77 /*----- Static variables --------------------------------------------------*/
79 static connrec_v cv
= DA_INIT
;
80 static conn
*qhead
= 0, **qtail
= &qhead
;
81 static int kidfd
= -1;
84 /*----- Main code ---------------------------------------------------------*/
88 * Arguments: @const connrq *rq@ = index of connection record
90 * Returns: Connected file descriptor, or @-1@.
92 * Use: Main privileged connection thing.
95 static int doconn(const connrq
*rq
)
97 struct sockaddr_in sin_bind
;
98 struct sockaddr_in sin_peer
;
103 /* --- Check the argument --- */
105 if (rq
->i
< 0 || rq
->i
>= DA_LEN(&cv
)) {
111 /* --- Make a new socket --- */
113 if ((fd
= socket(PF_INET
, SOCK_STREAM
, 0)) < 0)
116 /* --- Bind it to a low-numbered port --- */
118 memset(&sin_bind
, 0, sizeof(sin_bind
));
119 sin_bind
.sin_family
= AF_INET
;
120 sin_bind
.sin_addr
= rq
->bind
;
121 for (i
= 1023; i
>= 512; i
--) {
122 sin_bind
.sin_port
= htons(i
);
123 if (!bind(fd
, (struct sockaddr
*)&sin_bind
, sizeof(sin_bind
)))
125 if (errno
!= EADDRINUSE
)
130 /* --- Connect to the peer --- *
132 * We can find out whether it's connected later, so there's no need to
133 * distinguish these cases.
137 memset(&sin_peer
, 0, sizeof(sin_peer
));
138 sin_peer
.sin_family
= AF_INET
;
139 sin_peer
.sin_addr
= c
->peer
;
140 sin_peer
.sin_port
= c
->port
;
141 fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, 0, 0);
142 if (connect(fd
, (struct sockaddr
*)&sin_peer
, sizeof(sin_peer
)) < 0 &&
143 errno
!= EINPROGRESS
)
147 /* --- Tidy up on errors --- */
155 /* --- @dochild@ --- *
157 * Arguments: @int fd@ = my file descriptor
161 * Use: Child process for making privileged connections, separated
162 * from main process after initialization.
165 static void dochild(int fd
)
171 #if defined(_SC_OPEN_MAX)
172 int maxfd
= sysconf(_SC_OPEN_MAX
);
173 #elif defined(OPEN_MAX)
174 int maxfd
= OPEN_MAX
;
179 struct sigaction sa_dfl
;
181 /* --- Clear out unnecessary file descriptors --- */
185 for (i
= 3; i
< maxfd
; i
++)
186 if (i
!= fd
) close(i
);
188 /* --- Close off signal handlers --- */
190 sa_dfl
.sa_handler
= SIG_DFL
;
191 sigemptyset(&sa_dfl
.sa_mask
);
193 for (i
= 0; i
< 256; i
++) {
194 if (sigaction(i
, 0, &sa
))
196 if (sa
.sa_handler
!= SIG_DFL
&& sa
.sa_handler
!= SIG_IGN
)
197 sigaction(i
, &sa_dfl
, 0);
200 /* --- Main loop --- */
203 sz
= read(fd
, &rq
, sizeof(rq
));
207 die(1, "read error in privconn child: %s", strerror(errno
));
208 if ((nfd
= doconn(&rq
)) < 0)
211 sz
= fdpass_send(fd
, nfd
, &i
, sizeof(i
));
215 die(1, "short write in privconn child");
219 if (write(fd
, &errno
, sizeof(errno
)) < 0)
220 die(1, "write error in privconn child: %s", strerror(errno
));
225 /* --- @dorecvfd@ --- *
227 * Arguments: @int fd@ = file descriptor (@== kidfd@)
228 * @unsigned mode@ = what's happening (@== SEL_READ@)
229 * @void *p@ = uninteresting (@== 0@)
233 * Use: Receives a file descriptor from the privileged part.
236 void dorecvfd(int fd
, unsigned mode
, void *p
)
242 n
= fdpass_recv(kidfd
, &fd
, &e
, sizeof(e
));
247 qhead
= (conn
*)c
->writer
.next
;
248 if (!qhead
) qtail
= &qhead
;
249 if (n
< 0 || (errno
= e
) != 0)
255 conn_fd(c
, c
->writer
.s
, fd
, c
->func
, c
->p
);
267 for (c
= qhead
; c
; c
= cc
) {
268 cc
= (conn
*)c
->writer
.next
;
276 /* --- @privconn_split@ --- *
278 * Arguments: @sel_state *s@ = select state
282 * Use: Splits off the privileged binding code into a separate
286 void privconn_split(sel_state
*s
)
291 if (kidfd
!= -1 || DA_LEN(&cv
) == 0)
293 if (socketpair(PF_UNIX
, SOCK_STREAM
, 0, fd
) < 0)
294 die(1, "couldn't create privconn socketpair: %s", strerror(errno
));
296 if ((kid
= fork()) < 0)
297 die(1, "couldn't fork privconn child: %s", strerror(errno
));
304 fdflags(kidfd
, 0, 0, FD_CLOEXEC
, FD_CLOEXEC
);
305 sel_initfile(s
, &sf
, kidfd
, SEL_READ
, dorecvfd
, 0);
309 /* --- @privconn_adddest@ --- *
311 * Arguments: @struct in_addr peer@ = address to connect to
312 * @unsigned port@ = port to connect to
314 * Returns: Index for this destination address, or @-1@ if not
317 * Use: Adds a valid destination for a privileged connection.
320 int privconn_adddest(struct in_addr peer
, unsigned port
)
327 for (i
= 0; i
< DA_LEN(&cv
); i
++) {
329 if (peer
.s_addr
== c
->peer
.s_addr
&& port
== c
->port
)
340 /* --- @privconn_connect@ --- *
342 * Arguments: @conn *c@ = connection structure to fill in
343 * @sel_state *s@ = pointer to select state to attach to
344 * @int i@ = address index to connect to
345 * @struct in_addr bind@ = address to bind to
346 * @void (*func)(int, void *)@ = function to call on connect
347 * @void *p@ = argument for the function
349 * Returns: Zero on success, @-1@ on failure.
351 * Use: Sets up a privileged connection job.
354 int privconn_connect(conn
*c
, sel_state
*s
, int i
, struct in_addr bind
,
355 void (*func
)(int, void *), void *p
)
364 if ((fd
= doconn(&rq
)) < 0)
366 conn_fd(c
, s
, fd
, func
, p
);
370 n
= write(kidfd
, &rq
, sizeof(rq
));
379 qtail
= (conn
**)&c
->writer
.next
;
383 /*----- That's all, folks -------------------------------------------------*/