From 1d1ccf4f22d6f2863cd74f799f4081849bc0574a Mon Sep 17 00:00:00 2001 Message-Id: <1d1ccf4f22d6f2863cd74f799f4081849bc0574a.1716997080.git.mdw@distorted.org.uk> From: Mark Wooding Date: Mon, 22 Dec 2008 01:23:12 +0000 Subject: [PATCH] noip.c, uopen.c: Add commentary and GPL notices. Organization: Straylight/Edgeware From: Mark Wooding This makes the whole package look much more palatable. --- noip.c | 230 ++++++++++++++++++++++++++++++++++++++++++-------------- uopen.c | 52 ++++++++++++- 2 files changed, 226 insertions(+), 56 deletions(-) diff --git a/noip.c b/noip.c index 50284e1..38021db 100644 --- a/noip.c +++ b/noip.c @@ -1,8 +1,36 @@ +/* -*-c-*- + * + * Make programs use Unix-domain sockets instead of IP + * + * (c) 2008 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the preload-hacks package. + * + * Preload-hacks are free software; you can redistribute it and/or modify + * them 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. + * + * Preload-hacks 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 mLib; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + #define _GNU_SOURCE #undef sun #undef SUN #define DEBUG +/*----- Header files ------------------------------------------------------*/ + #include #include #include @@ -25,9 +53,13 @@ #include #include -enum { UNUSED, STALE, USED }; -enum { WANT_FRESH, WANT_EXISTING }; -enum { DENY, ALLOW }; +/*----- Data structures ---------------------------------------------------*/ + +enum { UNUSED, STALE, USED }; /* Unix socket status values */ +enum { WANT_FRESH, WANT_EXISTING }; /* Socket address dispositions */ +enum { DENY, ALLOW }; /* ACL verdicts */ + +/* Access control list nodes */ typedef struct aclnode { struct aclnode *next; int act; @@ -35,20 +67,24 @@ typedef struct aclnode { unsigned short minport, maxport; } aclnode; +/* Local address records */ #define MAX_LOCAL_IPADDRS 16 static struct in_addr local_ipaddrs[MAX_LOCAL_IPADDRS]; static int n_local_ipaddrs; +/* General configuration */ static uid_t uid; static char *sockdir = 0; static int debug = 0; static unsigned minautoport = 16384, maxautoport = 65536; +/* Access control lists */ static aclnode *bind_real, **bind_tail = &bind_real; static aclnode *connect_real, **connect_tail = &connect_real; -/* --- Import magic --- */ +/*----- Import the real versions of functions -----------------------------*/ +/* The list of functions to immport. */ #define IMPORTS(_) \ _(socket, int, (int, int, int)) \ _(socketpair, int, (int, int, int, int *)) \ @@ -67,11 +103,12 @@ static aclnode *connect_real, **connect_tail = &connect_real; _(recvmsg, ssize_t, (int, struct msghdr *, int)) \ _(close, int, (int)) +/* Function pointers to set up. */ #define DECL(imp, ret, args) static ret (*real_##imp) args; IMPORTS(DECL) #undef DECL -static void setup(void) __attribute__((constructor)); +/* Import the system calls. */ static void import(void) { #define IMPORT(imp, ret, args) \ @@ -80,27 +117,33 @@ static void import(void) #undef IMPORT } -/* --- Support --- */ +/*----- Utilities ---------------------------------------------------------*/ +/* Socket address casts */ #define SA(sa) ((struct sockaddr *)(sa)) #define SIN(sa) ((struct sockaddr_in *)(sa)) #define SUN(sa) ((struct sockaddr_un *)(sa)) +/* Raw bytes */ #define UC(ch) ((unsigned char)(ch)) +/* Memory allocation */ #define NEW(x) ((x) = xmalloc(sizeof(*x))) #define NEWV(x, n) ((x) = xmalloc(sizeof(*x) * (n))) +/* Debugging */ #ifdef DEBUG # define D(body) { if (debug) { body } } #else # define D(body) ; #endif +/* Preservation of error status */ #define PRESERVING_ERRNO(body) do { \ int _err = errno; { body } errno = _err; \ } while (0) +/* Allocate N bytes of memory; abort on failure. */ static void *xmalloc(size_t n) { void *p; @@ -109,6 +152,7 @@ static void *xmalloc(size_t n) return (p); } +/* Allocate a copy of the null-terminated string P; abort on failure. */ static char *xstrdup(const char *p) { size_t n = strlen(p) + 1; @@ -116,40 +160,11 @@ static char *xstrdup(const char *p) memcpy(q, p, n); return (q); } - -static int unix_socket_status(struct sockaddr_un *sun, int quickp) -{ - struct stat st; - FILE *fp = 0; - size_t len, n; - int rc; - char buf[256]; - - if (stat(sun->sun_path, &st)) - return (errno == ENOENT ? UNUSED : USED); - if (!S_ISSOCK(st.st_mode) || quickp) - return (USED); - rc = USED; - if ((fp = fopen("/proc/net/unix", "r")) == 0) - goto done; - fgets(buf, sizeof(buf), fp); /* skip header */ - len = strlen(sun->sun_path); - while (fgets(buf, sizeof(buf), fp)) { - n = strlen(buf); - if (n >= len + 2 && buf[n - len - 2] == ' ' && buf[n - 1] == '\n' && - memcmp(buf + n - len - 1, sun->sun_path, len) == 0) - goto done; - } - if (ferror(fp)) - goto done; - rc = STALE; -done: - if (fp) fclose(fp); - return (rc); -} +/*----- Access control lists ----------------------------------------------*/ #ifdef DEBUG +/* Write to standard error a description of the ACL node A. */ static void dump_aclnode(aclnode *a) { char minbuf[16], maxbuf[16]; @@ -176,8 +191,21 @@ static void dump_aclnode(aclnode *a) fputc('\n', stderr); } +static void dump_acl(aclnode *a) +{ + int act = ALLOW; + + for (; a; a = a->next) { + dump_aclnode(a); + act = a->act; + } + fprintf(stderr, "noip: [default policy: %s]\n", + act == ALLOW ? "DENY" : "ALLOW"); +} + #endif +/* Returns nonzero if the ACL A allows the IP socket SIN. */ static int acl_allows_p(aclnode *a, const struct sockaddr_in *sin) { unsigned long addr = ntohl(sin->sin_addr.s_addr); @@ -201,22 +229,9 @@ static int acl_allows_p(aclnode *a, const struct sockaddr_in *sin) return (!act); } -#ifdef DEBUG - -static void dump_acl(aclnode *a) -{ - int act = ALLOW; - - for (; a; a = a->next) { - dump_aclnode(a); - act = a->act; - } - fprintf(stderr, "noip: [default policy: %s]\n", - act == ALLOW ? "DENY" : "ALLOW"); -} - -#endif +/*----- Socket address conversion -----------------------------------------*/ +/* Return a uniformly distributed integer between MIN and MAX inclusive. */ static unsigned randrange(unsigned min, unsigned max) { unsigned mask, i; @@ -230,6 +245,48 @@ static unsigned randrange(unsigned min, unsigned max) return (i + min); } +/* Return the status of Unix-domain socket address SUN. Returns: UNUSED if + * the socket doesn't exist; USED if the path refers to an active socket, or + * isn't really a socket at all, or we can't tell without a careful search + * and QUICKP is set; or STALE if the file refers to a socket which isn't + * being used any more. + */ +static int unix_socket_status(struct sockaddr_un *sun, int quickp) +{ + struct stat st; + FILE *fp = 0; + size_t len, n; + int rc; + char buf[256]; + + if (stat(sun->sun_path, &st)) + return (errno == ENOENT ? UNUSED : USED); + if (!S_ISSOCK(st.st_mode) || quickp) + return (USED); + rc = USED; + if ((fp = fopen("/proc/net/unix", "r")) == 0) + goto done; + fgets(buf, sizeof(buf), fp); /* skip header */ + len = strlen(sun->sun_path); + while (fgets(buf, sizeof(buf), fp)) { + n = strlen(buf); + if (n >= len + 2 && buf[n - len - 2] == ' ' && buf[n - 1] == '\n' && + memcmp(buf + n - len - 1, sun->sun_path, len) == 0) + goto done; + } + if (ferror(fp)) + goto done; + rc = STALE; +done: + if (fp) fclose(fp); + return (rc); +} + +/* Encode the Internet address SIN as a Unix-domain address SUN. If WANT is + * WANT_FRESH, and SIN->sin_port is zero, then we pick an arbitrary local + * port. Otherwise we pick the port given. There's an unpleasant hack to + * find servers bound to INADDR_ANY. Returns zero on success; -1 on failure. + */ static int encode_inet_addr(struct sockaddr_un *sun, const struct sockaddr_in *sin, int want) @@ -283,6 +340,8 @@ static int encode_inet_addr(struct sockaddr_un *sun, return (0); } +/* Decode the Unix address SUN to an Internet address SIN. Returns zero on + * success; -1 on failure (e.g., it wasn't one of our addresses). */ static int decode_inet_addr(struct sockaddr_in *sin, const struct sockaddr_un *sun, socklen_t len) @@ -335,6 +394,10 @@ static int decode_inet_addr(struct sockaddr_in *sin, return (0); } +/* SK is (or at least might be) a Unix-domain socket we created when an + * Internet socket was asked for. We've decided it should be an Internet + * socket after all, so convert it. + */ static int fixup_real_ip_socket(int sk) { int nsk; @@ -395,6 +458,9 @@ static int fixup_real_ip_socket(int sk) return (0); } +/* The socket SK is about to be used to communicate with the remote address + * SA. Assign it a local address so that getpeername does something useful. + */ static int do_implicit_bind(int sk, const struct sockaddr **sa, socklen_t *len, struct sockaddr_un *sun) { @@ -425,6 +491,11 @@ static int do_implicit_bind(int sk, const struct sockaddr **sa, return (0); } +/* We found the real address SA, with length LEN; if it's a Unix-domain + * address corresponding to a fake socket, convert it to cover up the + * deception. Whatever happens, put the result at FAKE and store its length + * at FAKELEN. + */ static void return_fake_name(struct sockaddr *sa, socklen_t len, struct sockaddr *fake, socklen_t *fakelen) { @@ -443,8 +514,9 @@ static void return_fake_name(struct sockaddr *sa, socklen_t len, *fakelen = alen; } -/* --- Configuration --- */ +/*----- Configuration -----------------------------------------------------*/ +/* Return the process owner's home directory. */ static char *home(void) { char *p; @@ -459,6 +531,7 @@ static char *home(void) return "/notexist"; } +/* Return a good temporary directory to use. */ static char *tmpdir(void) { char *p; @@ -468,6 +541,7 @@ static char *tmpdir(void) else return ("/tmp"); } +/* Return the user's name, or at least something distinctive. */ static char *user(void) { static char buf[16]; @@ -483,13 +557,20 @@ static char *user(void) } } +/* Skip P over space characters. */ #define SKIPSPC do { while (*p && isspace(UC(*p))) p++; } while (0) + +/* Set Q to point to the next word following P, null-terminate it, and step P + * past it. */ #define NEXTWORD(q) do { \ SKIPSPC; \ q = p; \ while (*p && !isspace(UC(*p))) p++; \ if (*p) *p++ = 0; \ } while (0) + +/* Set Q to point to the next dotted-quad address, store the ending delimiter + * in DEL, null-terminate it, and step P past it. */ #define NEXTADDR(q, del) do { \ SKIPSPC; \ q = p; \ @@ -497,6 +578,9 @@ static char *user(void) del = *p; \ if (*p) *p++ = 0; \ } while (0) + +/* Set Q to point to the next decimal number, store the ending delimiter in + * DEL, null-terminate it, and step P past it. */ #define NEXTNUMBER(q, del) do { \ SKIPSPC; \ q = p; \ @@ -504,11 +588,23 @@ static char *user(void) del = *p; \ if (*p) *p++ = 0; \ } while (0) + +/* Push the character DEL back so we scan it again, unless it's zero + * (end-of-file). */ #define RESCAN(del) do { if (del) *--p = del; } while (0) + +/* Evaluate true if P is pointing to the word KW (and not some longer string + * of which KW is a prefix). */ + #define KWMATCHP(kw) (strncmp(p, kw, sizeof(kw) - 1) == 0 && \ !isalnum(UC(p[sizeof(kw) - 1])) && \ (p += sizeof(kw) - 1)) +/* Parse a port list, starting at *PP. Port lists have the form + * [:LOW[-HIGH]]: if omitted, all ports are included; if HIGH is omitted, + * it's as if HIGH = LOW. Store LOW in *MIN, HIGH in *MAX and set *PP to the + * rest of the string. + */ static void parse_ports(char **pp, unsigned short *min, unsigned short *max) { char *p = *pp, *q; @@ -529,6 +625,10 @@ static void parse_ports(char **pp, unsigned short *min, unsigned short *max) *pp = p; } +/* Make a new ACL node. ACT is the verdict; MINADDR and MAXADDR are the + * ranges on IP addresses; MINPORT and MAXPORT are the ranges on port + * numbers; TAIL is the list tail to attach the new node to. + */ #define ACLNODE(tail_, act_, \ minaddr_, maxaddr_, minport_, maxport_) do { \ aclnode *a_; \ @@ -539,6 +639,12 @@ static void parse_ports(char **pp, unsigned short *min, unsigned short *max) *tail_ = a_; tail_ = &a_->next; \ } while (0) +/* Parse an ACL line. *PP points to the end of the line; *TAIL points to + * the list tail (i.e., the final link in the list). An ACL entry has the + * form +|- [any | local | ADDR | ADDR - ADDR | ADDR/ADDR | ADDR/INT] PORTS + * where PORTS is parsed by parse_ports above; an ACL line consists of a + * comma-separated sequence of entries.. + */ static void parse_acl_line(char **pp, aclnode ***tail) { struct in_addr addr; @@ -617,6 +723,7 @@ bad: return; } +/* Parse the autoports configuration directive. Syntax is MIN - MAX. */ static void parse_autoports(char **pp) { char *p = *pp, *q; @@ -636,6 +743,8 @@ bad: return; } +/* Parse an ACL from an environment variable VAR, attaching it to the list + * TAIL. */ static void parse_acl_env(const char *var, aclnode ***tail) { char *p, *q; @@ -647,6 +756,7 @@ static void parse_acl_env(const char *var, aclnode ***tail) } } +/* Read the configuration from the config file and environment. */ static void readconfig(void) { FILE *fp; @@ -717,7 +827,7 @@ done: dump_acl(connect_real); ) } -/* --- Hooks --- */ +/*----- Overridden system calls -------------------------------------------*/ int socket(int pf, int ty, int proto) { @@ -916,8 +1026,9 @@ int setsockopt(int sk, int lev, int opt, const void *p, socklen_t len) return real_setsockopt(sk, lev, opt, p, len); } -/* --- Initialization --- */ +/*----- Initialization ----------------------------------------------------*/ +/* Clean up the socket directory, deleting stale sockets. */ static void cleanup_sockdir(void) { DIR *dir; @@ -949,6 +1060,9 @@ static void cleanup_sockdir(void) closedir(dir); } +/* Find the addresses attached to local network interfaces, and remember them + * in a table. + */ static void get_local_ipaddrs(void) { struct if_nameindex *ifn; @@ -974,8 +1088,10 @@ static void get_local_ipaddrs(void) close(sk); } +/* Print the given message to standard error. Avoids stdio. */ static void printerr(const char *p) { write(STDERR_FILENO, p, strlen(p)); } +/* Create the socket directory, being careful about permissions. */ static void create_sockdir(void) { struct stat st; @@ -1007,6 +1123,8 @@ check: } } +/* Initialization function. */ +static void setup(void) __attribute__((constructor)); static void setup(void) { PRESERVING_ERRNO({ @@ -1022,3 +1140,5 @@ static void setup(void) cleanup_sockdir(); }); } + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/uopen.c b/uopen.c index 206ec75..f3887c5 100644 --- a/uopen.c +++ b/uopen.c @@ -1,6 +1,34 @@ +/* -*-c-*- + * + * Allow open(2) to work on Unix-domain sockets + * + * (c) 2008 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the preload-hacks package. + * + * Preload-hacks are free software; you can redistribute it and/or modify + * them 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. + * + * Preload-hacks 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 mLib; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + #define _GNU_SOURCE #undef sun +/*----- Header files ------------------------------------------------------*/ + #include #include #include @@ -14,17 +42,21 @@ #include #include +/*----- Import the real versions of functions -----------------------------*/ + +/* The list of functions to immport. */ #define IMPORTS(_) \ _(open, int, (const char *, int, ...)) \ _(open64, int, (const char *, int, ...)) \ _(fopen, FILE *, (const char *, const char *)) \ _(freopen, FILE *, (const char *, const char *, FILE *)) +/* Function pointers to set up. */ #define DECL(imp, ret, args) static ret (*real_##imp) args; IMPORTS(DECL) #undef DECL -static void setup(void) __attribute__((constructor)); +/* Import the system calls. */ static void import(void) { #define IMPORT(imp, ret, args) \ @@ -32,13 +64,21 @@ static void import(void) IMPORTS(IMPORT) #undef IMPORT } +/*----- Utilities ---------------------------------------------------------*/ +/* Socket address casts */ #define SA(sa) ((struct sockaddr *)(sa)) +/* Preservation of error status */ #define PRESERVING_ERRNO(body) do { \ int _err = errno; { body } errno = _err; \ } while (0) +/*----- Connecting to Unix-domain sockets ---------------------------------*/ + +/* If FN refers to a Unix-domain socket, connect to it, stash the socket + * (or -1 on error) in *FDP, and return 1. Otherwise return 0. + */ static int maybe_connect(const char *fn, int *fdp) { int fd; @@ -63,6 +103,9 @@ static int maybe_connect(const char *fn, int *fdp) return (1); } +/*----- Intercepted system calls ------------------------------------------*/ + +/* Create an open(2)-like function OPEN. */ #define OPEN_VENEER(open) \ int open(const char *fn, int how, ...) \ { \ @@ -78,6 +121,8 @@ static int maybe_connect(const char *fn, int *fdp) }); \ return (fd); \ } + +/* open(2) and open64(2). */ OPEN_VENEER(open) OPEN_VENEER(open64) @@ -110,9 +155,14 @@ FILE *freopen(const char *fn, const char *how, FILE *fp) return (fp); } +/*----- Initialization ----------------------------------------------------*/ + +static void setup(void) __attribute__((constructor)); static void setup(void) { PRESERVING_ERRNO({ import(); }); } + +/*----- That's all, folks -------------------------------------------------*/ -- [mdw]