3 * Making privileged connections
5 * (c) 2003 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the `fw' port forwarder.
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.
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.
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.
29 /*----- Data structures ---------------------------------------------------*/
31 typedef struct connrec {
36 typedef struct connrq {
41 DA_DECL(connrec_v, connrec);
43 /*----- Static variables --------------------------------------------------*/
45 static connrec_v cv = DA_INIT;
46 static conn *qhead = 0, **qtail = &qhead;
47 static int kidfd = -1;
50 /*----- Main code ---------------------------------------------------------*/
54 * Arguments: @const connrq *rq@ = index of connection record
56 * Returns: Connected file descriptor, or @-1@.
58 * Use: Main privileged connection thing.
61 static int doconn(const connrq *rq)
63 struct sockaddr_in sin_bind;
64 struct sockaddr_in sin_peer;
69 /* --- Check the argument --- */
71 if (rq->i < 0 || rq->i >= DA_LEN(&cv)) {
77 /* --- Make a new socket --- */
79 if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
82 /* --- Bind it to a low-numbered port --- */
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)))
91 if (errno != EADDRINUSE)
96 /* --- Connect to the peer --- *
98 * We can find out whether it's connected later, so there's no need to
99 * distinguish these cases.
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)
113 /* --- Tidy up on errors --- */
121 /* --- @dochild@ --- *
123 * Arguments: @int fd@ = my file descriptor
127 * Use: Child process for making privileged connections, separated
128 * from main process after initialization.
131 static void dochild(int fd)
137 #if defined(_SC_OPEN_MAX)
138 int maxfd = sysconf(_SC_OPEN_MAX);
139 #elif defined(OPEN_MAX)
140 int maxfd = OPEN_MAX;
145 struct sigaction sa_dfl;
147 /* --- Clear out unnecessary file descriptors --- */
151 for (i = 3; i < maxfd; i++)
152 if (i != fd) close(i);
154 /* --- Close off signal handlers --- */
156 sa_dfl.sa_handler = SIG_DFL;
157 sigemptyset(&sa_dfl.sa_mask);
159 for (i = 0; i < 256; i++) {
160 if (sigaction(i, 0, &sa))
162 if (sa.sa_handler != SIG_DFL && sa.sa_handler != SIG_IGN)
163 sigaction(i, &sa_dfl, 0);
166 /* --- Main loop --- */
169 sz = read(fd, &rq, sizeof(rq));
173 die(1, "read error in privconn child: %s", strerror(errno));
174 if ((nfd = doconn(&rq)) < 0)
177 sz = fdpass_send(fd, nfd, &i, sizeof(i));
181 die(1, "short write in privconn child");
185 if (write(fd, &errno, sizeof(errno)) < 0)
186 die(1, "write error in privconn child: %s", strerror(errno));
191 /* --- @dorecvfd@ --- *
193 * Arguments: @int fd@ = file descriptor (@== kidfd@)
194 * @unsigned mode@ = what's happening (@== SEL_READ@)
195 * @void *p@ = uninteresting (@== 0@)
199 * Use: Receives a file descriptor from the privileged part.
202 void dorecvfd(int fd, unsigned mode, void *p)
208 n = fdpass_recv(kidfd, &fd, &e, sizeof(e));
213 qhead = (conn *)c->writer.next;
214 if (!qhead) qtail = &qhead;
215 if (n < 0 || (errno = e) != 0)
221 conn_fd(c, c->writer.s, fd, c->func, c->p);
233 for (c = qhead; c; c = cc) {
234 cc = (conn *)c->writer.next;
242 /* --- @privconn_split@ --- *
244 * Arguments: @sel_state *s@ = select state
248 * Use: Splits off the privileged binding code into a separate
252 void privconn_split(sel_state *s)
257 if (kidfd != -1 || DA_LEN(&cv) == 0)
259 if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd) < 0)
260 die(1, "couldn't create privconn socketpair: %s", strerror(errno));
262 if ((kid = fork()) < 0)
263 die(1, "couldn't fork privconn child: %s", strerror(errno));
270 fdflags(kidfd, 0, 0, FD_CLOEXEC, FD_CLOEXEC);
271 sel_initfile(s, &sf, kidfd, SEL_READ, dorecvfd, 0);
275 /* --- @privconn_adddest@ --- *
277 * Arguments: @struct in_addr peer@ = address to connect to
278 * @unsigned port@ = port to connect to
280 * Returns: Index for this destination address, or @-1@ if not
283 * Use: Adds a valid destination for a privileged connection.
286 int privconn_adddest(struct in_addr peer, unsigned port)
293 for (i = 0; i < DA_LEN(&cv); i++) {
295 if (peer.s_addr == c->peer.s_addr && port == c->port)
306 /* --- @privconn_connect@ --- *
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
315 * Returns: Zero on success, @-1@ on failure.
317 * Use: Sets up a privileged connection job.
320 int privconn_connect(conn *c, sel_state *s, int i, struct in_addr bind,
321 void (*func)(int, void *), void *p)
330 if ((fd = doconn(&rq)) < 0)
332 conn_fd(c, s, fd, func, p);
336 n = write(kidfd, &rq, sizeof(rq));
345 qtail = (conn **)&c->writer.next;
349 /*----- That's all, folks -------------------------------------------------*/