Added socat-broker.sh for group communications
diff --git a/Makefile.in b/Makefile.in
index a01fb0a..fdbcf67 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -76,7 +76,7 @@
 
 
 DOCFILES = README README.FIPS CHANGES FILES EXAMPLES PORTING SECURITY DEVELOPMENT doc/socat.yo doc/socat.1 doc/socat.html FAQ BUGREPORTS COPYING COPYING.OpenSSL doc/dest-unreach.css doc/socat-openssltunnel.html doc/socat-multicast.html doc/socat-tun.html doc/socat-genericsocket.html
-SHFILES = socat-chain.sh socat-mux.sh \
+SHFILES = socat-chain.sh socat-mux.sh socat-broker.sh \
 	daemon.sh mail.sh ftp.sh readline.sh \
 	socat_buildscript_for_android.sh
 TESTFILES = test.sh socks4echo.sh proxyecho.sh readline-test.sh \
@@ -134,6 +134,7 @@
 	ln -s socat1 $(DESTDIR)$(BINDEST)/socat
 	$(INSTALL) -m 755 socat-chain.sh  $(DESTDIR)$(BINDEST)
 	$(INSTALL) -m 755 socat-mux.sh    $(DESTDIR)$(BINDEST)
+	$(INSTALL) -m 755 socat-broker.sh $(DESTDIR)$(BINDEST)
 	$(INSTALL) -m 755 procan $(DESTDIR)$(BINDEST)
 	$(INSTALL) -m 755 filan $(DESTDIR)$(BINDEST)
 	mkdir -p $(DESTDIR)$(MANDEST)/man1
@@ -145,6 +146,7 @@
 	rm -f $(DESTDIR)$(BINDEST)/socat1
 	rm -f $(DESTDIR)$(BINDEST)/socat-chain.sh
 	rm -f $(DESTDIR)$(BINDEST)/socat-mux.sh
+	rm -f $(DESTDIR)$(BINDEST)/socat-broker.sh
 	rm -f $(DESTDIR)$(BINDEST)/procan
 	rm -f $(DESTDIR)$(BINDEST)/filan
 	rm -f $(DESTDIR)$(MANDEST)/man1/socat.1
