lib: use "protocol-independed API" from RFC3678, if that is available

(This commit is based on the patch from BZ#420, and should fix that bug.)

* configure.ac: detect availability of that API
* sockopt.c (setsockopt_ipv4_multicast): use it for join/leave IPv4
  multicast groups
diff --git a/configure.ac b/configure.ac
index a5b9dc4..40e31f2 100755
--- a/configure.ac
+++ b/configure.ac
@@ -944,6 +944,15 @@
 AC_DEFINE(HAVE_BSD_STRUCT_IP_MREQ_HACK,,[Can pass ifindex in struct ip_mreq])],
 AC_MSG_RESULT(no))
 
+AC_MSG_CHECKING([for RFC3678 protocol-independed API])
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <netinet/in.h>
+], [struct group_req gr; int sock; setsockopt(sock, IPPROTO_IP, MCAST_JOIN_GROUP, (void*)&gr, sizeof(gr));
+], [AC_MSG_RESULT(yes)
+AC_DEFINE(HAVE_RFC3678,1,[Have RFC3678 protocol-independed API])],
+AC_MSG_RESULT(no))
+
 dnl ---------------------------------------------------------------
 dnl figure out how to check link-state
 dnl ---------------------------------------------------------------
diff --git a/lib/sockopt.c b/lib/sockopt.c
index 8a1eec1..9ff15ca 100644
--- a/lib/sockopt.c
+++ b/lib/sockopt.c
@@ -209,17 +209,35 @@
 			unsigned int mcast_addr,
 			unsigned int ifindex)
 {
+#ifdef HAVE_RFC3678
+  struct group_req gr;
+  struct sockaddr_in *si;
+  int ret;
+  memset (&gr, 0, sizeof(gr));
+  si = (struct sockaddr_in *)&gr.gr_group;
+  gr.gr_interface = ifindex;
+  si->sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+  si->sin_len = sizeof(struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+  si->sin_addr.s_addr = mcast_addr;
+  ret = setsockopt(sock, IPPROTO_IP, (optname == IP_ADD_MEMBERSHIP) ? 
+    MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, (void *)&gr, sizeof(gr));
+  if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
+    {
+      setsockopt(sock, IPPROTO_IP, MCAST_LEAVE_GROUP, (void *)&gr, sizeof(gr));
+      ret = setsockopt(sock, IPPROTO_IP, MCAST_JOIN_GROUP, (void *)&gr, sizeof(gr));
+    }
+  return ret;
 
-#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+#elif defined(HAVE_STRUCT_IP_MREQN_IMR_IFINDEX) && !defined(__FreeBSD__)
   struct ip_mreqn mreqn;
   int ret;
   
   assert(optname == IP_ADD_MEMBERSHIP || optname == IP_DROP_MEMBERSHIP);
   memset (&mreqn, 0, sizeof(mreqn));
 
-  if (mcast_addr)
-    mreqn.imr_multiaddr.s_addr = mcast_addr;
-  
+  mreqn.imr_multiaddr.s_addr = mcast_addr;
   mreqn.imr_ifindex = ifindex;
   
   ret = setsockopt(sock, IPPROTO_IP, optname,