Index: include/apr_network_io.h
===================================================================
RCS file: /home/cvspublic/apr/include/apr_network_io.h,v
retrieving revision 1.140
diff -u -d -b -w -r1.140 apr_network_io.h
--- include/apr_network_io.h	5 Mar 2003 21:22:26 -0000	1.140
+++ include/apr_network_io.h	16 Apr 2003 01:18:29 -0000
@@ -140,6 +140,30 @@
                                    * IPv6 listening socket.
                                    */
 
+#define APR_SO_MC_REUSEADDR 32768 /**< Reuse both ports and addresses, 
+                                   * suitable for multicast UDP.
+                                   * Implies APR_SO_REUSEADDR.
+                                   * @see APR_SO_REUSEADDR.
+                                   */
+
+/** @} */
+
+/**
+ * @defgroup apr_sockopt_ext Extended socket option definitions
+ * @{
+ */
+
+#define APR_SO_EXT_MC_GROUP_JOIN  1 /**< Join a Multicast group.
+                                     * The argument is a pointer to an 
+                                     * apr_socket_mc_join_arg_t.
+                                     * @see apr_socket_mc_join_arg
+                                     */
+#define APR_SO_EXT_MC_GROUP_LEAVE 2 /**< Leave a Multicast group.
+                                     * The argument is a pointer to an 
+                                     * apr_sockaddr_t.
+                                     * @see apr_sockaddr_t
+                                     */
+
 /** @} */
 
 /** Define what type of socket shutdown should occur. */
@@ -268,6 +292,19 @@
     apr_sockaddr_t *next;
 };
 
+/** @see apr_socket_mc_join_arg_t. */
+typedef struct apr_socket_mc_join_arg_t apr_socket_mc_join_arg_t;
+
+/**
+ * The sockopt argument for APR_SO_EXT_MC_GROUP_JOIN.  @a iface is the
+ * address of the local interface to use, and @a group is the address of 
+ * the group.
+ */
+struct apr_socket_mc_join_arg_t {
+  apr_sockaddr_t *iface;
+  apr_sockaddr_t *group;
+};
+
 #if APR_HAS_SENDFILE
 /** 
  * Support reusing the socket on platforms which support it (from disconnect,
@@ -667,6 +704,24 @@
  */
 APR_DECLARE(apr_status_t) apr_socket_opt_set(apr_socket_t *sock,
                                              apr_int32_t opt, apr_int32_t on);
+
+/**
+ * Setup extended socket options for the specified socket
+ * @param sock The socket to set up.
+ * @param opt The option we would like to set.  One of:
+ * <PRE>
+ *            APR_SO_EXT_MC_GROUP_JOIN  -- Join a multicast group
+ *            APR_SO_EXT_MC_GROUP_LEAVE -- Leave a multicast group
+ * </PRE>
+ * @param a pointer to an argument for the option.  Each option takes the 
+ * following type of argument:
+ * <PRE>
+ *            APR_SO_EXT_MC_GROUP_JOIN  -- apr_socket_mc_join_arg_t
+ *            APR_SO_EXT_MC_GROUP_LEAVE -- apr_sockaddr_t
+ * </PRE>
+ */
+APR_DECLARE(apr_status_t) apr_socket_opt_set_ext(apr_socket_t *sock,
+                                                 apr_int32_t opt, void *arg);
 
 /** @deprecated @see apr_socket_opt_set */
 APR_DECLARE(apr_status_t) apr_setsocketopt(apr_socket_t *sock,
Index: network_io/unix/sockopt.c
===================================================================
RCS file: /home/cvspublic/apr/network_io/unix/sockopt.c,v
retrieving revision 1.68
diff -u -d -b -w -r1.68 sockopt.c
--- network_io/unix/sockopt.c	4 Apr 2003 11:47:40 -0000	1.68
+++ network_io/unix/sockopt.c	16 Apr 2003 01:18:29 -0000
@@ -178,6 +178,20 @@
             apr_set_option(&sock->netmask, APR_SO_DEBUG, on);
         }
         break;
