1 /* GNet - Networking library
2  * Copyright (C) 2000, 2002-3  David Helder
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA  02111-1307, USA.
18  */
19 
20 #include "gnet-private.h"
21 #include "mcast.h"
22 
23 #define GNET_UDP_SOCKET(s)       ((GUdpSocket*)(s))
24 #define GNET_IS_MCAST_SOCKET(s)  ((s)&&(GNET_UDP_SOCKET(s)->type == GNET_MCAST_SOCKET_TYPE_COOKIE))
25 
26 /**
27  *  gnet_mcast_socket_new
28  *
29  *  Creates a #GMcastSocket bound to all interfaces and an arbitrary
30  *  port.
31  *
32  *  Returns: a new #GMcastSocket; NULL on error.
33  *
34  **/
35 GMcastSocket*
gnet_mcast_socket_new(void)36 gnet_mcast_socket_new (void)
37 {
38   return gnet_mcast_socket_new_full (NULL, 0);
39 }
40 
41 
42 
43 /**
44  *  gnet_mcast_socket_new_with_port
45  *  @port: port to bind to
46  *
47  *  Creates a #GMcastSocket bound to all interfaces and port @port.
48  *  If @port is 0, an arbitrary port will be used.  Use this
49  *  constructor if you know the port of the group you will join.  Most
50  *  applications will use this constructor.
51  *
52  *  Returns: a new #GMcastSocket; NULL on error.
53  *
54  **/
55 GMcastSocket*
gnet_mcast_socket_new_with_port(gint port)56 gnet_mcast_socket_new_with_port (gint port)
57 {
58   return gnet_mcast_socket_new_full (NULL, port);
59 }
60 
61 
62 
63 /**
64  *  gnet_mcast_socket_new_full
65  *  @iface: interface to bind to (NULL for all interfaces)
66  *  @port: port to bind to (0 for an arbitrary port)
67  *
68  *  Creates a #GMcastSocket bound to interface @iface and port @port.
69  *  If @iface is NULL, all interfaces will be used.  If @port is 0, an
70  *  arbitrary port will be used.  To receive packets from this group,
71  *  call gnet_mcast_socket_join_group() next.  Loopback is disabled by
72  *  default.
73  *
74  *  Returns: a new #GMcastSocket; NULL on error.
75  *
76  **/
77 GMcastSocket*
gnet_mcast_socket_new_full(const GInetAddr * iface,gint port)78 gnet_mcast_socket_new_full (const GInetAddr* iface, gint port)
79 {
80   struct sockaddr_storage sa;
81   GMcastSocket*           ms;
82   GUdpSocket*             us;
83   const int               on = 1;
84   int                     sockfd;
85 
86   /* Create sockfd and address */
87   sockfd = _gnet_create_listen_socket (SOCK_DGRAM, iface, port, &sa);
88   if (!GNET_IS_SOCKET_VALID(sockfd))
89     {
90       g_warning ("socket() failed");
91       return NULL;
92     }
93 
94   /* Set socket option to share the UDP port */
95   if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
96 		 (void*) &on, sizeof(on)) != 0)
97     g_warning("Can't reuse mcast socket\n");
98 
99   /* Bind to the socket to some local address and port */
100   if (bind(sockfd, &GNET_SOCKADDR_SA(sa), GNET_SOCKADDR_LEN(sa)) != 0)
101     {
102       GNET_CLOSE_SOCKET (sockfd);
103       return NULL;
104     }
105 
106   /* Create socket */
107   ms = g_new0(GMcastSocket, 1);
108   us = (GUdpSocket *) ms;
109   us->type = GNET_MCAST_SOCKET_TYPE_COOKIE;
110   us->sockfd = sockfd;
111   us->sa = sa;
112   us->ref_count = 1;
113 
114   gnet_mcast_socket_set_loopback (ms, FALSE);
115 
116   return ms;
117 }
118 
119 
120 
121 /**
122  *  gnet_mcast_socket_delete:
123  *  @socket: a #GMcastSocket, or NULL
124  *
125  *  Deletes a #GMcastSocket. Does nothing if @socket is NULL.
126  *
127  **/
128 void
gnet_mcast_socket_delete(GMcastSocket * socket)129 gnet_mcast_socket_delete (GMcastSocket* socket)
130 {
131   g_return_if_fail (socket == NULL || GNET_IS_MCAST_SOCKET (socket));
132 
133   gnet_udp_socket_unref ((GUdpSocket *) socket);
134 }
135 
136 
137 
138 /**
139  *  gnet_mcast_socket_ref
140  *  @socket: a #GMcastSocket
141  *
142  *  Adds a reference to a #GMcastSocket.
143  *
144  **/
145 void
gnet_mcast_socket_ref(GMcastSocket * socket)146 gnet_mcast_socket_ref (GMcastSocket* socket)
147 {
148   g_return_if_fail (socket != NULL);
149   g_return_if_fail (GNET_IS_MCAST_SOCKET (socket));
150 
151   gnet_udp_socket_ref ((GUdpSocket *) socket);
152 }
153 
154 
155 /**
156  *  gnet_mcast_socket_unref
157  *  @socket: a #GMcastSocket
158  *
159  *  Removes a reference from a #GMcastSocket.  A #GMcastSocket is
160  *  deleted when the reference count reaches 0.
161  *
162  **/
163 void
gnet_mcast_socket_unref(GMcastSocket * socket)164 gnet_mcast_socket_unref (GMcastSocket* socket)
165 {
166   g_return_if_fail (socket != NULL);
167   g_return_if_fail (GNET_IS_MCAST_SOCKET (socket));
168 
169   gnet_udp_socket_unref ((GUdpSocket *) socket);
170 }
171 
172 
173 
174 /**
175  *  gnet_mcast_socket_get_io_channel:
176  *  @socket: a #GMcastSocket
177  *
178  *  Gets the #GIOChannel of a #GMcastSocket.
179  *
180  *  Use the channel with g_io_add_watch() to check if the socket is
181  *  readable or writable.  If the channel is readable, call
182  *  gnet_mcast_socket_receive() to receive a packet.  If the channel
183  *  is writable, call gnet_mcast_socket_send() to send a packet.  This
184  *  is not a normal giochannel - do not read from or write to it.
185  *
186  *  Every #GMcastSocket has one and only one #GIOChannel.  If you ref
187  *  the channel, then you must unref it eventually.  Do not close the
188  *  channel.  The channel is closed by GNet when the socket is
189  *  deleted.
190  *
191  *  Returns: a #GIOChannel.
192  *
193  **/
194 GIOChannel*
gnet_mcast_socket_get_io_channel(GMcastSocket * socket)195 gnet_mcast_socket_get_io_channel (GMcastSocket* socket)
196 {
197   g_return_val_if_fail (socket != NULL, NULL);
198   g_return_val_if_fail (GNET_IS_MCAST_SOCKET (socket), NULL);
199 
200   return gnet_udp_socket_get_io_channel((GUdpSocket*) socket);
201 }
202 
203 
204 /**
205  *  gnet_mcast_socket_get_local_inetaddr
206  *  @socket: a #GMcastSocket
207  *
208  *  Gets the local host's address from a #GMcastSocket.
209  *
210  *  Returns: a #GInetAddr.
211  *
212  **/
213 GInetAddr*
gnet_mcast_socket_get_local_inetaddr(const GMcastSocket * socket)214 gnet_mcast_socket_get_local_inetaddr (const GMcastSocket* socket)
215 {
216   g_return_val_if_fail (socket != NULL, NULL);
217   g_return_val_if_fail (GNET_IS_MCAST_SOCKET (socket), NULL);
218 
219   return gnet_udp_socket_get_local_inetaddr((GUdpSocket*) socket);
220 }
221 
222 
223 
224 /**
225  *  gnet_mcast_socket_join_group
226  *  @socket: a #GMcastSocket
227  *  @inetaddr: address of the group
228  *
229  *  Joins a multicast group.  Join only one group per socket.
230  *
231  *  Returns: 0 on success.
232  *
233  **/
234 gint
gnet_mcast_socket_join_group(GMcastSocket * socket,const GInetAddr * inetaddr)235 gnet_mcast_socket_join_group (GMcastSocket* socket,
236 			      const GInetAddr* inetaddr)
237 {
238   GUdpSocket *udpsocket;
239   gint rv = -1;
240 
241   g_return_val_if_fail (socket != NULL, -1);
242   g_return_val_if_fail (GNET_IS_MCAST_SOCKET (socket), -1);
243 
244   udpsocket = GNET_UDP_SOCKET (socket);
245 
246   if (GNET_INETADDR_FAMILY(inetaddr) == AF_INET)
247     {
248       struct ip_mreq mreq;
249 
250       /* Create the multicast request structure */
251       memcpy(&mreq.imr_multiaddr, GNET_INETADDR_ADDRP(inetaddr),
252 	     sizeof(mreq.imr_multiaddr));
253       mreq.imr_interface.s_addr = g_htonl(INADDR_ANY);
254 
255       /* Join the group */
256       rv = setsockopt (udpsocket->sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
257           (void*) &mreq, sizeof(mreq));
258     }
259 #ifdef HAVE_IPV6
260   else if (GNET_INETADDR_FAMILY(inetaddr) == AF_INET6)
261     {
262       struct ipv6_mreq mreq;
263 
264       /* Create the multicast request structure */
265       memcpy(&mreq.ipv6mr_multiaddr, GNET_INETADDR_ADDRP(inetaddr),
266 	     sizeof(mreq.ipv6mr_multiaddr));
267       mreq.ipv6mr_interface = 0;
268 
269       /* Join the group */
270       rv = setsockopt (udpsocket->sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
271           (void*) &mreq, sizeof(mreq));
272     }
273 #endif
274   else
275     g_assert_not_reached ();
276 
277   return rv;
278 }
279 
280 
281 /**
282  *  gnet_mcast_socket_leave_group
283  *  @socket: a #GMcastSocket
284  *  @inetaddr: address of the group
285  *
286  *  Leaves a mulitcast group.
287  *
288  *  Returns: 0 on success.
289  *
290  **/
291 gint
gnet_mcast_socket_leave_group(GMcastSocket * socket,const GInetAddr * inetaddr)292 gnet_mcast_socket_leave_group (GMcastSocket* socket,
293 			       const GInetAddr* inetaddr)
294 {
295   GUdpSocket *udpsocket;
296 
297   gint rv = -1;
298 
299   g_return_val_if_fail (socket != NULL, -1);
300   g_return_val_if_fail (GNET_IS_MCAST_SOCKET (socket), -1);
301 
302   udpsocket = GNET_UDP_SOCKET (socket);
303 
304   if (GNET_INETADDR_FAMILY(inetaddr) == AF_INET)
305     {
306       struct ip_mreq mreq;
307 
308       /* Create the multicast request structure */
309       memcpy(&mreq.imr_multiaddr, GNET_INETADDR_ADDRP(inetaddr),
310 	     sizeof(mreq.imr_multiaddr));
311       mreq.imr_interface.s_addr = g_htonl(INADDR_ANY);
312 
313       /* Join the group */
314       rv = setsockopt (udpsocket->sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
315           (void*) &mreq, sizeof(mreq));
316     }
317 #ifdef HAVE_IPV6
318   else if (GNET_INETADDR_FAMILY(inetaddr) == AF_INET6)
319     {
320       struct ipv6_mreq mreq;
321 
322       /* Create the multicast request structure */
323       memcpy(&mreq.ipv6mr_multiaddr, GNET_INETADDR_ADDRP(inetaddr),
324 	     sizeof(mreq.ipv6mr_multiaddr));
325       mreq.ipv6mr_interface = 0;
326 
327       /* Join the group */
328       rv = setsockopt (udpsocket->sockfd, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
329           (void*) &mreq, sizeof(mreq));
330     }
331 #endif
332   else
333     g_assert_not_reached ();
334 
335   return rv;
336 }
337 
338 
339 
340 /**
341  *  gnet_mcast_socket_get_ttl
342  *  @socket: a #GMcastSocket
343  *
344  *  Gets the multicast time-to-live (TTL) of a #GMcastSocket.  All IP
345  *  multicast packets have a TTL field.  This field is decremented by
346  *  a router before it forwards the packet.  If the TTL reaches zero,
347  *  the packet is discarded.  The default value is sufficient for most
348  *  applications.
349  *
350  *  The table below shows the scope for a given TTL.  The scope is
351  *  only an estimate.
352  *
353  *  <table>
354  *    <title>TTL and scope</title>
355  *    <tgroup cols="2" align="left">
356  *    <thead>
357  *      <row>
358  *        <entry>TTL</entry>
359  *        <entry>Scope</entry>
360  *      </row>
361  *    </thead>
362  *    <tbody>
363  *      <row>
364  *        <entry>0</entry>
365  *        <entry>node local</entry>
366  *      </row>
367  *      <row>
368  *        <entry>1</entry>
369  *        <entry>link local</entry>
370  *      </row>
371  *      <row>
372  *        <entry>2-32</entry>
373  *        <entry>site local</entry>
374  *      </row>
375  *      <row>
376  *        <entry>33-64</entry>
377  *        <entry>region local</entry>
378  *      </row>
379  *      <row>
380  *        <entry>65-128</entry>
381  *        <entry>continent local</entry>
382  *      </row>
383  *      <row>
384  *        <entry>129-255</entry>
385  *        <entry>unrestricted (global)</entry>
386  *      </row>
387  *    </tbody>
388  *    </tgroup>
389  *  </table>
390  *
391  *  Returns: the TTL (an integer between 0 and 255), -1 if the kernel
392  *  default is being used, or an integer less than -1 on error.
393  *
394  **/
395 gint
gnet_mcast_socket_get_ttl(const GMcastSocket * socket)396 gnet_mcast_socket_get_ttl (const GMcastSocket* socket)
397 {
398   GUdpSocket *udpsocket;
399   guchar ttl;
400   socklen_t ttl_size;
401   int rv = -1;
402 
403   g_return_val_if_fail (socket != NULL, -2);
404   g_return_val_if_fail (GNET_IS_MCAST_SOCKET (socket), -2);
405 
406   udpsocket = GNET_UDP_SOCKET (socket);
407 
408   ttl_size = sizeof(ttl);
409 
410   /* Get the IPv4 TTL if the socket is bound to an IPv4 address */
411   if (GNET_SOCKADDR_FAMILY (udpsocket->sa) == AF_INET)
412     {
413       rv = getsockopt (udpsocket->sockfd, IPPROTO_IP,
414           IP_MULTICAST_TTL, (void*) &ttl, &ttl_size);
415     }
416 
417   /* Get the IPv6 TTL if the socket is bound to an IPv6 address */
418 #ifdef HAVE_IPV6
419   else if (GNET_SOCKADDR_FAMILY (udpsocket->sa) == AF_INET6)
420     {
421       rv = getsockopt (udpsocket->sockfd, IPPROTO_IPV6,
422           IPV6_MULTICAST_HOPS, (void*) &ttl, &ttl_size);
423     }
424 #endif
425   else
426     g_assert_not_reached ();
427 
428   if (rv == -1)
429     return -2;
430 
431   return ttl;
432 }
433 
434 
435 /**
436  *  gnet_mcast_socket_set_ttl
437  *  @socket: a #GMcastSocket
438  *  @ttl: value to set TTL to
439  *
440  *  Sets the time-to-live (TTL) default of a #GMcastSocket.  Set the TTL
441  *  to -1 to use the kernel default.  The default value is sufficient
442  *  for most applications.
443  *
444  *  Returns: 0 if successful.
445  *
446  **/
447 gint
gnet_mcast_socket_set_ttl(GMcastSocket * socket,gint ttl)448 gnet_mcast_socket_set_ttl (GMcastSocket* socket, gint ttl)
449 {
450   GUdpSocket *udpsocket;
451   guchar ttlb;
452   int rv1, rv2;
453 #ifdef HAVE_IPV6
454   GIPv6Policy policy;
455 #endif
456 
457   g_return_val_if_fail (socket != NULL, -1);
458   g_return_val_if_fail (GNET_IS_MCAST_SOCKET (socket), -1);
459 
460   udpsocket = GNET_UDP_SOCKET (socket);
461 
462   rv1 = -1;
463   rv2 = -1;
464 
465   /* If the bind address is anything IPv4 *or* the bind address is
466      0::0 IPv6 and IPv6 policy allows IPv4, set IP_TTL.  In the latter case,
467      if we bind to 0::0 and the host is dual-stacked, then all IPv4
468      interfaces will be bound to also. */
469   if (GNET_SOCKADDR_FAMILY (udpsocket->sa) == AF_INET
470 #ifdef HAVE_IPV6
471       || (GNET_SOCKADDR_FAMILY (udpsocket->sa) == AF_INET6 &&
472        IN6_IS_ADDR_UNSPECIFIED(&GNET_SOCKADDR_SA6(udpsocket->sa).sin6_addr) &&
473        ((policy = gnet_ipv6_get_policy()) == GIPV6_POLICY_IPV4_THEN_IPV6 ||
474 	policy == GIPV6_POLICY_IPV6_THEN_IPV4))
475 #endif
476       )
477     {
478       ttlb = ttl;
479       rv1 = setsockopt (udpsocket->sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
480 		       (void*) &ttlb, sizeof(ttlb));
481     }
482 
483 
484   /* If the bind address is IPv6, set IPV6_UNICAST_HOPS */
485 #ifdef HAVE_IPV6
486   if (GNET_SOCKADDR_FAMILY (udpsocket->sa) == AF_INET6)
487     {
488       ttlb = ttl;
489       rv2 = setsockopt (udpsocket->sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
490 		       (void*) &ttlb, sizeof(ttlb));
491     }
492 #endif
493 
494   if (rv1 == -1 && rv2 == -1)
495     return -1;
496 
497   return 0;
498 }
499 
500 
501 
502 /**
503  *  gnet_mcast_socket_is_loopback
504  *  @socket: a #GMcastSocket
505  *
506  *  Checks if a #GMcastSocket has loopback enabled.  If loopback is
507  *  enabled, all packets sent by the host will also be received by the
508  *  host.  Loopback is disabled by default.
509  *
510  *  Returns: 0 if loopback is disabled, 1 if enabled, and -1 on error.
511  *
512  **/
513 gint
gnet_mcast_socket_is_loopback(const GMcastSocket * socket)514 gnet_mcast_socket_is_loopback (const GMcastSocket* socket)
515 {
516   GUdpSocket *udpsocket;
517   socklen_t flag_size;
518   int rv = -1;
519   gint is_loopback = 0;
520 
521   g_return_val_if_fail (socket != NULL, -1);
522   g_return_val_if_fail (GNET_IS_MCAST_SOCKET (socket), -1);
523 
524   udpsocket = GNET_UDP_SOCKET (socket);
525 
526   /* Get IPv4 loopback if it's bound to a IPv4 address */
527   if (GNET_SOCKADDR_FAMILY (udpsocket->sa) == AF_INET)
528     {
529       guchar flag;
530 
531       flag_size = sizeof (flag);
532       rv = getsockopt (udpsocket->sockfd, IPPROTO_IP, IP_MULTICAST_LOOP,
533 		      (char *) &flag, &flag_size);
534       if (flag)
535 	is_loopback = 1;
536     }
537 
538   /* Otherwise, get IPv6 loopback */
539 #ifdef HAVE_IPV6
540   else if (GNET_SOCKADDR_FAMILY (udpsocket->sa) == AF_INET6)
541     {
542       guint flag;
543 
544       flag_size = sizeof (flag);
545       rv = getsockopt (udpsocket->sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
546 		      (void *) &flag, &flag_size);
547       if (flag)
548 	is_loopback = 1;
549     }
550 #endif
551   else
552     g_assert_not_reached();
553 
554   if (rv == -1)
555     return -1;
556 
557   return is_loopback;
558 }
559 
560 
561 
562 /**
563  *  gnet_mcast_socket_set_loopback
564  *  @socket: a #GMcastSocket
565  *  @enable: should loopback be enabled?
566  *
567  *  Enables (or disables) loopback on a #GMcastSocket.  Loopback is
568  *  disabled by default.
569  *
570  *  Returns: 0 if successful.
571  *
572  **/
573 gint
gnet_mcast_socket_set_loopback(GMcastSocket * socket,gboolean enable)574 gnet_mcast_socket_set_loopback (GMcastSocket* socket, gboolean enable)
575 {
576   GUdpSocket *udpsocket;
577   int rv1, rv2;
578 #ifdef HAVE_IPV6
579   GIPv6Policy policy;
580 #endif
581 
582   g_return_val_if_fail (socket != NULL, -1);
583   g_return_val_if_fail (GNET_IS_MCAST_SOCKET (socket), -1);
584 
585   udpsocket = GNET_UDP_SOCKET (socket);
586 
587   rv1 = -1;
588   rv2 = -1;
589 
590   /* Set IPv4 loopback.  (As in set_ttl().) */
591   if (GNET_SOCKADDR_FAMILY (udpsocket->sa) == AF_INET
592 #ifdef HAVE_IPV6
593       || (GNET_SOCKADDR_FAMILY (udpsocket->sa) == AF_INET6 &&
594        IN6_IS_ADDR_UNSPECIFIED(&GNET_SOCKADDR_SA6 (udpsocket->sa).sin6_addr) &&
595        ((policy = gnet_ipv6_get_policy()) == GIPV6_POLICY_IPV4_THEN_IPV6 ||
596 	policy == GIPV6_POLICY_IPV6_THEN_IPV4))
597 #endif
598       )
599     {
600       guchar flag;
601 
602       flag = (guchar) enable;
603 
604       rv1 = setsockopt (udpsocket->sockfd, IPPROTO_IP, IP_MULTICAST_LOOP,
605 		       (char *) &flag, sizeof(flag));
606     }
607 
608   /* Set IPv6 loopback */
609 #ifdef HAVE_IPV6
610   if (GNET_SOCKADDR_FAMILY (udpsocket->sa) == AF_INET6)
611     {
612       guint flag;
613 
614       flag = (guint) enable;
615 
616       rv2 = setsockopt (udpsocket->sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
617 		       (void *) &flag, sizeof(flag));
618     }
619 #endif
620 
621   if (rv1 == -1 && rv2 == -1)
622     return -1;
623 
624   return 0;
625 }
626 
627 
628 
629 /**
630  *  gnet_mcast_socket_send
631  *  @socket: a #GMcastSocket
632  *  @buffer: buffer to send
633  *  @length: length of buffer
634  *  @dst: destination address
635  *
636  *  Sends data to a host using a #GMcastSocket.
637  *
638  *  Returns: 0 if successful.
639  *
640  **/
641 gint
gnet_mcast_socket_send(GMcastSocket * socket,const gchar * buffer,gint length,const GInetAddr * dst)642 gnet_mcast_socket_send (GMcastSocket* socket, const gchar* buffer, gint length,
643 			const GInetAddr* dst)
644 {
645   return gnet_udp_socket_send((GUdpSocket*) socket, buffer, length, dst);
646 }
647 
648 
649 /**
650  *  gnet_mcast_socket_receive
651  *  @socket: a #GMcastSocket
652  *  @buffer: buffer to write to
653  *  @length: length of @buffer
654  *  @src: pointer to source address (optional)
655  *
656  *  Receives data using a #GMcastSocket.  If @src is set, the source
657  *  address is stored in the location @src points to.  The address is
658  *  caller owned.
659  *
660  *  Returns: the number of bytes received, -1 if unsuccessful.
661  *
662  **/
663 gint
gnet_mcast_socket_receive(GMcastSocket * socket,gchar * buffer,gint length,GInetAddr ** src)664 gnet_mcast_socket_receive (GMcastSocket* socket, gchar* buffer, gint length,
665 			   GInetAddr** src)
666 {
667   return gnet_udp_socket_receive((GUdpSocket*) socket, buffer, length, src);
668 }
669 
670 
671 /**
672  *  gnet_mcast_socket_has_packet:
673  *  @socket: a #GMcastSocket
674  *
675  *  Tests if a #GMcastSocket has a packet waiting to be received.
676  *
677  *  Returns: TRUE if there is packet waiting, FALSE otherwise.
678  *
679  **/
680 gboolean
gnet_mcast_socket_has_packet(const GMcastSocket * socket)681 gnet_mcast_socket_has_packet (const GMcastSocket* socket)
682 {
683   return gnet_udp_socket_has_packet((const GUdpSocket*) socket);
684 }
685 
686