From 6557136426498ef5e6ab757b0cc170c55096acd7 Mon Sep 17 00:00:00 2001 From: Martin Langhoff Date: Sun, 24 Jan 2010 14:39:15 +0100 Subject: [PATCH] ejabberdctl: support concurrent connections with bound conn names If flock is available, ejabberdctl will use it to grab one of a bound number of connection names. This allows concurrent connections while using a bound number of atoms. Using PID, timestamps or random strings for transient connection IDs (which would avoid the need for flock) uses an unbound number of atoms. This can effectively DoS servers, as these connection names are not garbage collected. --- src/Makefile.in | 10 ++++++ src/ejabberdctl.template | 78 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/src/Makefile.in b/src/Makefile.in index 1a52db44e..3aa979ebe 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -108,6 +108,9 @@ MSGSDIR = $(PRIVDIR)/msgs # /var/lib/ejabberd/ SPOOLDIR = $(DESTDIR)@localstatedir@/lib/ejabberd +# /var/lock/ejabberdctl +CTLLOCKDIR = $(DESTDIR)@localstatedir@/lock/ejabberdctl + # /var/lib/ejabberd/.erlang.cookie COOKIEFILE = $(SPOOLDIR)/.erlang.cookie @@ -226,6 +229,12 @@ install: all install -d -m 750 $(O_USER) $(SPOOLDIR) $(CHOWN_COMMAND) -R @INSTALLUSER@ $(SPOOLDIR) >$(CHOWN_OUTPUT) chmod -R 750 $(SPOOLDIR) + + # ejabberdctl lock directory + install -d -m 750 $(O_USER) $(CTLLOCKDIR) + $(CHOWN_COMMAND) -R @INSTALLUSER@ $(CTLLOCKDIR) >$(CHOWN_OUTPUT) + chmod -R 750 $(CTLLOCKDIR) + [ ! -f $(COOKIEFILE) ] || { $(CHOWN_COMMAND) @INSTALLUSER@ $(COOKIEFILE) >$(CHOWN_OUTPUT) ; chmod 400 $(COOKIEFILE) ; } # # Log directory @@ -261,6 +270,7 @@ uninstall-all: uninstall-binary rm -rf $(ETCDIR) rm -rf $(EJABBERDDIR) rm -rf $(SPOOLDIR) + rm -rf $(CTLLOCKDIR) rm -rf $(LOGDIR) clean: clean-recursive clean-local clean-devdoc diff --git a/src/ejabberdctl.template b/src/ejabberdctl.template index 2df7be710..f5af13be2 100644 --- a/src/ejabberdctl.template +++ b/src/ejabberdctl.template @@ -14,6 +14,17 @@ ERLANG_NODE=$NODE@$HOST ERL=@erl@ INSTALLUSER=@installuser@ +# Control number of connections identifiers +# using flock if available. Expects a linux-style +# flock that can lock a file descriptor. +MAXCONNID=100 +CONNLOCKDIR=@LOCALSTATEDIR@/lock/ejabberdctl +FLOCK='/usr/bin/flock' +if [ ! -x "$FLOCK" ];then + FLOCK="" +fi + + # parse command line parameters ARGS= while [ $# -ne 0 ] ; do @@ -228,13 +239,54 @@ help () ctl () { COMMAND=$@ - $EXEC_CMD "$ERL \ - $NAME ctl-${ERLANG_NODE} \ - -noinput \ - -hidden \ - -pa $EJABBERD_EBIN_PATH \ - -s ejabberd_ctl -extra $ERLANG_NODE $COMMAND" - result=$? + + if [ ! "$FLOCK" ];then + # no flock, simply invoke ctlexec() + CTL_CONN="ctl-${EJABBERD_NODE}" + ctlexec $CTL_CONN $COMMAND + result=$? + else + # we have flock so we get a lock + # on one of a limited number of + # conn names -- this allows + # concurrent invokations using a bound + # number of atoms + for N in $(seq 1 $MAXCONNID); do + CTL_CONN="ejabberdctl-$N" + CTL_LOCKFILE="$CONNLOCKDIR/$CTL_CONN" + ( + exec 8>"$CTL_LOCKFILE" + if flock --nb 8; then + ctlexec $CTL_CONN $COMMAND + ssresult=$? + # segregate from possible flock exit(1) + ssresult=$(expr $ssresult \* 10) + exit $ssresult + else + exit 1 + fi + ) + result=$? + if [ $result -eq 1 ]; then + # means we errored out in flock + # rather than in the exec - stay in the loop + # trying other conn names... + badlock=1 + else + badlock="" + break; + fi + done + result=$(expr $result / 10) + fi + + if [ "$badlock" ];then + echo "Ran out of connections to try. Your ejabberd processes" >&2 + echo "may be stuck or this is a very busy server. For very" >&2 + echo "busy servers, consider raising MAXCONNIDS" >&2 + exit 1; + fi + case $result in 0) :;; 1) :;; @@ -244,6 +296,18 @@ ctl () return $result } +ctlexec () +{ + CONN_NAME=$1; shift + COMMAND=$@ + $EXEC_CMD "$ERL \ + $NAME ${CONN_NAME} \ + -noinput \ + -hidden \ + -pa $EJABBERD_EBIN_PATH \ + -s ejabberd_ctl -extra $ERLANG_NODE $COMMAND" +} + # display ctl usage usage () {