aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-12-23 23:51:39 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2009-12-23 23:57:48 +0200
commita37a83f6143e672a71ee4436fa24aaa2f9c81877 (patch)
treec5791a295aebf12bf7865f1c100dc3ee72bbc79b
parentbb72dbbbbac7806a6321a404ec4c852b63e819b7 (diff)
downloadpies-a37a83f6143e672a71ee4436fa24aaa2f9c81877.tar.gz
pies-a37a83f6143e672a71ee4436fa24aaa2f9c81877.tar.bz2
Limit number of connections per socket (IP).
* src/inetd-bi.c (fd_write): Remove. Use fd_report instead. * src/pies.c (component_keywords): New keywords: max-instances-message, max-ip-connections, max-ip-connections-message, access-denied-message. * src/pies.h (struct component): New members: max_ip_connections, access_denied_message, max_instances_message, max_ip_connections_message. (fd_report): New extern. * src/progman.c (conn_class): New struct. (struct prog.p): New member cclass. (conn_tab): New static. (conn_class_lookup, conn_class_report): New functions. (progman_run_comp): Set cclass. (fd_report): New function. (_prog_accept): In case of failure (access denied, etc.) optionally send response strings over the fd. Limit number of connections per socket (IP). (progman_cleanup): Update cclass counter.
-rw-r--r--src/inetd-bi.c21
-rw-r--r--src/pies.c25
-rw-r--r--src/pies.h8
-rw-r--r--src/progman.c161
4 files changed, 199 insertions, 16 deletions
diff --git a/src/inetd-bi.c b/src/inetd-bi.c
index d002e4c..b771936 100644
--- a/src/inetd-bi.c
+++ b/src/inetd-bi.c
@@ -296,20 +296,14 @@ fd_getline (int fd, char *buf, int len)
}
static int
-fd_write (int fd, const char *text)
-{
- return write (fd, text, strlen (text));
-}
-
-static int
tcpmux_help (struct component *comp, void *data)
{
int *pfd = data;
if (!(comp->flags & CF_DISABLED) && ISCF_TCPMUX (comp->flags))
{
- fd_write (*pfd, comp->service);
- fd_write (*pfd, "\r\n");
+ fd_report (*pfd, comp->service);
+ fd_report (*pfd, "\r\n");
}
return 0;
}
@@ -327,7 +321,7 @@ tcpmux (int fd, struct component const *comp)
/* Read service name */
if ((len = fd_getline (fd, service, MAX_SERV_LEN)) < 0)
{
- fd_write (fd, "-Error reading service name\r\n");
+ fd_report (fd, "-Error reading service name\r\n");
return;
}
service[len] = 0;
@@ -343,7 +337,7 @@ tcpmux (int fd, struct component const *comp)
srv_comp = progman_lookup_tcpmux (service, comp->tag);
if (!srv_comp)
{
- fd_write (fd, "-Service not available\r\n");
+ fd_report (fd, "-Service not available\r\n");
return;
}
@@ -356,19 +350,20 @@ tcpmux (int fd, struct component const *comp)
{
if (rc)
{
- fd_write (fd, "-Service not available\r\n");
+ fd_report (fd, "-Service not available\r\n");
return;
}
if (check_acl (comp->acl, (struct sockaddr *) &sa, salen))
{
- fd_write (fd, "-Service not available\r\n");
+ fd_report (fd, "-Service not available\r\n");
return;
}
}
+ /* FIXME: What about max-instances, etc.? */
if (srv_comp->flags & CF_TCPMUXPLUS)
- fd_write (fd, "+Go\r\n");
+ fd_report (fd, "+Go\r\n");
progman_run_comp (srv_comp, fd, &sa, salen);
}
diff --git a/src/pies.c b/src/pies.c
index 91fabca..29f3ed0 100644
--- a/src/pies.c
+++ b/src/pies.c
@@ -988,6 +988,25 @@ struct grecs_keyword component_keywords[] = {
grecs_type_size, NULL,
offsetof (struct component, max_instances),
NULL },
+ {"max-instances-message",
+ NULL,
+ N_("Text to send back if max-instances is reached (inetd-components only)."),
+ grecs_type_string, NULL,
+ offsetof (struct component, max_instances_message),
+ NULL },
+ {"max-ip-connections",
+ NULL,
+ N_("Maximum number of simultaneous connections per IP address (inetd only)."),
+ grecs_type_size, NULL,
+ offsetof (struct component, max_ip_connections),
+ NULL },
+ {"max-ip-connections-message",
+ NULL,
+ N_("Text to send back if max-ip-connections-message is reached (inetd only)."),
+ grecs_type_string, NULL,
+ offsetof (struct component, max_ip_connections_message),
+ NULL },
+
{"max-rate",
NULL,
N_("Maximum number of times an inetd component can be invoked in one minute."),
@@ -1020,6 +1039,12 @@ struct grecs_keyword component_keywords[] = {
N_("Set ACL."),
grecs_type_section, NULL, offsetof (struct component, acl),
acl_section_parser, NULL, acl_keywords},
+ {"access-denied-message",
+ NULL,
+ N_("Text to send back if access is denied (inetd-components only)."),
+ grecs_type_string, NULL,
+ offsetof (struct component, access_denied_message),
+ NULL },
{"remove-file",
N_("file"),
N_("Remove file before starting the component."),
diff --git a/src/pies.h b/src/pies.h
index d6347ce..25db08a 100644
--- a/src/pies.h
+++ b/src/pies.h
@@ -177,6 +177,7 @@ struct component
/* For inetd components */
size_t max_rate; /* Maximum number of invocations per minute */
+ size_t max_ip_connections; /* Max. number of connections per IP address */
int socket_type; /* Socket type */
struct inetd_builtin *builtin; /* Builtin function */
char *service;
@@ -189,6 +190,12 @@ struct component
become available. */
pies_acl_t acl;
char *tcpmux; /* Master service for TCPMUX */
+
+ /* Optional error messages to be sent back on the socket: */
+ char *access_denied_message;
+ char *max_instances_message;
+ char *max_ip_connections_message;
+
/* Redirectors: */
int facility; /* Syslog facility. */
struct redirector redir[2]; /* Repeaters for stdout and stderr */
@@ -239,6 +246,7 @@ void progman_run_comp (struct component *comp, int fd,
void progman_iterate_comp (int (*fun) (struct component *, void *),
void *data);
+void fd_report (int fd, const char *msg);
int check_acl (pies_acl_t acl, struct sockaddr *s, socklen_t salen);
diff --git a/src/progman.c b/src/progman.c
index 9143b99..5e8d331 100644
--- a/src/progman.c
+++ b/src/progman.c
@@ -15,6 +15,7 @@
along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
#include "pies.h"
+#include <hash.h>
enum prog_type
{
@@ -34,6 +35,14 @@ enum prog_status
status_stopping, /* Component is being stopped */
};
+struct conn_class
+{
+ const char *tag;
+ union pies_sockaddr_storage sa_storage;
+ size_t sa_len;
+ size_t count;
+};
+
struct prog
{
struct prog *next, *prev;
@@ -59,6 +68,7 @@ struct prog
struct prog *listener;
union pies_sockaddr_storage sa_storage;
size_t sa_len;
+ struct conn_class *cclass;
} p;
struct
@@ -79,6 +89,7 @@ static size_t numcomp;
static struct prog *proghead, *progtail;
static pies_depmap_t depmap;
static int recompute_alarm;
+static Hash_table *conn_tab;
void
progman_iterate_comp (int (*fun) (struct component *, void *), void *data)
@@ -510,6 +521,96 @@ open_redirector (struct prog *master, int stream)
}
+static size_t
+conn_class_hasher (void const *data, size_t n_buckets)
+{
+ struct conn_class const *pcclass = data;
+ unsigned char const *tag = (unsigned char const *)pcclass->tag;
+ size_t value = 0;
+ unsigned char ch;
+ size_t len;
+
+ while ((ch = *tag++))
+ value = (value * 31 + ch) % n_buckets;
+
+ for (tag = (const unsigned char *)&pcclass->sa_storage,
+ len = pcclass->sa_len;
+ len;
+ tag++, len--)
+ value = (value * 31 + *tag) % n_buckets;
+ return value;
+}
+
+/* Compare two strings for equality. */
+static bool
+conn_class_compare (void const *data1, void const *data2)
+{
+ struct conn_class const *p1 = data1;
+ struct conn_class const *p2 = data2;
+
+ return p1->sa_len == p2->sa_len &&
+ memcmp (&p1->sa_storage, &p2->sa_storage, p1->sa_len) == 0 &&
+ strcmp (p1->tag, p2->tag) == 0;
+}
+
+static void
+conn_class_free (void *data)
+{
+ free (data);
+}
+
+static struct conn_class *
+conn_class_lookup (const char *tag,
+ union pies_sockaddr_storage const *sa_storage_ptr,
+ size_t sa_len)
+{
+ struct conn_class *probe, *ret;
+
+ probe = xmalloc (sizeof (probe[0]));
+ probe->tag = tag;
+ probe->sa_storage = *sa_storage_ptr;
+ probe->sa_len = sa_len;
+ switch (probe->sa_storage.s.sa_family)
+ {
+ case AF_INET:
+ probe->sa_storage.s_in.sin_port = 0;
+ break;
+
+ case AF_UNIX:
+ break;
+
+ default:
+ logmsg (LOG_ERR, _("unexpected socket family: %d"),
+ probe->sa_storage.s.sa_family);
+ break;
+ }
+
+ probe->count = 0;
+
+ if (!((conn_tab
+ || (conn_tab = hash_initialize (0, 0,
+ conn_class_hasher,
+ conn_class_compare,
+ conn_class_free)))
+ && (ret = hash_insert (conn_tab, probe))))
+ xalloc_die ();
+
+ if (probe != ret)
+ free (probe);
+ return ret;
+}
+
+static void
+conn_class_report (struct conn_class *pcclass)
+{
+ char *s = sockaddr_to_astr ((struct sockaddr *)&pcclass->sa_storage,
+ pcclass->sa_len);
+ logmsg (LOG_DEBUG, _("connections in class %s/%s: %lu"),
+ pcclass->tag, s, (unsigned long)pcclass->count);
+ free (s);
+}
+
+
extern char **environ; /* Environment */
static size_t envsize; /* Size of environ. If it is not 0, then we
have allocated space for the environ. */
@@ -963,6 +1064,7 @@ progman_run_comp (struct component *comp, int fd,
prog->v.p.socket = fd;
prog->v.p.sa_storage = *sa;
prog->v.p.sa_len = salen;
+ prog->v.p.cclass = conn_class_lookup (comp->tag, sa, salen);
prog_start_prologue (prog);
prog_sockenv (prog);
prog_execute (prog);
@@ -1149,6 +1251,31 @@ check_acl (pies_acl_t acl, struct sockaddr *s, socklen_t salen)
return 0;
}
+void
+fd_report (int fd, const char *msg)
+{
+ size_t len;
+
+ if (!msg)
+ return;
+
+ for (len = strlen (msg); len; )
+ {
+ ssize_t rc = write (fd, msg, len);
+ if (rc == -1)
+ {
+ logmsg (LOG_ERR,
+ _("error writing to socket: %s"),
+ strerror (errno));
+ break;
+ }
+ else if (rc == 0)
+ break;
+ len -= rc;
+ msg += rc;
+ }
+}
+
static int
_prog_accept (struct prog *p)
{
@@ -1156,7 +1283,8 @@ _prog_accept (struct prog *p)
struct prog *pinst;
union pies_sockaddr_storage addr;
socklen_t addrlen = sizeof addr;
-
+ struct conn_class *pcclass;
+
fd = accept (p->v.p.socket, (struct sockaddr*) &addr, &addrlen);
if (fd == -1)
{
@@ -1174,6 +1302,7 @@ _prog_accept (struct prog *p)
if (check_acl (p->v.p.comp->acl, (struct sockaddr *)&addr, addrlen)
|| check_acl (pies_acl, (struct sockaddr *)&addr, addrlen))
{
+ fd_report (fd, p->v.p.comp->access_denied_message);
close (fd);
return 1;
}
@@ -1183,25 +1312,47 @@ _prog_accept (struct prog *p)
{
char *s = sockaddr_to_astr ((struct sockaddr *)&addr, addrlen);
logmsg (LOG_ERR,
- _("%s: too many instances running, access from %s denied"),
+ _("%s: access from %s denied: too many instances running"),
p->tag, s);
free (s);
+ fd_report (fd, p->v.p.comp->max_instances_message);
close (fd);
return 1;
}
+ pcclass = conn_class_lookup (p->tag, &addr, addrlen);
+ if (p->v.p.comp->max_ip_connections &&
+ pcclass->count >= p->v.p.comp->max_ip_connections)
+ {
+ char *s = sockaddr_to_astr ((struct sockaddr *)&addr, addrlen);
+ logmsg (LOG_ERR,
+ _("%s: access from %s denied: "
+ "too many connections from that ip"),
+ p->tag, s);
+ free (s);
+ fd_report (fd, p->v.p.comp->max_ip_connections_message);
+ close (fd);
+ return 1;
+ }
+
if (check_connection_rate (p))
{
disable_socket (p->v.p.socket);
close (fd);
return 1;
}
-
+
+ pcclass->count++;
+
+ if (debug_level > 1)
+ conn_class_report (pcclass);
+
pinst = register_prog0 (p->v.p.comp, -1);
pinst->v.p.socket = fd;
pinst->v.p.listener = p;
pinst->v.p.sa_storage = addr;
pinst->v.p.sa_len = addrlen;
+ pinst->v.p.cclass = pcclass;
prog_start (pinst);
close (fd);
pinst->v.p.socket = -1;
@@ -2142,6 +2293,10 @@ progman_cleanup (int expect_term)
struct prog *listener = prog->v.p.listener;
listener->v.p.num_instances--;
+ prog->v.p.cclass->count--;
+ if (debug_level > 1)
+ conn_class_report (prog->v.p.cclass);
+
prog_stop_redirectors (prog);
destroy_prog (&prog);
react (listener, status, pid);

Return to:

Send suggestions and report system problems to the System administrator.