Hello community,

here is the log from the commit of package mcjoin for openSUSE:Factory checked 
in at 2020-02-27 14:38:36
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/mcjoin (Old)
 and      /work/SRC/openSUSE:Factory/.mcjoin.new.26092 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "mcjoin"

Thu Feb 27 14:38:36 2020 rev:3 rq:779674 version:2.5

Changes:
--------
--- /work/SRC/openSUSE:Factory/mcjoin/mcjoin.changes    2019-04-30 
13:06:10.497822285 +0200
+++ /work/SRC/openSUSE:Factory/.mcjoin.new.26092/mcjoin.changes 2020-02-27 
14:39:04.446247202 +0100
@@ -1,0 +2,22 @@
+Wed Feb 26 11:56:58 UTC 2020 - Martin Hauke <mar...@gmx.de>
+
+- Update to version 2.5
+  * Replaced mutually exclusive options -q and -d with -l level.
+    Default log level is notice, for true quiet mode, use -l none
+  * Added new daemon mode, which makes mcjoin detach from its
+    controlling terminal and run in the background, with all
+    output except progress redirected to syslog
+  * Fix possible file descriptor leak
+  * Improve error detection if mcjoin cannot find a default
+    interface, a valid (source) IP address, or anything else
+    goes wrong
+  * Add support for -w SEC, initial delay before actually
+    starting. Useful if started very early in a system bootstrap
+    before network has come up properly
+- Update to version 2.4
+  * Support for SSM (S,G) join, in addition to the existing
+    ASM (*,G) join
+  * Find interface bsaed on default route with best metric
+  * Initial support for IPv6 (*,G) and (S,G) joins
+
+-------------------------------------------------------------------

Old:
----
  mcjoin-2.3.tar.gz

New:
----
  mcjoin-2.5.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ mcjoin.spec ++++++
--- /var/tmp/diff_new_pack.SCFNb4/_old  2020-02-27 14:39:08.406255373 +0100
+++ /var/tmp/diff_new_pack.SCFNb4/_new  2020-02-27 14:39:08.406255373 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package mcjoin
 #
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LLC
 # Copyright (c) 2018, Martin Hauke <mar...@gmx.de>
 #
 # All modifications and additions to the file contributed by third parties
@@ -18,7 +18,7 @@
 
 
 Name:           mcjoin
-Version:        2.3
+Version:        2.5
 Release:        0
 Summary:        IPv4 tool for verifying multicast connectivity
 License:        ISC
@@ -44,7 +44,7 @@
 %build
 autoreconf -fiv
 %configure
-make %{?_smp_mflags}
+%make_build
 
 %install
 %make_install

++++++ mcjoin-2.3.tar.gz -> mcjoin-2.5.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/.gitignore new/mcjoin-2.5/.gitignore
--- old/mcjoin-2.3/.gitignore   2018-09-20 20:55:39.000000000 +0200
+++ new/mcjoin-2.5/.gitignore   2020-02-23 06:52:53.000000000 +0100
@@ -1,5 +1,8 @@
 *~
 *.o
