struct client {
int fd; /* The connection to the client */
selbuf b; /* Accumulate lines of input */
+ union addr raddr; /* Remote address */
struct query q; /* The clients query and our reply */
struct sel_timer t; /* Timeout for idle or doomed conn */
struct listen *l; /* Back to the listener (and ops) */
struct writebuf wb; /* Write buffer for our reply */
struct proxy *px; /* Proxy if conn goes via NAT */
+ struct client *next; /* Next in a chain of clients */
};
/* A proxy connection. */
selbuf b; /* Accumulate the response line */
struct writebuf wb; /* Write buffer for query */
char nat[ADDRLEN]; /* Server address, as text */
+ struct proxy *next; /* Next in a chain of proxies */
};
/*----- Static variables --------------------------------------------------*/
static size_t tokenptr = sizeof(tokenbuf); /* Current read position */
static int randfd; /* File descriptor for random data */
+static struct client *dead_clients = 0; /* List of defunct clients */
+static struct proxy *dead_proxies = 0; /* List of defunct proxies */
+
static unsigned flags = 0; /* Various interesting flags */
#define F_SYSLOG 1u /* Use syslog for logging */
#define F_RUNNING 2u /* Running properly now */
/* Format the message FMT and queue it to be sent to the client. Client
* input will be disabled until the write completes.
*/
-static void write_to_client(struct client *c, const char *fmt, ...)
+static void PRINTF_LIKE(2, 3)
+ write_to_client(struct client *c, const char *fmt, ...)
{
va_list ap;
char buf[WRBUFSZ];
conn_kill(&px->cn);
else {
close(px->fd);
- selbuf_destroy(&px->b);
- free_writebuf(&px->wb);
+ selbuf_disable(&px->b);
}
- selbuf_enable(&px->c->b);
px->c->px = 0;
- xfree(px);
+ selbuf_enable(&px->c->b);
+ px->next = dead_proxies;
+ dead_proxies = px;
+}
+
+/* Delayed destruction of unsafe parts of proxies. */
+static void reap_dead_proxies(void)
+{
+ struct proxy *px, *pp;
+
+ for (px = dead_proxies; px; px = pp) {
+ pp = px->next;
+ if (px->fd != -1) {
+ selbuf_destroy(&px->b);
+ free_writebuf(&px->wb);
+ }
+ xfree(px);
+ }
+ dead_proxies = 0;
}
/* Notification that a line (presumably a reply) has been received from the
/* Disconnect a client, freeing up any associated resources. */
static void disconnect_client(struct client *c)
{
+ selbuf_disable(&c->b);
close(c->fd);
- selbuf_destroy(&c->b);
sel_rmtimer(&c->t);
free_writebuf(&c->wb);
if (c->px) cancel_proxy(c->px);
- xfree(c);
+ c->next = dead_clients;
+ dead_clients = c;
+}
+
+/* Throw away dead clients now that we've reached a safe point in the
+ * program.
+ */
+static void reap_dead_clients(void)
+{
+ struct client *c, *cc;
+ for (c = dead_clients; c; c = cc) {
+ cc = c->next;
+ selbuf_destroy(&c->b);
+ xfree(c);
+ }
+ dead_clients = 0;
}
/* Time out a client because it's been idle for too long. */
struct policy upol = POLICY_INIT(A_LIMIT);
struct policy_file pf;
char buf[16];
- int i;
+ int i, t;
/* If the connection has closed, then tidy stuff away. */
+ c->q.s[R].addr = c->raddr;
c->q.s[L].port = c->q.s[R].port = 0;
if (!line) {
disconnect_client(c);
*/
DRESET(&d);
dstr_putf(&d, "%s/.yaid.policy", pw->pw_dir);
- if (open_policy_file(&pf, d.buf, "user policy file", &c->q))
+ if (open_policy_file(&pf, d.buf, "user policy file", &c->q, OPF_NOENTOK))
continue;
- while (!read_policy_file(&pf)) {
+ while ((t = read_policy_file(&pf)) < T_ERROR) {
- /* Give up after 100 lines. If the user's policy is that complicated,
- * something's gone very wrong. Or there's too much commentary or
- * something.
+ /* Give up after 100 lines or if there's an error. If the user's
+ * policy is that complicated, something's gone very wrong. Or there's
+ * too much commentary or something.
*/
if (pf.lno > 100) {
logmsg(&c->q, LOG_ERR, "%s:%d: user policy file too long",
break;
}
+ /* If this was a blank line, just go around again. */
+ if (t != T_OK) continue;
+
/* If this isn't a match, go around for the next rule. */
if (!match_policy(&pf.p, &c->q)) continue;
c->q.ao = l->ao;
/* Collect the local and remote addresses. */
- l->ao->sockaddr_to_addr(&ssr, &c->q.s[R].addr);
+ l->ao->sockaddr_to_addr(&ssr, &c->raddr);
ssz = sizeof(ssl);
if (getsockname(sk, (struct sockaddr *)&ssl, &ssz)) {
logmsg(0, LOG_ERR,
}
/* Quit because of a fatal signal. */
-static void quit(int sig, void *p)
+static void NORETURN quit(int sig, void *p)
{
const char *signame = p;
for (;;) {
if (sel_select(&sel) && errno != EINTR)
die(1, "select failed: %s", strerror(errno));
+ reap_dead_proxies();
+ reap_dead_clients();
}
/* This just keeps the compiler happy. */