X-Git-Url: https://git.distorted.org.uk/~mdw/fwd/blobdiff_plain/bc241e9852f14199c850f88e727abb39acdf90c6..755e6619263d316dc46fa504e0c333c963ec8eaa:/inet.c diff --git a/inet.c b/inet.c index a9a559e..ad93beb 100644 --- a/inet.c +++ b/inet.c @@ -1,75 +1,30 @@ /* -*-c-*- * - * $Id: inet.c,v 1.3 2000/08/01 17:59:56 mdw Exp $ - * * Protocol specific definitions for IPv4 sockets * * (c) 1999 Straylight/Edgeware */ -/*----- Licensing notice --------------------------------------------------* +/*----- Licensing notice --------------------------------------------------* * - * This file is part of the `fw' port forwarder. + * This file is part of the `fwd' port forwarder. * - * `fw' is free software; you can redistribute it and/or modify + * `fwd' is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * `fw' is distributed in the hope that it will be useful, + * + * `fwd' is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License - * along with `fw'; if not, write to the Free Software Foundation, + * along with `fwd'; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/*----- Revision history --------------------------------------------------* - * - * $Log: inet.c,v $ - * Revision 1.3 2000/08/01 17:59:56 mdw - * Switch over to using `size_t' for socket address lengths. - * - * Revision 1.2 1999/07/27 18:30:53 mdw - * Various minor portability fixes. - * - * Revision 1.1 1999/07/26 23:34:11 mdw - * New socket address types. - * - */ - -/*----- Header files ------------------------------------------------------*/ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "acl.h" -#include "addr.h" -#include "conf.h" -#include "identify.h" -#include "inet.h" -#include "reffd.h" -#include "scan.h" +#include "fwd.h" /*----- Data structures ---------------------------------------------------*/ @@ -80,9 +35,26 @@ typedef struct inet_addrx { typedef struct inet_opts { addr_opts ao; + struct in_addr bind; +} inet_opts; + +typedef struct inet_srcopts { + inet_opts io; acl_entry *acl; acl_entry **acltail; -} inet_opts; +} inet_srcopts; + +typedef struct inet_targopts { + inet_opts io; + int ipriv; +} inet_targopts; + +#define ADDRF_PRIVCONN 16u + +static inet_srcopts inet_globalsrc = + { { { 0 }, { INADDR_ANY } }, 0, &inet_globalsrc.acl }; +static inet_targopts inet_globaltarg = + { { { 0 }, { INADDR_ANY } } }; /*----- Protocol operations -----------------------------------------------*/ @@ -164,24 +136,65 @@ static void inet_print(addr *a, unsigned type, dstr *d) /* --- @initopts@ --- */ -static addr_opts *inet_initopts(void) +static addr_opts *inet_initsrcopts(void) { - inet_opts *io = CREATE(inet_opts); + inet_srcopts *io = CREATE(inet_srcopts); + *io = inet_globalsrc; io->acl = 0; io->acltail = &io->acl; - return (&io->ao); + return (&io->io.ao); +} + +static addr_opts *inet_inittargopts(void) +{ + inet_targopts *io = CREATE(inet_targopts); + *io = inet_globaltarg; + io->ipriv = -1; + return (&io->io.ao); } /* --- @option@ --- */ -static int inet_option(scanner *sc, addr_opts *ao) +static void addropt(scanner *sc, inet_opts *io) { - inet_opts *io = (inet_opts *)ao; + dstr d = DSTR_INIT; + struct hostent *h; - CONF_BEGIN(sc, "inet", "Internet socket") + token(sc); + if (sc->t == '=') + token(sc); + if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "any") == 0) + io->bind.s_addr = INADDR_ANY; + else { + conf_name(sc, '.', &d); + if ((h = gethostbyname(d.buf)) == 0) + error(sc, "couldn't resolve address `%s'", d.buf); + memcpy(&io->bind, h->h_addr, sizeof(struct in_addr)); + } +} +static int srcopt(scanner *sc, addr_opts *ao) +{ + inet_srcopts *io = (inet_srcopts *)ao; unsigned act; + CONF_BEGIN(sc, "source", "Internet socket source") + + /* --- Initialization --- */ + + if (!io) { + if (!inet_globalsrc.acltail) + inet_globalsrc.acltail = &inet_globalsrc.acl; + io = &inet_globalsrc; + } + + /* --- Source address configuration --- */ + + if (strcmp(sc->d.buf, "addr") == 0) { + addropt(sc, &io->io); + CONF_ACCEPT; + } + /* --- Access control limitations --- */ if ((strcmp(sc->d.buf, "allow") == 0 && (act = ACL_ALLOW, 1)) || @@ -191,47 +204,62 @@ static int inet_option(scanner *sc, addr_opts *ao) struct in_addr a, m; dstr d = DSTR_INIT; - /* --- Find the host or network address --- */ + /* --- Find out what's going on --- */ token(sc); if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "from") == 0) token(sc); - conf_name(sc, '.', &d); - if ((n = getnetbyname(d.buf)) != 0) - a.s_addr = htonl(n->n_net); - else if ((h = gethostbyname(d.buf)) == 0) - error(sc, "couldn't resolve address `%s'", d.buf); - else - memcpy(&a, h->h_addr, sizeof(struct in_addr)); - /* --- Find the netmask, if any --- */ - - if (sc->t != '/') - m.s_addr = ~0ul; - else { + if (sc->t == CTOK_WORD && (strcmp(sc->d.buf, "priv") == 0 || + strcmp(sc->d.buf, "priv-port") == 0)) { + acl_addpriv(&io->acltail, act); token(sc); - DRESET(&d); + } else { + if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "host") == 0) + token(sc); + + /* --- Find the host or network address --- */ + conf_name(sc, '.', &d); - if (strchr(d.buf, '.') == 0) { - int n = atoi(d.buf); - if (n == 0) - m.s_addr = 0; +#ifdef HAVE_GETNETBYNAME + if ((n = getnetbyname(d.buf)) != 0) + a.s_addr = htonl(n->n_net); + else +#endif + if ((h = gethostbyname(d.buf)) == 0) + error(sc, "couldn't resolve address `%s'", d.buf); else - m.s_addr = htonl((~0ul << (32 - n)) & 0xffffffff); - } else { + memcpy(&a, h->h_addr, sizeof(struct in_addr)); + + /* --- Find the netmask, if any --- */ + + if (sc->t != '/') + m.s_addr = (in_addr_t)~0ul; + else { + token(sc); + DRESET(&d); + conf_name(sc, '.', &d); + if (strchr(d.buf, '.') == 0) { + int n = atoi(d.buf); + if (n == 0) + m.s_addr = 0; + else + m.s_addr = htonl((~0ul << (32 - n)) & 0xffffffff); + } else { #ifdef HAVE_INET_ATON - if (!inet_aton(d.buf, &m)) - error(sc, "bad netmask `%s'", d.buf); + if (!inet_aton(d.buf, &m)) + error(sc, "bad netmask `%s'", d.buf); #else - m.s_addr = inet_addr(d.buf); + m.s_addr = inet_addr(d.buf); #endif + } } - } - dstr_destroy(&d); + dstr_destroy(&d); - /* --- Add the access control entry --- */ + /* --- Add the access control entry --- */ - acl_add(io ? &io->acltail : 0, act, a, m); + acl_addhost(&io->acltail, act, a, m); + } CONF_ACCEPT; } @@ -240,14 +268,105 @@ static int inet_option(scanner *sc, addr_opts *ao) CONF_END; } +static int targopt(scanner *sc, addr_opts *ao) +{ + inet_targopts *io = (inet_targopts *)ao; + + CONF_BEGIN(sc, "dest", "Internet socket target"); + if (strcmp(sc->d.buf, "addr") == 0) { + addropt(sc, &io->io); + CONF_ACCEPT; + } + if (strcmp(sc->d.buf, "priv") == 0 || + strcmp(sc->d.buf, "priv-port") == 0) { + token(sc); + if (sc->t == '=') token(sc); + if (conf_enum(sc, "no,yes", ENUM_ABBREV, "privileged connection status")) + io->io.ao.f |= ADDRF_PRIVCONN; + else + io->io.ao.f &= ~ADDRF_PRIVCONN; + CONF_ACCEPT; + } + CONF_END; +} + +static int inet_option(scanner *sc, addr_opts *ao, unsigned type) +{ + CONF_BEGIN(sc, "inet", "Internet socket"); + if (type != ADDR_DEST && srcopt(sc, ao)) + CONF_ACCEPT; + if (type != ADDR_SRC && targopt(sc, ao)) + CONF_ACCEPT; + CONF_END; +} + +/* --- @confirm@ --- */ + +static void inet_confirm(addr *a, unsigned type, addr_opts *ao) +{ + inet_addrx *ia = (inet_addrx *)a; + + switch (type) { + case ADDR_DEST: { + inet_targopts *io = (inet_targopts *)ao; + if ((io->io.ao.f & ADDRF_PRIVCONN) && + (io->ipriv = privconn_adddest(ia->sin.sin_addr, + ia->sin.sin_port)) < 0) + die(1, "couldn't add privileged connection target (too late)"); + } break; + } +} + +/* --- @freeopts@ --- */ + +static void inet_freesrcopts(addr_opts *ao) +{ + inet_srcopts *io = (inet_srcopts *)ao; + acl_free(io->acl); + DESTROY(io); +} + +static void inet_freetargopts(addr_opts *ao) +{ + inet_targopts *io = (inet_targopts *)ao; + DESTROY(io); +} + +/* --- @bind@ --- */ + +static int inet_bind(addr *a, addr_opts *ao) +{ + inet_addrx *ia = (inet_addrx *)a; + inet_srcopts *io = (inet_srcopts *)ao; + struct sockaddr_in sin; + int opt = 1; + int fd; + + if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) + goto fail_0; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC); + sin = ia->sin; + sin.sin_addr = io->io.bind; + if (bind(fd, (struct sockaddr *)&sin, sizeof(ia->sin))) + goto fail_1; + return (fd); + +fail_1: + close(fd); +fail_0: + return (-1); +} + /* --- @accept@ --- */ static reffd *inet_accept(int fd, addr_opts *ao, const char *desc) { - inet_opts *io = (inet_opts *)ao; + inet_srcopts *io = (inet_srcopts *)ao; int nfd; id_req q; - size_t lsinsz = sizeof(q.lsin), rsinsz = sizeof(q.rsin); + socklen_t lsinsz = sizeof(q.lsin), rsinsz = sizeof(q.rsin); + int act = ACL_ALLOW; /* --- Accept the new connection --- */ @@ -262,9 +381,12 @@ static reffd *inet_accept(int fd, addr_opts *ao, const char *desc) /* --- Find out whether this connection is allowed --- */ - if (!acl_check(io->acl, q.rsin.sin_addr)) { + if (!acl_check(io->acl, q.rsin.sin_addr, ntohs(q.rsin.sin_port), &act)) + acl_check(inet_globalsrc.acl, q.rsin.sin_addr, + ntohs(q.rsin.sin_port), &act); + if (act != ACL_ALLOW) { q.act = "refused"; - if (!(io->ao.f & ADDRF_NOLOG)) + if (!(io->io.ao.f & ADDRF_NOLOG)) identify(&q); REFFD_DEC(q.r); return (0); @@ -273,26 +395,52 @@ static reffd *inet_accept(int fd, addr_opts *ao, const char *desc) /* --- Everything seems to be OK --- */ q.act = "accepted"; - if (!(io->ao.f & ADDRF_NOLOG)) + if (!(io->io.ao.f & ADDRF_NOLOG)) identify(&q); return (q.r); } -/* --- @freeopts@ --- */ +/* --- @connect@ --- */ -static void inet_freeopts(addr_opts *ao) +static int inet_connect(addr *a, addr_opts *ao, conn *c, endpt *e) { - inet_opts *io = (inet_opts *)ao; - acl_free(io->acl); - DESTROY(ao); + inet_addrx *ia = (inet_addrx *)a; + inet_targopts *io = (inet_targopts *)ao; + int fd; + + if (io->ipriv >= 0) { + return (privconn_connect(c, sel, io->ipriv, io->io.bind, + starget_connected, e)); + } + if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) + goto fail_0; + if (io->io.bind.s_addr != INADDR_ANY) { + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr = io->io.bind; + sin.sin_port = 0; + if (bind(fd, (struct sockaddr *)&sin, sizeof(sin))) + goto fail_1; + } + fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC); + return (conn_init(c, sel, fd, (struct sockaddr *)&ia->sin, sizeof(ia->sin), + starget_connected, e)); +fail_1: + close(fd); +fail_0: + return (-1); } /* --- Ops table --- */ addr_ops inet_ops = { - "inet", PF_INET, + "inet", inet_read, inet_destroy, inet_print, - inet_initopts, inet_option, inet_accept, inet_freeopts, 0, 0 + inet_initsrcopts, inet_option, inet_confirm, inet_freesrcopts, + inet_bind, 0, inet_accept, + inet_inittargopts, inet_freetargopts, + inet_connect }; /*----- That's all, folks -------------------------------------------------*/