+GPATH
+GRTAGS
+GTAGS
 /.deps/*
 /autom4te.cache/*
 /Makefile
@@ -16,6 +19,7 @@
 /mcjoin
 /missing
 /stamp-h1
+/debian/autoreconf.*
 /debian/.debhelper/*
 /debian/debhelper-build-stamp
 /debian/mcjoin/*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/ChangeLog.md new/mcjoin-2.5/ChangeLog.md
--- old/mcjoin-2.3/ChangeLog.md 2018-09-20 20:55:39.000000000 +0200
+++ new/mcjoin-2.5/ChangeLog.md 2020-02-23 06:52:53.000000000 +0100
@@ -4,6 +4,32 @@
 All notable changes to the project are documented in this file.
 
 
+[v2.5][] - 202-02-23
+--------------------
+
+- Add Dockerfile, thanks to Graeme Lawes
+- FreeBSD support, by Ryan Libby
+- Replaced mutually exclusive options `-q` and `-d` with `-l level`.
+  Default log level is `notice`, for true quiet mode, use `-l none`
+- Added new daemon mode, which makes mcjoin detach from its controlling
+  terminal and run in the background, with all output except progress
+  redirected to syslog
+- Fix possible file descriptor leak
+- Improve error detection if mcjoin cannot find a default interface,
+  a valid (source) IP address, or anything else goes wrong
+- Add support for `-w SEC`, initial delay before actually starting.
+  Useful if started very early in a system bootstrap before network
+  has come up properly
+
+
+[v2.4][] - 2019-04-04
+---------------------
+
+- Support for SSM (S,G) join, in addition to the existing ASM (*,G) join
+- Find interface bsaed on default route with *best* metric
+- Initial support for IPv6 (*,G) and (S,G) joins
+
+
 [v2.3][] - 2018-09-20
 ---------------------
 
@@ -69,16 +95,12 @@
 and developed further by Joachim Nilsson, on his spare time.
 
 
-[UNRELEASED]: https://github.com/troglobit/mcjoin/compare/v2.3...HEAD
+[UNRELEASED]: https://github.com/troglobit/mcjoin/compare/v2.5...HEAD
+[v2.5]:       https://github.com/troglobit/mcjoin/compare/v2.4...v2.5
+[v2.4]:       https://github.com/troglobit/mcjoin/compare/v2.3...v2.4
 [v2.3]:       https://github.com/troglobit/mcjoin/compare/v2.2...v2.3
 [v2.2]:       https://github.com/troglobit/mcjoin/compare/v2.1...v2.2
 [v2.1]:       https://github.com/troglobit/mcjoin/compare/v2.0...v2.1
 [v2.0]:       https://github.com/troglobit/mcjoin/compare/v1.5...v2.0
 [v1.5]:       https://github.com/troglobit/mcjoin/compare/v1.4...v1.5
 [v1.4]:       https://github.com/troglobit/mcjoin/compare/v1.3...v1.4
-
-<!--
-  -- Local Variables:
-  -- mode: markdown
-  -- End:
-  -->
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/Dockerfile new/mcjoin-2.5/Dockerfile
--- old/mcjoin-2.3/Dockerfile   1970-01-01 01:00:00.000000000 +0100
+++ new/mcjoin-2.5/Dockerfile   2020-02-23 06:52:53.000000000 +0100
@@ -0,0 +1,14 @@
+FROM alpine:3.9
+RUN apk add --update git build-base automake autoconf
+
+RUN git clone --depth=1 https://github.com/troglobit/mcjoin.git /root/mcjoin
+WORKDIR /root/mcjoin
+
+RUN ./autogen.sh
+RUN ./configure --prefix=/usr
+RUN make
+
+FROM alpine:3.9
+COPY --from=0 /root/mcjoin/mcjoin /usr/bin/mcjoin
+
+CMD [ "/usr/bin/mcjoin" ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/LICENSE new/mcjoin-2.5/LICENSE
--- old/mcjoin-2.3/LICENSE      2018-09-20 20:55:39.000000000 +0200
+++ new/mcjoin-2.5/LICENSE      2020-02-23 06:52:53.000000000 +0100
@@ -1,7 +1,5 @@
-Join a multicast group and/or generate UDP test data
-
 Copyright (C) 2004       David Stevens <dlstevens()us!ibm!com>
-Copyright (C) 2008-2018  Joachim Nilsson <troglobit()gmail!com>
+Copyright (C) 2008-2019  Joachim Nilsson <troglobit()gmail!com>
 
 Permission to use, copy, modify, and/or distribute this software for any
 purpose with or without fee is hereby granted, provided that the above
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/README.md new/mcjoin-2.5/README.md
--- old/mcjoin-2.3/README.md    2018-09-20 20:55:39.000000000 +0200
+++ new/mcjoin-2.5/README.md    2020-02-23 06:52:53.000000000 +0100
@@ -2,15 +2,19 @@
 =========================================
 [![Travis Status][]][Travis] [![Coverity Status][]][Coverity Scan]
 
-`mcjoin` is a very simple and easy-to-use tool to test IPv4 multicast.
-Simply start a multicast generator (server) on one end and one or more
-data sinks (clients).
-
-By default the group `225.1.2.3` and the UDP port `1234` is used, you
-may want to use the `MCAST_TEST_NET` from RFC5771, `233.252.0.0/24`, or
-the `ompoing(8)` test group `232.43.211.234`, defined in this IETF draft
-<http://tools.ietf.org/html/draft-ietf-mboned-ssmping-08> and UDP port
-`4321`.  At the moment max 250 groups can be joined.
+`mcjoin` is a very simple and easy-to-use tool to test IPv4 and IPv6
+multicast.  it features:
+
+- an optional multicast generator (server)
+- an end device that can act as a data sink (client)
+- supports joining one or more groups:
+  - ASM (*,G) support
+  - SSM (S,G) support
+- IPv4
+- IPv6
+
+> the latest release is always available from GitHub at  
+> https://github.com/troglobit/mcjoin/releases
 
 
 example
@@ -22,6 +26,10 @@
 sender$
 ```
 
+without any arguments `mcjoin` defaults to an IPv4 ASM (*,G) join of
+`225.1.2.3`, UDP port `1234`.  see the usage section below for more
+help.
+
 ```shell
 receiver$ mcjoin
 joined group 225.1.2.3 on eth0 ...
@@ -30,6 +38,45 @@
 receiver$
 ```
 
+for testing purposes you may want to use the `MCAST_TEST_NET` from
+RFC5771, `233.252.0.0/24`, or possibly the [ompoing(8)][1] test group
+`232.43.211.234`, UDP port `4321`, as defined in [this IETF draft][2].
+
+for testing IPv6 you can use ff2e::42
+
+
+usage
+-----
+
+```shell
+    $ mcjoin -h
+    
+    Usage: mcjoin [-dhjsv] [-c COUNT] [-i IFACE] [-p PORT] [-r SEC] [-t TTL] 
[-w SEC]
+                  [[SOURCE,]GROUP0 .. [SOURCE,]GROUPN | [SOURCE,]GROUP+NUM]
+    
+    Options:
+      -c COUNT    Stop sending/receiving after COUNT number of packets
+      -d          Run as daemon in background, output except progress to syslog
+      -h          This help text
+      -i IFACE    Interface to use for sending/receiving multicast, default 
eth0
+      -j          Join groups, default unless acting as sender
+      -l LEVEL    Set log level; none, notice*, debug\n"
+      -p PORT     UDP port number to listen to, default: 1234
+      -r SEC      Do a join/leave every SEC seconds (backwards compat. option)
+      -s          Act as sender, sends packets to select groups
+      -t TTL      TTL to use when sending multicast packets, default 1
+      -v          Display program version
+         -w SEC      Initial wait before opening sockets
+    
+    Bug report address: https://github.com/troglobit/mcjoin/issues
+    Project homepage: https://github.com/troglobit/mcjoin/
+```
+
+the `SOURCE` argument is optional, but when used it must be of the same
+address family as the group.  to join multiple groups, either list them
+all on the command line, separated with space, or use the `+NUM` syntax.
+at the moment max 250 groups can be joined.
+
 
 troubleshooting
 ---------------
@@ -52,35 +99,6 @@
     mcjoin
 
 
-usage
------
-
-```shell
-    $ mcjoin -h
-    
-    Usage: mcjoin [dhjqsv] [-c COUNT] [-i IFNAME] [-p PORT] [-r SEC] [-t TTL]
-             [GROUP0 .. GROUPN | GROUP+NUM]
-    
-    Options:
-      -c COUNT     Exit after COUNT number of received and/or sent packets
-      -d           Debug output
-      -h           This help text
-      -i IFNAME    Interface to use for multicast groups, default eth0
-      -j           Join groups, default unless acting as sender
-      -p PORT      UDP port number to listen to, default: 1234
-      -q           Quiet mode
-      -r SEC       Do a join/leave every SEC seconds
-      -s           Act as sender, sends packets to select groups
-      -t TTL       TTL to use when sending multicast packets, default 1
-      -v           Display program version
-    
-    Bug report address: https://github.com/troglobit/mcjoin/issues
-    Project homepage: https://github.com/troglobit/mcjoin/
-    
-    $
-```
-
-
 caveat
 ------
 
@@ -118,9 +136,9 @@
 build & install
 ---------------
 
-the GNU Configure & Build system use `/usr/local` as the default install
-prefix.  for most use-cases this is fine, but if you want to change this
-to `/usr` use the `--prefix=/usr` configure option:
+the [GNU Configure & Build][buildsystem] system use `/usr/local` as the
+default install prefix.  for most use-cases this is fine, but if you
+want to change this to `/usr` use the `--prefix=/usr` configure option:
 
     $ ./configure --prefix=/usr
     $ make -j5
@@ -132,7 +150,7 @@
 
 if you want to contribute, or simply just try out the latest but
 unreleased features, then you need to know a few things about the
-[GNU build system][buildsystem]:
+[GNU Configure & Build][buildsystem] system:
 
 - `configure.ac` and a per-directory `Makefile.am` are key files
 - `configure` and `Makefile.in` are generated from `autogen.sh`,
@@ -140,9 +158,9 @@
   release tarballs
 - `Makefile` is generated by `configure` script
 
-to build from GIT you first need to clone the repository and run the
-`autogen.sh` script.  this requires `automake` and `autoconf` to be
-installed on your system.
+to build from GIT; clone the repository and run the `autogen.sh` script.
+this requires `automake` and `autoconf` to be installed on your system.
+(if you build from a released tarball you don't need them.)
 
     git clone https://github.com/troglobit/mcjoin.git
     cd mcjoin/
@@ -150,17 +168,11 @@
     ./configure && make
     sudo make install-strip
 
-**NOTE:** GIT sources are a moving target and are not recommended for
-  production systems, unless you know what you are doing!
-
 
+[1]:               https://github.com/troglobit/omping
+[2]:               http://tools.ietf.org/html/draft-ietf-mboned-ssmping-08
 [Travis]:          https://travis-ci.org/troglobit/mcjoin
 [Travis Status]:   https://travis-ci.org/troglobit/mcjoin.png?branch=master
 [Coverity Scan]:   https://scan.coverity.com/projects/9108
 [Coverity Status]: https://scan.coverity.com/projects/9108/badge.svg
-
-<!--
-  -- Local Variables:
-  -- mode: markdown
-  -- End:
-  -->
+[buildsystem]:     https://airs.com/ian/configure/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/addr.c new/mcjoin-2.5/addr.c
--- old/mcjoin-2.3/addr.c       2018-09-20 20:55:39.000000000 +0200
+++ new/mcjoin-2.5/addr.c       2020-02-23 06:52:53.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018  Joachim Nilsson <troglo...@gmail.com>
+ * Copyright (c) 2018-2019  Joachim Nilsson <troglo...@gmail.com>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -43,14 +43,20 @@
 {
        uint32_t dest, gw, mask;
        char buf[256], name[17];
+       char *ptr;
        FILE *fp;
        int rc, flags, cnt, use, metric, mtu, win, irtt;
-       int found = 0;
+       int best = 100000, found = 0;
 
        fp = fopen("/proc/net/route", "r");
        if (!fp)
                return NULL;
 
+       /* Skip heading */
+       ptr = fgets(buf, sizeof(buf), fp);
+       if (!ptr)
+               goto end;
+
        while (fgets(buf, sizeof(buf), fp) != NULL) {
                rc = sscanf(buf, "%16s %X %X %X %d %d %d %X %d %d %d\n",
                           name, &dest, &gw, &flags, &cnt, &use, &metric,
@@ -63,13 +69,18 @@
                        continue;
 
                if (!ifname[0] || !strncmp(ifname, "tun", 3)) {
+                       if (metric >= best)
+                               continue;
+
                        strncpy(ifname, name, len);
+                       ifname[len] = 0;
+                       best = metric;
                        found = 1;
-                       break;
                }
        }
-       fclose(fp);
 
+end:
+       fclose(fp);
        if (found)
                return ifname;
 
@@ -96,12 +107,14 @@
        char buf[20] = { 0 };
        int rc = -1;
 
-       if (!iface)
+       if (!iface || !iface[0])
                iface = getifname(ifname, sizeof(ifname));
+       if (!iface)
+               return -1;
 
        rc = getifaddrs(&ifaddr);
        if (rc)
-               return -1;
+               return -2;
 
        for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
                if (!ifa->ifa_addr)
@@ -132,7 +145,7 @@
        freeifaddrs(ifaddr);
 
        if (rc || IN_ZERONET(ntohl(ina->s_addr)))
-               return -1;
+               return -3;
 
        return 0;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/configure.ac new/mcjoin-2.5/configure.ac
--- old/mcjoin-2.3/configure.ac 2018-09-20 20:55:39.000000000 +0200
+++ new/mcjoin-2.5/configure.ac 2020-02-23 06:52:53.000000000 +0100
@@ -1,4 +1,4 @@
-AC_INIT(mcjoin, 2.3, https://github.com/troglobit/mcjoin/issues,, 
https://github.com/troglobit/mcjoin/)
+AC_INIT(mcjoin, 2.5, https://github.com/troglobit/mcjoin/issues,, 
https://github.com/troglobit/mcjoin/)
 AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
 
 AC_CONFIG_SRCDIR([mcjoin.c])
@@ -10,4 +10,9 @@
 
 AC_HEADER_STDC
 
+AC_CHECK_MEMBERS([struct sockaddr_storage.ss_len], , ,
+[
+#include <sys/socket.h>
+])
+
 AC_OUTPUT
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/debian/changelog 
new/mcjoin-2.5/debian/changelog
--- old/mcjoin-2.3/debian/changelog     2018-09-20 20:55:39.000000000 +0200
+++ new/mcjoin-2.5/debian/changelog     2020-02-23 06:52:53.000000000 +0100
@@ -1,3 +1,22 @@
+mcjoin (2.5) unstable; urgency=medium
+
+  * New upstream release.
+  * New and changed command like options.
+  * Fixes possible file descriptor leaks.
+  * Improved error detection on startup, i.e., no default interface,
+    or interface has no IP address (yet).
+
+ -- Joachim Nilsson <troglo...@gmail.com>  Sun, 23 Feb 2020 06:44:03 +0100
+
+mcjoin (2.4) unstable; urgency=medium
+
+  * New upstream release.  Support for SSM join (S,G) in addition to the
+    existing ASM (*,G) functionaliy.
+  * Find default interface based on default route with best metric.
+  * Initial support for IPv6 (*,G) and (S,G) joins
+
+ -- Joachim Nilsson <troglo...@gmail.com>  Thu, 04 Apr 2019 09:57:39 +0200
+
 mcjoin (2.3) unstable; urgency=medium
 
   * New upstream release.  Automatically detect default interface, fix bug
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/debian/compat new/mcjoin-2.5/debian/compat
--- old/mcjoin-2.3/debian/compat        2018-09-20 20:55:39.000000000 +0200
+++ new/mcjoin-2.5/debian/compat        2020-02-23 06:52:53.000000000 +0100
@@ -1 +1 @@
-9
+10
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/debian/control 
new/mcjoin-2.5/debian/control
--- old/mcjoin-2.3/debian/control       2018-09-20 20:55:39.000000000 +0200
+++ new/mcjoin-2.5/debian/control       2020-02-23 06:52:53.000000000 +0100
@@ -4,18 +4,18 @@
 Maintainer: Joachim Nilsson <troglo...@gmail.com>
 Homepage: https://github.com/troglobit/mcjoin
 Build-Depends: debhelper (>= 10)
+Standards-Version: 4.3.0
 Vcs-Git: https://github.com/troglobit/mcjoin.git
 Vcs-Browser: https://github.com/troglobit/mcjoin/commits/
-Standards-Version: 3.9.6
 
 Package: mcjoin
 Architecture: any
 Depends: ${shlibs:Depends}, ${misc:Depends}
-Description: IPv4 tool for verifying multicast connectivity.
- mcjoin can be used to join IPv4 multicast groups, display
- progress as multicast packets are received, and also send
- multicast packets on select groups.
+Description: tool for verifying multicast connectivity
+ mcjoin can be used to join IPv4/IPv6 multicast groups, display progress
+ as multicast packets are received, and also send multicast packets on
+ select groups.
  .
- mcjoin can help verify intended IGMP snooping functionality
- in layer-2 bridges/switches, as well as test forwarding of
- multicast in static or dynamic multicast routing setups.
+ mcjoin can help verify intended IGMP/MLD snooping functionality in
+ layer-2 bridges/switches, as well as test forwarding of multicast in
+ static or dynamic multicast routing setups.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/debian/copyright 
new/mcjoin-2.5/debian/copyright
--- old/mcjoin-2.3/debian/copyright     2018-09-20 20:55:39.000000000 +0200
+++ new/mcjoin-2.5/debian/copyright     2020-02-23 06:52:53.000000000 +0100
@@ -1,11 +1,22 @@
-Copyright (C) 2004       David Stevens <dlstevens()us!ibm!com>
-Copyright (C) 2008-2018  Joachim Nilsson <troglobit()gmail!com>
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: mcjoin
+Upstream-Contact: troglo...@gmail.com
+Source: https://github.com/troglobit/mcjoin
+
+Files: *
+Copyright: 2004 David Stevens <dlstevens()us!ibm!com>
+   2008-2019 Joachim Nilsson <troglobit()gmail!com>
+License: ISC
+
+Files: debian/*
+Copyright: 2008-2019 Joachim Nilsson <troglobit()gmail!com>
+License: ISC
 
 License: ISC
  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted, provided that the above
  copyright notice and this permission notice appear in all copies.
-
+ .
  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/debian/mcjoin.links 
new/mcjoin-2.5/debian/mcjoin.links
--- old/mcjoin-2.3/debian/mcjoin.links  2018-09-20 20:55:39.000000000 +0200
+++ new/mcjoin-2.5/debian/mcjoin.links  1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-/usr/bin/mcjoin /usr/local/bin/mcjoin
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/debian/mcjoin.lintian-overrides 
new/mcjoin-2.5/debian/mcjoin.lintian-overrides
--- old/mcjoin-2.3/debian/mcjoin.lintian-overrides      2018-09-20 
20:55:39.000000000 +0200
+++ new/mcjoin-2.5/debian/mcjoin.lintian-overrides      1970-01-01 
01:00:00.000000000 +0100
@@ -1,3 +0,0 @@
-mcjoin binary: dir-in-usr-local *
-mcjoin binary: file-in-usr-local *
-mcjoin binary: file-in-unusual-dir *
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/debian/rules new/mcjoin-2.5/debian/rules
--- old/mcjoin-2.3/debian/rules 2018-09-20 20:55:39.000000000 +0200
+++ new/mcjoin-2.5/debian/rules 2020-02-23 06:52:53.000000000 +0100
@@ -10,4 +10,4 @@
 
 override_dh_auto_install:
        dh_auto_install
-       find debian/ -name LICENSE -delete
+       rm debian/mcjoin/usr/share/doc/mcjoin/LICENSE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/mcjoin.1 new/mcjoin-2.5/mcjoin.1
--- old/mcjoin-2.3/mcjoin.1     2018-09-20 20:55:39.000000000 +0200
+++ new/mcjoin-2.5/mcjoin.1     2020-02-23 06:52:53.000000000 +0100
@@ -1,5 +1,20 @@
-.\" To process this file use: groff -man -Tascii netcalc.1
-.Dd 8 September, 2016
+.\" Hey Emacs, this is an -*- nroff -*- document
+.\"
+.\" Copyright (C) 2016-2019  Joachim Nilsson
+.\"
+.\" Permission to use, copy, modify, and/or distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\" 
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd March 8, 2020
 .Dt MCJOIN 1
 .Os
 .Sh NAME
@@ -7,22 +22,32 @@
 .Nd tiny multicast testing tool
 .Sh SYNOPSIS
 .Nm
-.Op Fl dhjqsv
+.Op Fl dhjsv
 .Op Fl c Ar COUNT
 .Op Fl i Ar IFNAME
+.Op Fl l Ar LEVEL
 .Op Fl p Ar PORT
 .Op Fl r Ar SEC
 .Op Fl t Ar TTL
-.Op Ar GROUP0 .. GROUPN | GROUP+NUM
+.Op Fl w Ar SEC
+.Op Ar [SOURCE,]GROUP0 .. [SOURCE,]GROUPN | [SOURCE,]GROUP+NUM
 .Sh DESCRIPTION
 .Nm
-can be used to join IPv4 multicast groups, display progress as multicast
-packets are received, and also send multicast packets on select groups.
+can be used to join IPv4 and IPv6 multicast groups, display progress as
+multicast packets are received, and also send multicast packets on
+select groups.
 .Pp
 .Nm
 can help verify intended IGMP snooping functionality in layer-2 bridges
 (switches), as well as verify forwarding of multicast in static or
 dynamic multicast routing setups.
+.Pp
+.Nm
+supports source-specific multicast, SSM (S,G), as well as any-source
+multicast, ASM (*,G).  The source IP of an (S,G) pair is an optional
+argument that must precede the group and be separated with a comma.  No
+spaces are allowed between source and group in this form.  Multiple
+(S,G) pairs are separated with space.
 .Sh OPTIONS
 With no options given
 .Nm
@@ -35,17 +60,21 @@
 .It Fl c Ar COUNT
 Stop sending/receiving after COUNT number of packets
 .It Fl d
-Debug output
+Run as a daemon in the background, detached from the current terminal.
+All output, except progress is sent to
+.Xr syslog 3 .
 .It Fl h
 Print a summary of the options and exit
 .It Fl i Ar IFNAME
 Interface to use for sending/receiving multicast, default: eth0
 .It Fl j
 Join groups, default unless acting as sender
+.It Fl l Ar LEVEL
+Control
+.Nm
+log level; none, notice, debug.  Default: notice.
 .It Fl p Ar PORT
 UDP port number to send/listen to, default: 1234
-.It Fl q
-Quiet mode
 .It Fl s
 Act as sender, sends packets to select groups, 1/100 msec, default: no
 .It Fl r Ar SEC
@@ -55,6 +84,13 @@
 TTL to use when sending multicast packets, default: 1
 .It Fl v
 Show version information
+.It Fl w Ar SEC
+Initial wait, sleep
+.Ar SEC
+seconds before starting anything.  Useful for scripting test systems
+that launch
+.Nm
+at boot without syncing with creation of networking.
 .El
 .Sh USAGE
 To verify multicast connectivity, the simplest way is to run
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mcjoin-2.3/mcjoin.c new/mcjoin-2.5/mcjoin.c
--- old/mcjoin-2.3/mcjoin.c     2018-09-20 20:55:39.000000000 +0200
+++ new/mcjoin-2.5/mcjoin.c     2020-02-23 06:52:53.000000000 +0100
@@ -1,7 +1,7 @@
 /* Join a multicast group and/or generate UDP test data
  *
  * Copyright (C) 2004       David Stevens <dlstevens()us!ibm!com>
- * Copyright (C) 2008-2018  Joachim Nilsson <troglobit()gmail!com>
+ * Copyright (C) 2008-2019  Joachim Nilsson <troglobit()gmail!com>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -17,6 +17,7 @@
  */
 
 #include "config.h"
+#define SYSLOG_NAMES
 
 #include <arpa/inet.h>
 #include <errno.h>
@@ -27,9 +28,12 @@
 #include <signal.h>
 #include <net/if.h>
 #include <netinet/in.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <syslog.h>
+#include <sys/param.h>         /* MIN() */
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <unistd.h>
@@ -39,15 +43,16 @@
 #define DEFAULT_GROUP   "225.1.2.3"
 #define DEFAULT_PORT    1234
 #define MAGIC_KEY       "Sender PID "
+#define QUIET          (log_level == INTERNAL_NOPRI || log_syslog)
 
 /* Esc[?25l (lower case L)    - Hide Cursor */
-#define hidecursor()          fputs ("\e[?25l", stdout)
+#define hidecursor()    if (!QUIET) fputs("\e[?25l", stderr)
 /* Esc[?25h (lower case H)    - Show Cursor */
-#define showcursor()          fputs ("\e[?25h", stdout)
+#define showcursor()    if (!QUIET) fputs("\e[?25h", stderr)
 
-#define DEBUG(fmt, ...) { if (debug)  printf(fmt "\n", ## __VA_ARGS__); 
fflush(stdout); }
-#define ERROR(fmt, ...) { fprintf(stderr, fmt "\n", ## __VA_ARGS__);    }
-#define PRINT(fmt, ...) { if (!quiet) printf(fmt "\n", ## __VA_ARGS__); 
fflush(stdout); }
+#define DEBUG(fmt, args...) do { logit(LOG_DEBUG,  fmt "\n", ##args); } while 
(0)
+#define ERROR(fmt, args...) do { logit(LOG_ERR,    fmt "\n", ##args); } while 
(0)
+#define PRINT(fmt, args...) do { logit(LOG_NOTICE, fmt "\n", ##args); } while 
(0)
 
 #ifndef IN_LINKLOCAL
 #define IN_LINKLOCALNETNUM     0xa9fe0000
@@ -67,17 +72,25 @@
 #define NELEMS(array) (sizeof(array) / sizeof(array[0]))
 #endif
 
+#ifdef AF_INET6
+#define INET_ADDRSTR_LEN  INET6_ADDRSTRLEN
+#else
+#define INET_ADDRSTR_LEN  INET_ADDRSTRLEN
+#endif
+typedef struct sockaddr_storage inet_addr_t;
+
 /* Group info */
 struct gr {
-       int                 sd;
-       size_t              count;
-       char               *group;
-       struct sockaddr_in  to;
+       int          sd;
+       size_t       count;
+       char        *source;
+       char        *group;
+       inet_addr_t  src;
+       inet_addr_t  grp;       /* to */
 };
 
 /* Mode flags */
 int join = 1;
-int quiet = 0;
 int debug = 0;
 int sender = 0;
 int running = 1;
@@ -90,6 +103,10 @@
 unsigned char ttl = 1;
 char *ident = PACKAGE_NAME;
 
+int log_level  = LOG_NOTICE;
+int log_syslog = 0;
+int log_opts   = LOG_NDELAY | LOG_PID;
+
 size_t group_num = 0;
 struct gr groups[MAX_NUM_GROUPS];
 
@@ -100,12 +117,85 @@
 int getaddr(char *iface, struct in_addr *ina);
 
 
-static int alloc_socket(struct in_addr group, int port)
+int loglvl(const char *level)
+{
+       int i;
+
+       for (i = 0; prioritynames[i].c_name; i++) {
+               size_t len = MIN(strlen(prioritynames[i].c_name), 
strlen(level));
+
+               if (!strncasecmp(prioritynames[i].c_name, level, len))
+                       return prioritynames[i].c_val;
+       }
+
+       return atoi(level);
+}
+
+int logit(int prio, char *fmt, ...)
 {
-       int sd, val;
-       struct sockaddr_in sin;
+       va_list ap;
+       int rc = 0;
+
+       va_start(ap, fmt);
+       if (log_syslog)
+               vsyslog(prio, fmt, ap);
+       else if (prio <= log_level) {
+               FILE *fp = stdout;
+               int sync = 1;
 
-       sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+               if (prio <= LOG_ERR) {
+                       fp = stderr;
+                       sync = 1;
+               }
+
+               rc = vfprintf(fp, fmt, ap);
+               if (sync)
+                       fflush(fp);
+       }
+       va_end(ap);
+
+       return rc;
+}
+
+static const char *convert_address(inet_addr_t *ss, char *buf, size_t len)
+{
+       struct sockaddr_in *sin;
+
+#ifdef AF_INET6
+       if (ss->ss_family == AF_INET6) {
+               struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
+               return inet_ntop(AF_INET6, &sin6->sin6_addr, buf, len);
+       }
+#endif
+
+       sin = (struct sockaddr_in *)ss;
+       return inet_ntop(AF_INET, &sin->sin_addr, buf, len);
+}
+
+static socklen_t ss_get_len(const struct sockaddr_storage *ss)
+{
+
+#ifdef AF_INET6
+       if (ss->ss_family == AF_INET6)
+               return sizeof(struct sockaddr_in6);
+#endif
+       if (ss->ss_family == AF_INET)
+               return sizeof(struct sockaddr_in);
+       return 0;
+}
+
+static int alloc_socket(inet_addr_t group)
+{
+       int sd, val, proto;
+
+#ifdef AF_INET6
+       if (group.ss_family == AF_INET6)
+               proto = IPPROTO_IPV6;
+       else
+#endif
+               proto = IPPROTO_IP;
+
+       sd = socket(group.ss_family, SOCK_DGRAM, IPPROTO_UDP);
        if (sd < 0) {
                ERROR("Failed opening socket(): %s", strerror(errno));
                return -1;
@@ -119,19 +209,22 @@
        if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)))
                ERROR("Failed enabling SO_REUSEADDR: %s", strerror(errno));
 
