/* -*-c-*-
*
- * $Id: inet.c,v 1.4 2002/01/13 14:49: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.4 2002/01/13 14:49:56 mdw
- * Conditional compilation for @getnetbyname@, since Cygwin doesn't have
- * it.
- *
- * 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 <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-
-#include <mLib/alloc.h>
-#include <mLib/dstr.h>
-#include <mLib/report.h>
-#include <mLib/sub.h>
-
-#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 ---------------------------------------------------*/
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 -----------------------------------------------*/
/* --- @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)) ||
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);
-#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
- 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;
}
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 --- */
/* --- 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);
/* --- 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 -------------------------------------------------*/