diff --git a/socat-broker.sh b/socat-broker.sh
new file mode 100755
index 0000000..ec23774
--- /dev/null
+++ b/socat-broker.sh
@@ -0,0 +1,83 @@
+#! /usr/bin/env bash
+# Copyright Gerhard Rieger and contributors (see file CHANGES)
+# Published under the GNU General Public License V.2, see file COPYING
+
+# Shell script to perform group communications, sometimes called brokering.
+# It starts a Socat instance that forks a child process for each
+# connected client; the clients communicate via IPv4 broadcast
+
+# Examples:
+
+#   socat-broker.sh TCP-L:1234
+# Now connect with an arbitrary number of clients like TCP:<server>:1234
+
+#   socat-broker.sh SSL-L:1234,cert=server.pem,cafile=clients.crt
+# Now connect with an arbitrary number of clients like SSL:<server>:1234,cafile=server.cert=clients.pem
+
+ECHO="echo -e"
+
+usage () {
+    $ECHO "Usage: $0 <options> <listener>"
+    $ECHO "	<listener> is a passive address like TCP4-L or SSL-L"
+    $ECHO "	<options>:"
+    $ECHO "		-d*  -S  -t <timeout>  -T <timeout> 	are passed to socat"
+    $ECHO "		-V	prints the socat command before starting it"
+    $ECHO "For example:"
+    $ECHO "	$0 \\"
+    $ECHO "		TCP4-L:1234"
+    $ECHO "Then connect with clients to port 1234"
+    $ECHO "Data sent by any client is forwarded to all other clients"
+}
+
+VERBOSE= QUIET= OPTS=
+while [ "$1" ]; do
+    case "X$1" in
+	X-h) usage; exit ;;
+	X-V) VERBOSE=1 ;;
+	X-q) QUIET=1; OPTS="-d0" ;;
+	X-d*|X-l?*) OPTS="$OPTS $1" ;;
+	X-b|X-S|X-t|X-T|X-l) OPT=$1; shift; OPTS="$OPTS $OPT $1" ;;
+	X-) break ;;
+	X-*) echo "Unknown option \"$1\"" >&2
+	     usage >&2
+	     exit 1 ;;
+	*) break ;;
+    esac
+    shift
+done
+
+LISTENER="$1"
+
+if [ -z "$LISTENER" ]; then
+    echo "$0: Missing parameter" >&2
+    usage >&2
+    exit 1
+fi
+
+shopt -s nocasematch
+if ! [[ "$LISTENER" =~ .*,fork ]] || [[ "$LISTENER" =~ .*,fork, ]]; then
+    LISTENER="$LISTENER,fork"
+fi
+
+case "$0" in
+    */*) SOCAT=${0%/*}/socat ;;
+    *) SOCAT=socat ;;
+esac
+
+PORT=$($SOCAT -d -d -T 0.000001 UDP4-RECV:0 /dev/null 2>&1 |grep bound |sed 's/.*:\([1-9][0-9]*\)$/\1/')
+if [ -z "$PORT" ]; then
+    echo "$0: Failed to determine free UDP port" >&2
+    exit 1
+fi
+
+BCADDR=127.255.255.255
+
+if [ "$VERBOSE" ]; then
+    echo -e "$SOCAT -lp socat-broker $OPTS \\
+	$LISTENER \
+	UDP4-DATAGRAM:$BCADDR:$PORT,bind=:$PORT,so-broadcast,so-reuseaddr"
+fi	
+$SOCAT -lp socat-broker $OPTS \
+    "$LISTENER" \
+    "UDP4-DATAGRAM:$BCADDR:$PORT,bind=:$PORT,so-broadcast,so-reuseaddr"
+
diff --git a/test.sh b/test.sh
index e10f6bf..ae8a2c0 100755
--- a/test.sh
+++ b/test.sh
@@ -123,8 +123,9 @@
 #SOCAT_EGD="egd=/dev/egd-pool"
 MISCDELAY=1
 
-opts="$opt_t $OPTS"
-export SOCAT_OPTS="$opts"
+OPTS="$opt_t $OPTS"
+opts="$OPTS"
+
 TESTS="$*"; export TESTS
 if ! SOCAT_MAIN_WAIT= $SOCAT -V >/dev/null 2>&1; then
     echo "Failed to execute $SOCAT, exiting" >&2
@@ -1148,90 +1149,6 @@
     return 1
 }
 
-
-# Perform a couple of checks to make sure the test has a chance of a useful
-# result:
-# platform is supported, features compiled in, addresses and options
-# available; needs root; is allowed to access the internet
-checkconds() {
-    local unames="$(echo "$1")" 	# must be one of... exa: "Linux,FreeBSD"
-    local root="$2" 				# "root" or ""
-    local progs="$(echo "$3" |tr 'A-Z,' 'a-z ')"	# exa: "nslookup"
-    local feats="$(echo "$4" |tr 'a-z,' 'A-Z ')" 	# list of req.features (socat -V)
-    local addrs="$(echo "$5" |tr 'a-z,' 'A-Z ')" 	# list of req.addresses (socat -h)
-    local opts="$(echo "$6" |tr 'A-Z,' 'a-z ')" 	# list of req.options (socat -hhh)
-    local runs="$(echo "$7" |tr , ' ')" 		# list of req.protocols, exa: "sctp6"
-    local inet="$8" 					# when "internet": needs allowance
-    local i
-
-    if [ "$unames" ]; then
-	local uname="$(echo $UNAME |tr 'A-Z' 'a-z')"
-	for i in $unames; do
-	    if [ "$uname" = "$(echo "$i" |tr 'A-Z,' 'a-z ')" ]; then
-	        # good, mark as passed
-		i=
-		break;
-	    fi
-	done
-	[ "$i" ] && { echo "Only on (one of) $unames"; return -1; }
-    fi
-
-    if [ "$root" = "root" ]; then
-	if [ $(id -u) -ne 0 -a "$withroot" -eq 0 ]; then
-	    echo "Must be root"
-	    return -1;
-	fi
-    fi
-
-    if [ "$progs" ]; then
-	for i in $progs; do
-	    if ! type  >/dev/null 2>&1; then
-		echo "Program $i not available"
-		return -1
-	    fi
-	done
-    fi
-
-    if [ "$feats" ]; then
-	if ! F=$(testfeats $feats); then
-	    echo "Feature $F not configured in $SOCAT"
-	    return -1
-	fi
-    fi
-
-    if [ "$addrs" ]; then
-       	 if ! A=$(testaddrs - $addrs); then
-	     echo "Address $A not available in $SOCAT"
-	     return -1
-	 fi
-    fi
-
-    if [ "$opts" ]; then
-	if ! o=$(testoptions $opts); then
-	    echo "Option $o not available in $SOCAT"
-	    return -1
-	fi
-    fi
-
-    if [ "$runs" ]; then
-	for i in $runs; do
-	    if ! runs$i >/dev/null; then
-		echo "$i not available on host"
-		return -1;
-	    fi
-	done
-    fi
-
-    if [ "$inet" ]; then
-	if [ -z "$FOREIGN" ]; then
-	    echo "Use test.sh option -internet"
-	    return -1
-	fi
-    fi
-    return 0
-}
-
-
 # we need this misleading function name for canonical reasons
 waitip4port () {
     waitip4proto "$1" "$2" "$3"
@@ -15938,7 +15855,7 @@
 te="$td/test$N.stderr"
 tdiff="$td/test$N.diff"
 da="test$N $(date) $RANDOM"
-newport tcp4 	# or whatever proto, or drop this line
+newport tcp4
 CMD0="$TRACE $SOCAT $opts -lp server0 -r \"$td/test$N.\\\$PROGNAME-\\\$TIMESTAMP.\\\$MICROS-\\\$SERVER0_PEERADDR-\\\$\\\$.in.log\" -R \"$td/test$N.\\\$PROGNAME-\\\$TIMESTAMP.\\\$MICROS-\\\$SERVER0_PEERADDR-\\\$\\\$.out.log\" TCP4-LISTEN:$PORT,so-reuseaddr,fork PIPE"
 CMD1="$TRACE $SOCAT $opts - TCP4-CONNECT:$LOCALHOST:$PORT"
 printf "test $F_n $TEST... " $N
@@ -16731,7 +16648,7 @@
 te="$td/test$N.stderr"
 tdiff="$td/test$N.diff"
 da="test$N $(date) $RANDOM"
-newport tcp4 	# or whatever proto, or drop this line
+newport tcp4
 CMD0="$TRACE $SOCAT $opts --experimental TCP4-LISTEN:$PORT,netns=$ns EXEC:'od -c'"
 CMD1="$TRACE $SOCAT $opts --experimental - TCP4:127.0.0.1:$PORT,netns=$ns"
 printf "test $F_n $TEST... " $N
@@ -17650,7 +17567,7 @@
 tdiff="$td/test$N.diff"
 da="$(echo test$N $(date) $RANDOM |tr ' :' '-')"
 echo "$da" >"$td/test$N.da"
-newport udp4 	# or whatever proto, or drop this line
+newport udp4
 CMD0="$TRACE $SOCAT $opts -u UDP4-RECVFROM:$PORT -"
 CMD1="$TRACE $SOCAT $opts - TCP4:$da:0,res-nsaddr=$LOCALHOST:$PORT"
 printf "test $F_n $TEST... " $N
@@ -19002,10 +18919,10 @@
 		  "" \
 		  "" \
 		  "" \
-		  "IP4 UDP TCP LISTEN STDIO PIPE" \
-		  "TCP4-LISTEN TCP4-CONNECT PIPE STDIO UDP-DATAGRAM" \
+		  "IP4 TCP LISTEN STDIO UNIX" \
+		  "TCP4-LISTEN PIPE STDIN STDOUT TCP4 UNIX UNIX-LISTEN" \
 		  "so-reuseaddr" \
-		  "udp4 tcp4" ); then
+		  "tcp4 unix" ); then
     $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N
     numCANT=$((numCANT+1))
     listCANT="$listCANT $N"
@@ -19043,7 +18960,7 @@
 	cat "${te}0" >&2
 	echo "$CMD1 &"
 	cat "${te}1" >&2
-	echo "{ sleep 1; echo \"\$da_a\"; sleep 2; } |$CMD2"
+	echo "{ sleep 1; echo \"\$da_a\"; sleep 2; } |$CMD2 &"
 	cat "${te}2a" >&2
 	echo "{ sleep 2; echo \"\$da_b\"; sleep 1; } |$CMD2"
 	cat "${te}2b" >&2
@@ -19056,7 +18973,7 @@
 	cat "${te}0" >&2
 	echo "$CMD1 &"
 	cat "${te}1" >&2
-	echo "{ sleep 1; echo \"\$da_a\"; sleep 2; } |$CMD2"
+	echo "{ sleep 1; echo \"\$da_a\"; sleep 2; } |$CMD2 &"
 	cat "${te}2a" >&2
 	echo "{ sleep 2; echo \"\$da_b\"; sleep 1; } |$CMD2"
 	cat "${te}2b" >&2
@@ -19071,9 +18988,9 @@
 	cat "${te}0" >&2
 	echo "$CMD1 &"
 	cat "${te}1" >&2
-	echo "{ sleep 1; echo \"\$da_a\"; sleep 2; } |$CMD2"
+	echo "{ sleep 1; echo \"\$da_a\"; sleep 2; } |$CMD2 &"
 	cat "${te}2a" >&2
-	echo "{ sleep 2; echo \"\$da_b\"; sleep 2; } |$CMD2"
+	echo "{ sleep 2; echo \"\$da_b\"; sleep 1; } |$CMD2"
 	cat "${te}2b" >&2
 	echo "// diff b:" >&2
 	cat "${tdiff}_b" >&2
@@ -19086,7 +19003,7 @@
 	if [ "$DEBUG" ];   then cat "${te}0" >&2; fi
 	if [ "$VERBOSE" ]; then echo "$CMD1 &"; fi
 	if [ "$DEBUG" ];   then cat "${te}1" >&2; fi
-	if [ "$VERBOSE" ]; then echo "{ sleep 1; echo \"\$da_a\"; sleep 2; } |$CMD2"; fi
+	if [ "$VERBOSE" ]; then echo "{ sleep 1; echo \"\$da_a\"; sleep 2; } |$CMD2 &"; fi
 	if [ "$DEBUG" ];   then cat "${te}2a" >&2; fi
 	if [ "$VERBOSE" ]; then echo "{ sleep 2; echo \"\$da_b\"; sleep 1; } |$CMD2"; fi
 	if [ "$DEBUG" ];   then cat "${te}2b" >&2; fi
@@ -19098,6 +19015,102 @@
 N=$((N+1))
 
 
+# Test the socat-broker.sh script
+# Requires lo/lo0 to have broadcast address 127.255.255.255
+NAME=SOCAT_BROKER
+case "$TESTS" in
+*%$N%*|*%functions%*|*%script%*|*%socat-broker%*|*%socket%*|*%udp%*|*%broadcast%*|*%$NAME%*)
+TEST="$NAME: test the socat-broker.sh script"
+# Start a socat-broker.sh instance
+# Connect with two clients, send different data records from both.
+# Check if both client received both records in order.
+if ! eval $NUMCOND; then :
+# Remove unneeded checks, adapt lists of the remaining ones
+elif ! cond=$(checkconds \
+		  "" \
+		  "" \
+		  "" \
+		  "IP4 UDP TCP LISTEN STDIO" \
+		  "TCP4-LISTEN TCP4-CONNECT STDIO UDP-DATAGRAM" \
+		  "so-reuseaddr" \
+		  "udp4 tcp4" ); then
+    $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N
+    numCANT=$((numCANT+1))
+    listCANT="$listCANT $N"
+    namesCANT="$namesCANT $NAME"
+else
+    tf="$td/test$N.stdout"
+    te="$td/test$N.stderr"
+    tdiff="$td/test$N.diff"
+    newport tcp4
+    CMD0="$TRACE ./socat-broker.sh $OPTS TCP4-LISTEN:$PORT"
+    CMD1="$TRACE $SOCAT $OPTS - TCP:$LOCALHOST:$PORT"
+    da_a="test$N $(date) $RANDOM"
+    da_b="test$N $(date) $RANDOM"
+    printf "test $F_n $TEST... " $N
+    $CMD0 >/dev/null 2>"${te}0" &
+    pid0=$!
+    waittcp4port $PORT 1
+    { sleep 1; echo "$da_a"; sleep 2; } </dev/null |$CMD1 >"${tf}1a" 2>"${te}1a" &
+    pid1a=$!
+    { sleep 2; echo "$da_b"; sleep 1; } |$CMD1 >"${tf}1b" 2>"${te}1b"
+    rc1b=$?
+    kill $(childpids $pid0) $pid0 $pid1a 2>/dev/null
+    wait  2>/dev/null
+    #kill $pid0 2>/dev/null; wait
+    if [ "$rc1b" -ne 0 ]; then
+	$PRINTF "$FAILED (rc1b=$rc1b)\n"
+	echo "$CMD0 &"
+	cat "${te}0" >&2
+	echo "{ sleep 1; echo \"\$da_a\"; sleep 2; } |$CMD1"
+	cat "${te}1a" >&2
+	echo "{ sleep 2; echo \"\$da_b\"; sleep 1; } |$CMD1"
+	cat "${te}1b" >&2
+	numFAIL=$((numFAIL+1))
+	listFAIL="$listFAIL $N"
+	namesFAIL="$namesFAIL $NAME"
+    elif ! $ECHO "$da_a\n$da_b" |diff - "${tf}1a" >${tdiff}_a; then
+	$PRINTF "$FAILED (diff a)\n"
+	echo "$CMD0 &"
+	cat "${te}0" >&2
+	echo "{ sleep 1; echo \"\$da_a\"; sleep 2; } |$CMD1"
+	cat "${te}1a" >&2
+	echo "{ sleep 2; echo \"\$da_b\"; sleep 1; } |$CMD1"
+	cat "${te}1b" >&2
+	echo "// diff a:" >&2
+	cat "${tdiff}_a" >&2
+	numFAIL=$((numFAIL+1))
+	listFAIL="$listFAIL $N"
+	namesFAIL="$namesFAIL $NAME"
+    elif ! $ECHO "$da_a\n$da_b" |diff - "${tf}1b" >${tdiff}_b; then
+	$PRINTF "$FAILED (diff b)\n"
+	echo "$CMD0 &"
+	cat "${te}0" >&2
+	echo "{ sleep 1; echo \"\$da_a\"; sleep 2; } |$CMD1"
+	cat "${te}1a" >&2
+	echo "{ sleep 2; echo \"\$da_b\"; sleep 1; } |$CMD1"
+	cat "${te}1b" >&2
+	echo "// diff b:" >&2
+	cat "${tdiff}_b" >&2
+	numFAIL=$((numFAIL+1))
+	listFAIL="$listFAIL $N"
+	namesFAIL="$namesFAIL $NAME"
+    else
+	$PRINTF "$OK\n"
+	if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
+	if [ "$DEBUG" ];   then cat "${te}0" >&2; fi
+	if [ "$VERBOSE" ]; then echo "{ sleep 1; echo \"\$da_a\"; sleep 2; } |$CMD1"; fi
+	if [ "$DEBUG" ];   then cat "${te}1a" >&2; fi
+	if [ "$VERBOSE" ]; then echo "{ sleep 2; echo \"\$da_b\"; sleep 1; } |$CMD1"; fi
+	if [ "$DEBUG" ];   then cat "${te}1b" >&2; fi
+	numOK=$((numOK+1))
+    fi
+fi # NUMCOND
+ ;;
+esac
+N=$((N+1))
+
+
 # end of common tests
 
 ##################################################################################