+#if defined(IP_PKTINFO) || !defined(IP_RECVDSTADDR)
        if (setsockopt(sd, SOL_IP, IP_PKTINFO, &val, sizeof(val)))
                ERROR("Failed enabling IP_PKTINFO: %s", strerror(errno));
+#elif defined(IP_RECVDSTADDR)
+       if (setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, &val, sizeof(val)))
+               ERROR("Failed enabling IP_RECVDSTADDR: %s", strerror(errno));
+#endif
 
        val = 0;
-       if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_ALL, &val, sizeof(val)))
+#ifdef IP_MULTICAST_ALL
+       if (setsockopt(sd, proto, IP_MULTICAST_ALL, &val, sizeof(val)))
                ERROR("Failed disabling IP_MULTICAST_ALL: %s", strerror(errno));
+#endif
 
-       memset(&sin, 0, sizeof(sin));
-       sin.sin_family = AF_INET;
-       sin.sin_addr   = group;
-       sin.sin_port   = htons(port);
-       if (bind(sd, (struct sockaddr *)&sin, sizeof(sin))) {
-               ERROR("Faild binding to socket: %s", strerror(errno));
+       if (bind(sd, (struct sockaddr *)&group, ss_get_len(&group))) {
+               ERROR("Failed binding to socket: %s", strerror(errno));
                close(sd);
                return -1;
        }
@@ -139,34 +232,67 @@
        return sd;
 }
 