+    case APR_SO_MC_REUSEADDR:
+        /* on platforms with SO_REUSEPORT, the only way to have multiple 
+         * sockets listening for the same kind of multicast traffic is to 
+         * set that socket option, along with SO_REUSEADDR, so we do both.
+         */
+#if defined(SO_REUSEPORT)
+        if (on != apr_is_option_set(sock->netmask, APR_SO_MC_REUSEADDR)) {
+            if (setsockopt(sock->socketdes, SOL_SOCKET, SO_REUSEPORT, (void *)&one, sizeof(int)) == -1) {
+                return errno;
+            }
+            apr_set_option(&sock->netmask, APR_SO_MC_REUSEADDR, on);
+        }
+#endif
+        /* fall through to APR_SO_REUSEADDR. */
     case APR_SO_REUSEADDR:
         if (on != apr_is_option_set(sock->netmask, APR_SO_REUSEADDR)) {
             if (setsockopt(sock->socketdes, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(int)) == -1) {
@@ -366,6 +380,93 @@
     return APR_SUCCESS;
 }
 
+static apr_status_t mc_group_join(apr_socket_t *sock,
+                                  apr_socket_mc_join_arg_t *arg)
+{
+    switch (sock->local_addr->family) {
+    case APR_INET:
+        {
+            struct ip_mreq mreq;
+
+            memcpy(&mreq.imr_multiaddr, &arg->group->sa.sin.sin_addr,
+                   sizeof(struct in_addr));
+
+            memcpy(&mreq.imr_interface, &arg->iface->sa.sin.sin_addr,
+                   sizeof(struct in_addr));
+
+            return setsockopt(sock->socketdes, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+                              &mreq, sizeof(mreq));
+        }
+#if APR_HAVE_INET6
+    case APR_INET6:
+        {
+            struct ipv6_mreq mreq6;
+
+            memcpy(&mreq6.ipv6mr_multiaddr, &arg->group->sa.sin6.sin6_addr,
+                   sizeof(struct in6_addr));
+
+            memcpy(&mreq6.ipv6mr_interface, &arg->iface->sa.sin6.sin6_addr,
+                   sizeof(struct in6_addr));
+
+            return setsockopt(sock->socketdes, IPPROTO_IPV6,
+                              IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
+        }
+#endif
+    default:
+        return APR_EINVAL;
+    }
+}
+
+static apr_status_t mc_group_leave(apr_socket_t *sock, apr_sockaddr_t *group)
+{
+    switch (sock->local_addr->family) {
+    case APR_INET:
+        {
+            struct ip_mreq mreq;
+
+            memcpy(&mreq.imr_multiaddr, &group->sa.sin.sin_addr,
+                   sizeof(struct in_addr));
+
+            mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+
+            return setsockopt(sock->socketdes, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+                              &mreq, sizeof(mreq));
+        }
+#if APR_HAVE_INET6
+    case APR_INET6:
+        {
+            struct ipv6_mreq mreq6;
+
+            memcpy(&mreq6.ipv6mr_multiaddr, &group->sa.sin6_addr,
+                   sizeof(struct in6_addr));
+
+            mreq6.ipv6mr_interface = htonl(INADDR_ANY);
+
+            return setsockopt(sock->socketdes, IPPROTO_IPV6,
+                              IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
+        }
+#endif
+    default:
+        return APR_EINVAL;
+    }
+}
+
+apr_status_t apr_socket_opt_ext_set(apr_socket_t *sock,
+                                    apr_int32_t opt, void *arg)
+{
+    switch (opt) {
+    case APR_SO_EXT_MC_GROUP_JOIN:
+        return mc_group_join(sock, arg);
+
+    case APR_SO_EXT_MC_GROUP_LEAVE:
+        return mc_group_leave(sock, arg);
+
+    default:
+        return APR_EINVAL;
+    }
+
+    return APR_SUCCESS;
+}
 
 /* deprecated */
 apr_status_t apr_setsocketopt(apr_socket_t *sock,
