3 * Protocol specific definitions for IPv4 sockets
5 * (c) 1999 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.
27 /*----- Header files ------------------------------------------------------*/
37 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
45 #include <mLib/alloc.h>
46 #include <mLib/dstr.h>
47 #include <mLib/fdflags.h>
48 #include <mLib/report.h>
62 /*----- Data structures ---------------------------------------------------*/
64 typedef struct inet_addrx {
66 struct sockaddr_in sin;
69 typedef struct inet_opts {
74 typedef struct inet_srcopts {
80 typedef struct inet_targopts {
85 #define ADDRF_PRIVCONN 16u
87 static inet_srcopts inet_globalsrc =
88 { { { 0 }, { INADDR_ANY } }, 0, &inet_globalsrc.acl };
89 static inet_targopts inet_globaltarg =
90 { { { 0 }, { INADDR_ANY } } };
92 /*----- Protocol operations -----------------------------------------------*/
96 static addr *inet_read(scanner *sc, unsigned type)
98 inet_addrx *ia = xmalloc(sizeof(*ia));
100 ia->a.ops = &inet_ops;
101 ia->a.sz = sizeof(struct sockaddr_in);
102 memset(&ia->sin, 0, sizeof(ia->sin));
103 ia->sin.sin_family = AF_INET;
105 /* --- Read the host address part --- */
109 if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "port") == 0)
111 ia->sin.sin_addr.s_addr = htonl(INADDR_ANY);
116 conf_name(sc, '.', &d);
117 if ((h = gethostbyname(d.buf)) == 0)
118 error(sc, "couldn't resolve Internet address `%s'", d.buf);
119 memcpy(&ia->sin.sin_addr, h->h_addr, sizeof(struct in_addr));
126 /* --- Read the port number --- */
131 if (sc->t != CTOK_WORD)
132 error(sc, "parse error, TCP port expected");
133 if (isdigit((unsigned char)sc->d.buf[0]))
134 ia->sin.sin_port = htons(atoi(sc->d.buf));
135 else if ((s = getservbyname(sc->d.buf, "tcp")) == 0)
136 error(sc, "unknown tcp service `%s'", sc->d.buf);
138 ia->sin.sin_port = s->s_port;
145 /* --- @destroy@ --- */
147 static void inet_destroy(addr *a)
149 inet_addrx *ia = (inet_addrx *)a;
153 /* --- @print@ --- */
155 static void inet_print(addr *a, unsigned type, dstr *d)
157 inet_addrx *ia = (inet_addrx *)a;
160 dstr_putf(d, "inet:%u", (unsigned)ntohs(ia->sin.sin_port));
163 dstr_putf(d, "inet:%s:%u",
164 inet_ntoa(ia->sin.sin_addr),
165 (unsigned)ntohs(ia->sin.sin_port));
170 /* --- @initopts@ --- */
172 static addr_opts *inet_initsrcopts(void)
174 inet_srcopts *io = CREATE(inet_srcopts);
175 *io = inet_globalsrc;
177 io->acltail = &io->acl;
181 static addr_opts *inet_inittargopts(void)
183 inet_targopts *io = CREATE(inet_targopts);
184 *io = inet_globaltarg;
189 /* --- @option@ --- */
191 static void addropt(scanner *sc, inet_opts *io)
199 if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "any") == 0)
200 io->bind.s_addr = INADDR_ANY;
202 conf_name(sc, '.', &d);
203 if ((h = gethostbyname(d.buf)) == 0)
204 error(sc, "couldn't resolve address `%s'", d.buf);
205 memcpy(&io->bind, h->h_addr, sizeof(struct in_addr));
209 static int srcopt(scanner *sc, addr_opts *ao)
211 inet_srcopts *io = (inet_srcopts *)ao;
214 CONF_BEGIN(sc, "source", "Internet socket source")
216 /* --- Initialization --- */
219 if (!inet_globalsrc.acltail)
220 inet_globalsrc.acltail = &inet_globalsrc.acl;
221 io = &inet_globalsrc;
224 /* --- Source address configuration --- */
226 if (strcmp(sc->d.buf, "addr") == 0) {
227 addropt(sc, &io->io);
231 /* --- Access control limitations --- */
233 if ((strcmp(sc->d.buf, "allow") == 0 && (act = ACL_ALLOW, 1)) ||
234 (strcmp(sc->d.buf, "deny") == 0 && (act = ACL_DENY, 1))) {
240 /* --- Find out what's going on --- */
243 if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "from") == 0)
246 if (sc->t == CTOK_WORD && (strcmp(sc->d.buf, "priv") == 0 ||
247 strcmp(sc->d.buf, "priv-port") == 0)) {
248 acl_addpriv(&io->acltail, act);
251 if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "host") == 0)
254 /* --- Find the host or network address --- */
256 conf_name(sc, '.', &d);
257 #ifdef HAVE_GETNETBYNAME
258 if ((n = getnetbyname(d.buf)) != 0)
259 a.s_addr = htonl(n->n_net);
262 if ((h = gethostbyname(d.buf)) == 0)
263 error(sc, "couldn't resolve address `%s'", d.buf);
265 memcpy(&a, h->h_addr, sizeof(struct in_addr));
267 /* --- Find the netmask, if any --- */
274 conf_name(sc, '.', &d);
275 if (strchr(d.buf, '.') == 0) {
280 m.s_addr = htonl((~0ul << (32 - n)) & 0xffffffff);
282 #ifdef HAVE_INET_ATON
283 if (!inet_aton(d.buf, &m))
284 error(sc, "bad netmask `%s'", d.buf);
286 m.s_addr = inet_addr(d.buf);
292 /* --- Add the access control entry --- */
294 acl_addhost(&io->acltail, act, a, m);
299 /* --- Anything unrecognized --- */
304 static int targopt(scanner *sc, addr_opts *ao)
306 inet_targopts *io = (inet_targopts *)ao;
308 CONF_BEGIN(sc, "dest", "Internet socket target");
309 if (strcmp(sc->d.buf, "addr") == 0) {
310 addropt(sc, &io->io);
313 if (strcmp(sc->d.buf, "priv") == 0 ||
314 strcmp(sc->d.buf, "priv-port") == 0) {
316 if (sc->t == '=') token(sc);
317 if (conf_enum(sc, "no,yes", ENUM_ABBREV, "privileged connection status"))
318 io->io.ao.f |= ADDRF_PRIVCONN;
320 io->io.ao.f &= ~ADDRF_PRIVCONN;
326 static int inet_option(scanner *sc, addr_opts *ao, unsigned type)
328 CONF_BEGIN(sc, "inet", "Internet socket");
329 if (type != ADDR_DEST && srcopt(sc, ao))
331 if (type != ADDR_SRC && targopt(sc, ao))
336 /* --- @confirm@ --- */
338 static void inet_confirm(addr *a, unsigned type, addr_opts *ao)
340 inet_addrx *ia = (inet_addrx *)a;
344 inet_targopts *io = (inet_targopts *)ao;
345 if ((io->io.ao.f & ADDRF_PRIVCONN) &&
346 (io->ipriv = privconn_adddest(ia->sin.sin_addr,
347 ia->sin.sin_port)) < 0)
348 die(1, "couldn't add privileged connection target (too late)");
353 /* --- @freeopts@ --- */
355 static void inet_freesrcopts(addr_opts *ao)
357 inet_srcopts *io = (inet_srcopts *)ao;
362 static void inet_freetargopts(addr_opts *ao)
364 inet_targopts *io = (inet_targopts *)ao;
370 static int inet_bind(addr *a, addr_opts *ao)
372 inet_addrx *ia = (inet_addrx *)a;
373 inet_srcopts *io = (inet_srcopts *)ao;
374 struct sockaddr_in sin;
378 if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
380 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
381 fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
383 sin.sin_addr = io->io.bind;
384 if (bind(fd, (struct sockaddr *)&sin, sizeof(ia->sin)))
394 /* --- @accept@ --- */
396 static reffd *inet_accept(int fd, addr_opts *ao, const char *desc)
398 inet_srcopts *io = (inet_srcopts *)ao;
401 size_t lsinsz = sizeof(q.lsin), rsinsz = sizeof(q.rsin);
404 /* --- Accept the new connection --- */
406 if ((nfd = accept(fd, (struct sockaddr *)&q.rsin, &rsinsz)) < 0)
408 if (getsockname(nfd, (struct sockaddr *)&q.lsin, &lsinsz)) {
413 q.r = reffd_init(nfd);
415 /* --- Find out whether this connection is allowed --- */
417 if (!acl_check(io->acl, q.rsin.sin_addr, ntohs(q.rsin.sin_port), &act))
418 acl_check(inet_globalsrc.acl, q.rsin.sin_addr,
419 ntohs(q.rsin.sin_port), &act);
420 if (act != ACL_ALLOW) {
422 if (!(io->io.ao.f & ADDRF_NOLOG))
428 /* --- Everything seems to be OK --- */
431 if (!(io->io.ao.f & ADDRF_NOLOG))
436 /* --- @connect@ --- */
438 static int inet_connect(addr *a, addr_opts *ao, conn *c, endpt *e)
440 inet_addrx *ia = (inet_addrx *)a;
441 inet_targopts *io = (inet_targopts *)ao;
444 if (io->ipriv >= 0) {
445 return (privconn_connect(c, sel, io->ipriv, io->io.bind,
446 starget_connected, e));
448 if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
450 if (io->io.bind.s_addr != INADDR_ANY) {
451 struct sockaddr_in sin;
452 memset(&sin, 0, sizeof(sin));
453 sin.sin_family = AF_INET;
454 sin.sin_addr = io->io.bind;
456 if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)))
459 fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
460 return (conn_init(c, sel, fd, (struct sockaddr *)&ia->sin, sizeof(ia->sin),
461 starget_connected, e));
468 /* --- Ops table --- */
470 addr_ops inet_ops = {
472 inet_read, inet_destroy, inet_print,
473 inet_initsrcopts, inet_option, inet_confirm, inet_freesrcopts,
474 inet_bind, 0, inet_accept,
475 inet_inittargopts, inet_freetargopts,
479 /*----- That's all, folks -------------------------------------------------*/