-static int join_group(int id)
+static int join_group(struct gr *sg)
 {
-       int sd;
-       struct ip_mreqn mreqn;
-       struct gr *gr = &groups[id];
+       struct group_source_req gsr;
+       struct group_req gr;
+       char src[INET_ADDRSTR_LEN] = "*";
+       char grp[INET_ADDRSTR_LEN];
+       size_t len;
+       void *arg;
+       int ifindex;
+       int sd, op, proto;
 
        /* Index port with id if IP_MULTICAST_ALL fails */
-       sd = alloc_socket(gr->to.sin_addr, port);
+       sd = alloc_socket(sg->grp);
        if (sd < 0)
                return 1;
 
-       memset(&mreqn, 0, sizeof(mreqn));
-       mreqn.imr_ifindex = if_nametoindex(iface);
-       if (!mreqn.imr_ifindex) {
+       ifindex = if_nametoindex(iface);
+       if (!ifindex) {
                ERROR("invalid interface: %s", iface);
                goto error;
        }
-       DEBUG("Added iface %s, idx %d", iface, mreqn.imr_ifindex);
-       mreqn.imr_multiaddr = gr->to.sin_addr;
-       DEBUG("GROUP %#x (%s)", ntohl(mreqn.imr_multiaddr.s_addr), gr->group);
+       DEBUG("Added iface %s, idx %d", iface, ifindex);
 
-       if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, 
sizeof(mreqn)) < 0) {
-               ERROR("IP_ADD_MEMBERSHIP: %s", strerror(errno));
+#ifdef AF_INET6
+       if (sg->grp.ss_family == AF_INET6)
+               proto = IPPROTO_IPV6;
+       else
+#endif
+               proto = IPPROTO_IP;
+
+       if (sg->source) {
+               gsr.gsr_interface  = ifindex;
+               gsr.gsr_source     = sg->src;
+               gsr.gsr_group      = sg->grp;
+               op                 = MCAST_JOIN_SOURCE_GROUP;
+               arg                = &gsr;
+               len                = sizeof(gsr);
+       } else {
+               gr.gr_interface    = ifindex;
+               gr.gr_group        = sg->grp;
+               op                 = MCAST_JOIN_GROUP;
+               arg                = &gr;
+               len                = sizeof(gr);
+       }
+
+       if (sg->source)
+               convert_address(&sg->src, src, sizeof(src));
+       convert_address(&sg->grp, grp, sizeof(grp));
+       DEBUG("Joining group (%s,%s) on iface %s, sd: %d", src, grp, iface, sd);
+
+       if (setsockopt(sd, proto, op, arg, len)) {
+               ERROR("Failed %s group (%s,%s) on sd %d ... %d: %s",
+                     src, grp, "joining", sd, errno, strerror(errno));
                goto error;
        }
 
-       PRINT("joined group %s on %s ...", gr->group, iface);
-       gr->sd = sd;
+       if (sg->source)
+               PRINT("joined source,group %s,%s on %s ...", sg->source, 
sg->group, iface);
+       else
+               PRINT("joined group %s on %s ...", sg->group, iface);
+       sg->sd = sd;
 
        return 0;
 
@@ -177,18 +303,21 @@
 
 static void send_mcast(int signo)
 {
-       size_t i;
-       char buf[BUFSZ] = { 0 };
-       static int ssock = 0;
        static unsigned int counter = 1;
+       static int ssock = 0;
+       char buf[BUFSZ] = { 0 };
+       size_t i;
 
        if (!ssock) {
                struct in_addr addr = {
                        .s_addr = INADDR_ANY,
                };
+               int rc;
 
-               if (getaddr(iface, &addr)) {
-                       ERROR("Failed locating (a valid address on) %s: %s", 
iface, strerror(errno));
+               rc = getaddr(iface, &addr);
+               if (rc) {
+                       ERROR("No interface (%s), or no source address yet, rc 
%d: %s",
+                             iface[0] ? iface : "N/A", rc, strerror(errno));
                        return;
                }
                DEBUG("Sending on iface %s addr %s", iface, inet_ntoa(addr));
@@ -207,8 +336,8 @@
        }
 
        for (i = 0; i < group_num; i++) {
-               socklen_t len = sizeof(groups[i].to);
-               struct sockaddr *dest = (struct sockaddr *)&groups[i].to;
+               struct sockaddr *dest = (struct sockaddr *)&groups[i].grp;
+               socklen_t len = ss_get_len(&groups[i].grp);
 
                snprintf(buf, sizeof(buf), "%s%u, MC group %s ... count: %u", 
MAGIC_KEY, getpid(), groups[i].group, counter++);
                DEBUG("Sending packet on signal %d, msg: %s", signo, buf);
@@ -217,15 +346,21 @@
        }
 }
 
-struct in_pktinfo *find_pktinfo(struct msghdr *msgh)
+struct in_addr *find_dstaddr(struct msghdr *msgh)
 {
        struct cmsghdr *cmsg;
 
        for (cmsg = CMSG_FIRSTHDR(msgh); cmsg; cmsg = CMSG_NXTHDR(msgh, cmsg)) {
-               if (cmsg->cmsg_level != SOL_IP || cmsg->cmsg_type != IP_PKTINFO)
-                       continue;
-
-               return (struct in_pktinfo *)CMSG_DATA(cmsg);
+#if defined(IP_PKTINFO) || !defined(IP_RECVDSTADDR)
+               if (cmsg->cmsg_level == IPPROTO_IP &&
+                   cmsg->cmsg_type == IP_PKTINFO)
+                       return &((struct in_pktinfo *)CMSG_DATA(cmsg))->
+                           ipi_addr;
+#elif defined(IP_RECVDSTADDR)
+               if (cmsg->cmsg_level == IPPROTO_IP &&
+                   cmsg->cmsg_type == IP_RECVDSTADDR)
+                       return (struct in_addr *)CMSG_DATA(cmsg);
+#endif
        }
 
        return NULL;
@@ -233,11 +368,11 @@
 
 static void progress(void)
 {
-       size_t num = 6;
        const char *style = ".oOOo.";
        static unsigned int i = 0;
+       size_t num = 6;
 
-       if (quiet)
+       if (QUIET)
                return;
 
        if (!(i % num))
@@ -255,21 +390,22 @@
  */
 static ssize_t recv_mcast(int id)
 {
+       struct sockaddr_storage src;
+       struct in_addr *dstaddr;
+       struct msghdr msgh;
+       struct iovec iov[1];
        ssize_t bytes;
-       char buf[BUFSZ];
        char cmbuf[0x100];
-       struct msghdr msgh;
-       struct in_pktinfo *ipi;
-       struct sockaddr_storage src;
-       struct iovec iov[1] = {
-               { .iov_base = buf, .iov_len = sizeof(buf) },
-       };
+       char buf[BUFSZ];
+
+       iov[0].iov_base = buf;
+       iov[0].iov_len  = sizeof(buf);
 
        memset(&msgh, 0, sizeof(msgh));
        msgh.msg_name       = &src;
        msgh.msg_namelen    = sizeof(src);
        msgh.msg_iov        = iov;
-       msgh.msg_iovlen     = 1;
+       msgh.msg_iovlen     = NELEMS(iov);
        msgh.msg_control    = cmbuf;
        msgh.msg_controllen = sizeof(cmbuf);
 
@@ -277,10 +413,10 @@
        if (bytes < 0)
                return -1;
 
-       ipi = find_pktinfo(&msgh);
-       if (ipi) {
-               int pid = 0;
+       dstaddr = find_dstaddr(&msgh);
+       if (dstaddr) {
                char *ptr;
+               int pid = 0;
 
                buf[bytes] = 0;
                ptr = strstr(buf, MAGIC_KEY);
@@ -289,7 +425,7 @@
 
                DEBUG("Count %5zu, our PID %d, sender PID %d, group %s msg: 
%s", groups[id].count, getpid(), pid, groups[id].group, buf);
                if (pid != getpid()) {
-                       char *dst = inet_ntoa(ipi->ipi_addr);
+                       char *dst = inet_ntoa(*dstaddr);
 
                        if (strcmp(dst, groups[id].group)) {
                                ERROR("Packet for group %s received on wrong 
socket, expected group %s.", dst, groups[id].group);
@@ -327,11 +463,11 @@
 
 static int loop(void)
 {
-       size_t i;
        struct sigaction sa = {
                .sa_flags = SA_RESTART,
                .sa_handler = send_mcast,
        };
+       size_t i;
 
        if (sender) {
                struct itimerval times;
@@ -347,14 +483,14 @@
 
        while (join && running) {
                for (i = 0; i < group_num; i++) {
-                       if (join_group(i))
+                       if (join_group(&groups[i]))
                                return 1;
                }
 
                hidecursor();
                while (running) {
-                       int ret;
                        struct pollfd pfd[MAX_NUM_GROUPS];
+                       int ret;
 
                        /* One group per socket */
                        for (i = 0; i < group_num; i++) {
@@ -421,23 +557,30 @@
 
 static int usage(int code)
 {
-       printf("Usage: %s [-dhjqsv] [-c COUNT] [-i IFACE] [-p PORT] [-r SEC] 
[-t TTL]\n"
-              "              [GROUP0 .. GROUPN | GROUP+NUM]\n"
+       if (!iface[0])
+               getifname(iface, sizeof(iface));
+
+       printf("Usage: %s [-dhjsv] [-c COUNT] [-i IFACE] [-l LEVEL] [-p PORT] 
[-r SEC]\n"
+              "              [-t TTL] [-w SEC]\n"
+              "              [[SOURCE,]GROUP0 .. [SOURCE,]GROUPN | 
[SOURCE,]GROUP+NUM]\n"
               "Options:\n"
-              "  -c COUNT     Stop sending/receiving after COUNT number of 
packets\n"
-              "  -d           Debug output\n"
-              "  -h           This help text\n"
-              "  -i IFACE     Interface to use for sending/receiving 
multicast, default: %s\n"
-              "  -j           Join groups, default unless acting as sender\n"
-              "  -p PORT      UDP port number to send/listen to, default: %d\n"
-              "  -q           Quiet mode\n"
-              "  -r SEC       Do a join/leave every SEC seconds (backwards 
compat. option)\n"
-              "  -s           Act as sender, sends packets to select groups, 
default: no\n"
-              "  -t TTL       TTL to use when sending multicast packets, 
default: 1\n"
-              "  -v           Display program version\n"
+              "  -c COUNT    Stop sending/receiving after COUNT number of 
packets\n"
+              "  -d          Run as daemon in background, output except 
progress to syslog\n"
+              "  -h          This help text\n"
+              "  -i IFACE    Interface to use for sending/receiving multicast, 
default: %s\n"
+              "  -j          Join groups, default unless acting as sender\n"
+              "  -l LEVEL    Set log level; none, notice*, debug\n"
+              "  -p PORT     UDP port number to send/listen to, default: %d\n"
+              "  -r SEC      Do a join/leave every SEC seconds (backwards 
compat. option)\n"
+              "  -s          Act as sender, sends packets to select groups, 
default: no\n"
+              "  -t TTL      TTL to use when sending multicast packets, 
default: 1\n"
+              "  -v          Display program version\n"
+              "  -w SEC      Initial wait before opening sockets\n"
               "\n"
-              "Bug report address : %-40s\n"
-              "Project homepage   : %s\n", ident, iface, DEFAULT_PORT, 
PACKAGE_BUGREPORT, PACKAGE_URL);
+              "Bug report address : %-40s\n", ident, iface, DEFAULT_PORT, 
PACKAGE_BUGREPORT);
+#ifdef PACKAGE_URL
+       printf("Project homepage:   %s\n", PACKAGE_URL);
+#endif
 
        return code;
 }
@@ -457,40 +600,41 @@
 
 int main(int argc, char *argv[])
 {
-       int i, c;
-       size_t len;
        struct sigaction sa = {
                .sa_flags = SA_RESTART,
                .sa_handler = exit_loop,
        };
        extern int optind;
+       size_t ilen;
+       int foreground = 1;
+       int wait = 0;
+       int i, c;
 
-       getifname(iface, sizeof(iface));
        for (i = 0; i < MAX_NUM_GROUPS; i++)
                memset(&groups[i], 0, sizeof(groups[0]));
 
        ident = progname(argv[0]);
-       while ((c = getopt(argc, argv, "c:di:jp:qr:st:vh")) != EOF) {
+       while ((c = getopt(argc, argv, "c:di:jl:p:r:st:vhw:")) != EOF) {
                switch (c) {
                case 'c':
                        count = (size_t)atoi(optarg);
                        break;
 
                case 'd':
-                       debug = 1;
+                       foreground = 0;
                        break;
 
                case 'h':
                        return usage(0);
 
                case 'i':
-                       len = strlen(optarg);
-                       if (len >= sizeof(iface)) {
+                       ilen = strlen(optarg);
+                       if (ilen >= sizeof(iface)) {
                                ERROR("Too long interface name, max %zd 
chars.", sizeof(iface) - 1);
                                return 1;
                        }
                        strncpy(iface, optarg, sizeof(iface));
-                       iface[len] = 0;
+                       iface[ilen] = 0;
                        DEBUG("IFACE: %s", iface);
                        break;
 
@@ -498,14 +642,14 @@
                        join++;
                        break;
 
-               case 'q':
-                       quiet = 1;
+               case 'l':
+                       log_level = loglvl(optarg);
                        break;
 
                case 'p':
                        port = atoi(optarg);
                        if (port < 1024 && geteuid())
-                               ERROR("Must be root to use priviliged ports (< 
1024)");
+                               ERROR("Must be root to use privileged ports (< 
1024)");
                        break;
 
                case 'r':
@@ -528,6 +672,10 @@
                        printf("%s\n", PACKAGE_VERSION);
                        return 0;
 
+               case 'w':
+                       wait = atoi(optarg);
+                       break;
+
                default:
                        return usage(1);
                }
@@ -536,14 +684,30 @@
        if (optind == argc)
                groups[group_num++].group = strdup(DEFAULT_GROUP);
 
+       if (!foreground) {
+               if (fork())
+                       _exit(0);
+               if (setsid() == (pid_t)-1 || daemon(0, 0))
+                       _exit(1);
+               log_syslog = 1;
+
+               openlog(ident, log_opts, LOG_DAEMON);
+               setlogmask(LOG_UPTO(log_level));
+       }
+
+       if (wait)
+               sleep(wait);
+
+       if (!iface[0])
+               getifname(iface, sizeof(iface));
+
        /*
         * mcjoin group+num
         * mcjoin group0 group1 group2
         */
        for (i = optind; i < argc; i++) {
+               char *pos, *group, *source = NULL;
                int j, num = 1;
-               char *pos, *group;
-               struct in_addr addr;
 
                group = argv[i];
                pos = strchr(group, '+');
@@ -552,33 +716,109 @@
                        num = atoi(&pos[1]);
                }
 
+               pos = strchr(group, ',');
+               if (pos) {
+                       *pos++ = 0;
+                       source = strdup(group);
+                       group  = pos;
+               }
+
                if (num < 1 || (num + group_num) >= NELEMS(groups)) {
                        ERROR("Invalid number of groups given (%d), or max 
(%zd) reached.", num, NELEMS(groups));
                        return usage(1);
                }
 
                for (j = 0; j < num && group_num < NELEMS(groups); j++) {
-                       if (!inet_aton(group, &addr)) {
-                               ERROR("%s is not a valid IPv4 multicast group", 
group);
+#ifdef AF_INET6
+                       struct sockaddr_in6 *sin6;
+#endif
+                       struct sockaddr_in *sin;
+                       inet_addr_t addr;
+                       socklen_t len;
+                       uint32_t step;
+                       void *ptr;
+                       int family;
+
+#ifdef AF_INET6
+                       if (strchr(group, ':')) {
+                               family = AF_INET6;
+                               sin6 = (struct sockaddr_in6 *)&addr;
+                               ptr = &sin6->sin6_addr;
+                       } else
+#endif
+                       {
+                               family = AF_INET;
+                               sin = (struct sockaddr_in *)&addr;
+                               ptr = &sin->sin_addr;
+                       }
+
+                       if (!inet_pton(family, group, ptr)) {
+                               ERROR("%s is not a valid multicast group", 
group);
                                return usage(1);
                        }
 
-                       DEBUG("Adding group %s (0x%04x) to list ...", group, 
ntohl(addr.s_addr));
+                       DEBUG("Adding (S,G) %s,%s to list ...", source ?: "*", 
group);
+                       groups[group_num].source  = source;
                        groups[group_num++].group = strdup(group);
 
-                       /* Next group ... if any */
-                       addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
-                       group = inet_ntoa(addr);
+                       /* Next group ... */
+#ifdef AF_INET6
+                       if (strchr(group, ':')) {
+                               memcpy(&step, &sin6->sin6_addr.s6_addr[12],
+                                   sizeof(step));
+                               step = htonl(ntohl(step) + 1);
+                               memcpy(&sin6->sin6_addr.s6_addr[12], &step,
+                                   sizeof(step));
+                               len = sizeof(*sin6);
+                       } else
+#endif
+                       {
+                               step = ntohl(sin->sin_addr.s_addr);
+                               step++;
+                               sin->sin_addr.s_addr = htonl(step);
+                               len = sizeof(*sin);
+                       }
+
+                       inet_ntop(family, ptr, group, len);
                }
        }
 
        for (i = 0; i < (int)group_num; i++) {
-               char *group = groups[i].group;
-               struct sockaddr_in *sin = &groups[i].to;
-
-               sin->sin_family      = AF_INET;
-               sin->sin_addr.s_addr = inet_addr(group);
-               sin->sin_port        = htons(port); /* Index port with i if 
IP_MULTICAST_ALL fails */
+#ifdef AF_INET6
+               if (strchr(groups[i].group, ':')) {
+                       struct sockaddr_in6 *grp = (struct sockaddr_in6 
*)&groups[i].grp;
+                       struct sockaddr_in6 *src = (struct sockaddr_in6 
*)&groups[i].src;
+
+                       inet_pton(AF_INET6, groups[i].group, &grp->sin6_addr);
+                       grp->sin6_family = AF_INET6;
+                       grp->sin6_port   = htons(port);
+
+                       if (groups[i].source) {
+                               inet_pton(AF_INET6, groups[i].source, 
&src->sin6_addr);
+                               src->sin6_family = AF_INET6;
+                               src->sin6_port   = htons(port);
+                       }
+               } else
+#endif
+               {
+                       struct sockaddr_in *grp = (struct sockaddr_in 
*)&groups[i].grp;
+                       struct sockaddr_in *src = (struct sockaddr_in 
*)&groups[i].src;
+
+                       inet_pton(AF_INET, groups[i].group, &grp->sin_addr);
+                       grp->sin_family = AF_INET;
+                       grp->sin_port   = htons(port);
+
+                       if (groups[i].source) {
+                               inet_pton(AF_INET, groups[i].source, 
&src->sin_addr);
+                               src->sin_family = AF_INET;
+                               src->sin_port   = htons(port);
+                       }
+               }
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
+               groups[i].grp.ss_len = ss_get_len(&groups[i].grp);
+               if (groups[i].source)
+                       groups[i].src.ss_len = ss_get_len(&groups[i].src);
+#endif
        }
 
        /*


Reply via email to