/* GNet - Networking library * Copyright (C) 2000, 2002-3 David Helder * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "gnet-private.h" #include "mcast.h" /** * gnet_mcast_socket_new * * Creates a #GMcastSocket bound to all interfaces and an arbitrary * port. * * Returns: a new #GMcastSocket; NULL on error. * **/ GMcastSocket* gnet_mcast_socket_new (void) { return gnet_mcast_socket_new_full (NULL, 0); } /** * gnet_mcast_socket_new_with_port * @port: port to bind to * * Creates a #GMcastSocket bound to all interfaces and port @port. * If @port is 0, an arbitrary port will be used. Use this * constructor if you know the port of the group you will join. Most * applications will use this constructor. * * Returns: a new #GMcastSocket; NULL on error. * **/ GMcastSocket* gnet_mcast_socket_new_with_port (gint port) { return gnet_mcast_socket_new_full (NULL, port); } /** * gnet_mcast_socket_new_full * @iface: interface to bind to (NULL for all interfaces) * @port: port to bind to (0 for an arbitrary port) * * Creates a #GMcastSocket bound to interface @iface and port @port. * If @iface is NULL, all interfaces will be used. If @port is 0, an * arbitrary port will be used. To receive packets from this group, * call gnet_mcast_socket_join_group() next. Loopback is disabled by * default. * * Returns: a new #GMcastSocket; NULL on error. * **/ GMcastSocket* gnet_mcast_socket_new_full (const GInetAddr* iface, gint port) { int sockfd; struct sockaddr_storage sa; GMcastSocket* ms; const int on = 1; /* Create sockfd and address */ sockfd = gnet_private_create_listen_socket (SOCK_DGRAM, iface, port, &sa); if (!GNET_IS_SOCKET_VALID(sockfd)) { g_warning ("socket() failed"); return NULL; } /* Set socket option to share the UDP port */ if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*) &on, sizeof(on)) != 0) g_warning("Can't reuse mcast socket\n"); /* Bind to the socket to some local address and port */ if (bind(sockfd, &GNET_SOCKADDR_SA(sa), GNET_SOCKADDR_LEN(sa)) != 0) { GNET_CLOSE_SOCKET (sockfd); return NULL; } /* Create socket */ ms = g_new0(GMcastSocket, 1); ms->sockfd = sockfd; ms->sa = sa; ms->ref_count = 1; gnet_mcast_socket_set_loopback (ms, FALSE); return ms; } /** * gnet_mcast_socket_delete: * @socket: a #GMcastSocket * * Deletes a #GMcastSocket. * **/ void gnet_mcast_socket_delete (GMcastSocket* socket) { if (socket != NULL) gnet_mcast_socket_unref(socket); } /** * gnet_mcast_socket_ref * @socket: a #GMcastSocket * * Adds a reference to a #GMcastSocket. * **/ void gnet_mcast_socket_ref (GMcastSocket* socket) { g_return_if_fail (socket != NULL); socket->ref_count++; } /** * gnet_mcast_socket_unref * @socket: a #GMcastSocket * * Removes a reference from a #GMcastSocket. A #GMcastSocket is * deleted when the reference count reaches 0. * **/ void gnet_mcast_socket_unref (GMcastSocket* socket) { g_return_if_fail (socket != NULL); socket->ref_count--; if (socket->ref_count == 0) { GNET_CLOSE_SOCKET(socket->sockfd);/* Don't care if this fails... */ if (socket->iochannel) g_io_channel_unref (socket->iochannel); g_free (socket); } } /** * gnet_mcast_socket_get_io_channel: * @socket: a #GMcastSocket * * Gets the #GIOChannel of a #GMcastSocket. * * Use the channel with g_io_add_watch() to check if the socket is * readable or writable. If the channel is readable, call * gnet_mcast_socket_receive() to receive a packet. If the channel * is writable, call gnet_mcast_socket_send() to send a packet. This * is not a normal giochannel - do not read from or write to it. * * Every #GMcastSocket has one and only one #GIOChannel. If you ref * the channel, then you must unref it eventually. Do not close the * channel. The channel is closed by GNet when the socket is * deleted. * * Returns: a #GIOChannel. * **/ GIOChannel* gnet_mcast_socket_get_io_channel (GMcastSocket* socket) { return gnet_udp_socket_get_io_channel((GUdpSocket*) socket); } /** * gnet_mcast_socket_get_local_inetaddr * @socket: a #GMcastSocket * * Gets the local host's address from a #GMcastSocket. * * Returns: a #GInetAddr. * **/ GInetAddr* gnet_mcast_socket_get_local_inetaddr (const GMcastSocket* socket) { return gnet_udp_socket_get_local_inetaddr((GUdpSocket*) socket); } /** * gnet_mcast_socket_join_group * @socket: a #GMcastSocket * @inetaddr: address of the group * * Joins a multicast group. Join only one group per socket. * * Returns: 0 on success. * **/ gint gnet_mcast_socket_join_group (GMcastSocket* socket, const GInetAddr* inetaddr) { gint rv = -1; if (GNET_INETADDR_FAMILY(inetaddr) == AF_INET) { struct ip_mreq mreq; /* Create the multicast request structure */ memcpy(&mreq.imr_multiaddr, GNET_INETADDR_ADDRP(inetaddr), sizeof(mreq.imr_multiaddr)); mreq.imr_interface.s_addr = g_htonl(INADDR_ANY); /* Join the group */ rv = setsockopt(socket->sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*) &mreq, sizeof(mreq)); } #ifdef HAVE_IPV6 else if (GNET_INETADDR_FAMILY(inetaddr) == AF_INET6) { struct ipv6_mreq mreq; /* Create the multicast request structure */ memcpy(&mreq.ipv6mr_multiaddr, GNET_INETADDR_ADDRP(inetaddr), sizeof(mreq.ipv6mr_multiaddr)); mreq.ipv6mr_interface = 0; /* Join the group */ rv = setsockopt(socket->sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP, (void*) &mreq, sizeof(mreq)); } #endif else g_assert_not_reached (); return rv; } /** * gnet_mcast_socket_leave_group * @socket: a #GMcastSocket * @inetaddr: address of the group * * Leaves a mulitcast group. * * Returns: 0 on success. * **/ gint gnet_mcast_socket_leave_group (GMcastSocket* socket, const GInetAddr* inetaddr) { gint rv = -1; if (GNET_INETADDR_FAMILY(inetaddr) == AF_INET) { struct ip_mreq mreq; /* Create the multicast request structure */ memcpy(&mreq.imr_multiaddr, GNET_INETADDR_ADDRP(inetaddr), sizeof(mreq.imr_multiaddr)); mreq.imr_interface.s_addr = g_htonl(INADDR_ANY); /* Join the group */ rv = setsockopt(socket->sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (void*) &mreq, sizeof(mreq)); } #ifdef HAVE_IPV6 else if (GNET_INETADDR_FAMILY(inetaddr) == AF_INET6) { struct ipv6_mreq mreq; /* Create the multicast request structure */ memcpy(&mreq.ipv6mr_multiaddr, GNET_INETADDR_ADDRP(inetaddr), sizeof(mreq.ipv6mr_multiaddr)); mreq.ipv6mr_interface = 0; /* Join the group */ rv = setsockopt(socket->sockfd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, (void*) &mreq, sizeof(mreq)); } #endif else g_assert_not_reached (); return rv; } /** * gnet_mcast_socket_get_ttl * @socket: a #GMcastSocket * * Gets the multicast time-to-live (TTL) of a #GMcastSocket. All IP * multicast packets have a TTL field. This field is decremented by * a router before it forwards the packet. If the TTL reaches zero, * the packet is discarded. The default value is sufficient for most * applications. * * The table below shows the scope for a given TTL. The scope is * only an estimate. * *