From a86cb7430fd35806b8645b2fbb228030dae98b4d Mon Sep 17 00:00:00 2001 From: gambas Date: Tue, 18 Aug 2020 23:16:05 +0200 Subject: [PATCH] UpdSocket: Add multicast support. [GB.NET] * NEW: UpdSocket: Add multicast support. --- gb.net/src/CUdpSocket.c | 240 ++++++++++++++++++++++++++++++---------- gb.net/src/CUdpSocket.h | 9 +- 2 files changed, 190 insertions(+), 59 deletions(-) diff --git a/gb.net/src/CUdpSocket.c b/gb.net/src/CUdpSocket.c index 4300bbf8a..d7dbb8669 100644 --- a/gb.net/src/CUdpSocket.c +++ b/gb.net/src/CUdpSocket.c @@ -23,6 +23,7 @@ #define __CUDPSOCKET_C +#include #include #include #include @@ -289,20 +290,56 @@ int CUdpSocket_stream_write(GB_STREAM *stream, char *buffer, int len) ################################################################################################ ***********************************************************************************************/ -static bool update_broadcast(CUDPSOCKET *_object) +static bool return_error(int err, const char *msg) { - if (SOCKET->socket < 0) - return FALSE; - - if (setsockopt(SOCKET->socket, SOL_SOCKET, SO_BROADCAST, (char *)&THIS->broadcast, sizeof(int)) < 0) + if (err != 0) { - GB.Error("Cannot set broadcast socket option"); + GB.Error(msg, strerror(errno)); return TRUE; } else return FALSE; } +static bool update_broadcast(CUDPSOCKET *_object) +{ + int broadcast; + + if (SOCKET->socket < 0) + return FALSE; + + broadcast = THIS->broadcast; + return return_error(setsockopt(SOCKET->socket, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof(int)), + "Cannot set broadcast socket option: &1"); +} + +static bool update_multicast_loop(CUDPSOCKET *_object) +{ + if (SOCKET->socket < 0) + return FALSE; + + return return_error(setsockopt(SOCKET->socket, IPPROTO_IP, IP_MULTICAST_LOOP, &THIS->mc_loop, sizeof(unsigned char)), + "Cannot set multicast loop socket option: &1"); +} + +static bool update_multicast_ttl(CUDPSOCKET *_object) +{ + if (SOCKET->socket < 0) + return FALSE; + + return return_error(setsockopt(SOCKET->socket, IPPROTO_IP, IP_MULTICAST_TTL, &THIS->mc_ttl, sizeof(unsigned char)), + "Cannot set multicast ttl socket option: &1"); +} + +static void fill_in_addr(struct in_addr *addr, const char *str) +{ + addr->sin_family = PF_INET; + if (!str || !*str) + addr->sin_addr.s_addr = htonl(INADDR_ANY); + else + addr->sin_addr.s_addr = inet_addr(str); +} + static void dgram_start(CUDPSOCKET *_object) { sa_family_t domain; @@ -328,28 +365,13 @@ static void dgram_start(CUDPSOCKET *_object) else { domain = PF_INET; - if (THIS->port < 0 || THIS->port > 65535) - { - GB.Error("Invalid port number"); - return; - } } if ((SOCKET->socket = socket(domain, SOCK_DGRAM, 0)) < 0) - { - SOCKET->status = NET_CANNOT_CREATE_SOCKET; - GB.Ref(THIS); - GB.Post(CUdpSocket_post_error, (intptr_t)THIS); - return; - } + goto CANNOT_CREATE_SOCKET; if (update_broadcast(THIS) || SOCKET_update_timeout(SOCKET)) - { - SOCKET->status = NET_CANNOT_CREATE_SOCKET; - GB.Ref(THIS); - GB.Post(CUdpSocket_post_error,(intptr_t)THIS); - return; - } + goto CANNOT_CREATE_SOCKET; CLEAR(&THIS->addr); @@ -364,16 +386,29 @@ static void dgram_start(CUDPSOCKET *_object) } else { + struct in_addr mc_interface_addr; + THIS->addr.in.sin_family = domain; if (!THIS->host) THIS->addr.in.sin_addr.s_addr = htonl(INADDR_ANY); else - THIS->addr.in.sin_addr.s_addr = inet_addr(THIS->host); + //THIS->addr.in.sin_addr.s_addr = inet_addr(THIS->host); + inet_aton(THIS->hist, &THIS->addr.in.sin_addr)); THIS->addr.in.sin_port = htons(THIS->port); size = sizeof(struct sockaddr_in); addr = (struct sockaddr *)&THIS->addr.in; - //bzero(&(THIS->addr.in.sin_zero), 8); + + if (update_multicast_loop(THIS) || update_multicast_ttl(THIS)) + goto CANNOT_CREATE_SOCKET; + + + if (THIS->mc_interface && *THIS->mc_interface) + inet_aton(THIS->mc_interface, &mc_interface_addr)); + else + mc_interface_addr.s_addr = htonl(INADDR_ANY); + + setsockopt(SOCKET->socket, IPPROTO_IP, IP_MULTICAST_IF, &mc_interface_addr, sizeof(mc_interface_addr)); } if (bind(SOCKET->socket, addr, size) < 0) @@ -390,6 +425,14 @@ static void dgram_start(CUDPSOCKET *_object) GB.Stream.SetSwapping(&SOCKET->stream, htons(0x1234) != 0x1234); GB.Watch(SOCKET->socket, GB_WATCH_READ, (void *)CUdpSocket_CallBack, (intptr_t)THIS); + return; + +CANNOT_CREATE_SOCKET: + + SOCKET->status = NET_CANNOT_CREATE_SOCKET; + GB.Ref(THIS); + GB.Post(CUdpSocket_post_error, (intptr_t)THIS); + return; } @@ -427,23 +470,32 @@ BEGIN_PROPERTY(UdpSocket_SourcePath) END_PROPERTY +static void handle_address_property(void *_object, void *_param, char **store, bool allow_any) +{ + char *addr; + struct in_addr rem_ip; + + if (READ_PROPERTY) + { + GB.ReturnString(*store); + } + else + { + addr = GB.ToZeroString(PROP(GB_STRING)); + + if ((*addr == 0 && !allow_any) || !inet_aton(addr, &rem_ip)) + { + GB.Error("Invalid IP address"); + return; + } + + GB.StoreString(PROP(GB_STRING), store); + } +} + BEGIN_PROPERTY(UdpSocket_TargetHost) - char *strtmp; - struct in_addr rem_ip; - if (READ_PROPERTY) - { - GB.ReturnString(THIS->thost); - return; - } - - strtmp=GB.ToZeroString(PROP(GB_STRING)); - if ( !inet_aton(strtmp,&rem_ip) ) - { - GB.Error("Invalid IP address"); - return; - } - GB.StoreString(PROP(GB_STRING), &THIS->thost); + handle_address_property(_object, _param, &THIS->thost, FALSE); END_PROPERTY @@ -485,10 +537,13 @@ END_PROPERTY /************************************************* Gambas object "Constructor" *************************************************/ + BEGIN_METHOD_VOID(UdpSocket_new) SOCKET->stream.tag = _object; SOCKET->socket = -1; + THIS->mc_loop = 1; + THIS->mc_ttl = 1; END_METHOD @@ -612,29 +667,100 @@ END_PROPERTY BEGIN_PROPERTY(UdpSocket_Host) - struct in_addr rem_ip; - - if (READ_PROPERTY) - { - GB.ReturnString(THIS->host); - return; - } - - if (!inet_aton(GB.ToZeroString(PROP(GB_STRING)), &rem_ip)) - { - GB.Error("Invalid IP address"); - return; - } - - GB.StoreString(PROP(GB_STRING), &THIS->host); + handle_address_property(_object, _param, &THIS->host, FALSE); END_PROPERTY +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(UdpSocketMulticast_Loop) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->mc_loop); + else + { + THIS->mc_loop = VPROP(GB_BOOLEAN); + update_multicast_loop(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(UdpSocketMulticast_Ttl) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->mc_ttl); + else + { + int ttl = VPROP(GB_INTEGER); + if (ttl < 0 || ttl > 255) + { + GB.Error(GB_ERR_ARG); + return; + } + THIS->mc_ttl = ttl; + update_multicast_ttl(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(UdpSocketMulticast_Interface) + + handle_address_property(_object, _param, &THIS->mc_interface, TRUE); + +END_PROPERTY + +static void handle_multicast_membership(CUDPSOCKET *_object, bool add, GB_STRING *group, GB_STRING *interface) +{ + struct ip_mreq membership; + + if (SOCKET->socket < 0) + { + GB.Error("UDP socket is not binded"); + return; + } + + membership.imr_multiaddr.s_addr = inet_addr(GB.ToZeroString(group)); + if (interface) + membership.imr_interface.s_addr = inet_addr(GB.ToZeroString(interface)); + else + membership.imr_interface.s_addr = htonl(INADDR_ANY); + + if (setsockopt(SOCKET->socket, IPPROTO_IP, add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &membership, sizeof(membership))) + GB.Error(add ? "Cannot join multicast group: &1" : "Cannot leave multicast group: &1", strerror(errno)); +} + +BEGIN_METHOD(UdpSocketMulticast_Join, GB_STRING group; GB_STRING interface) + + handle_multicast_membership(THIS, TRUE, ARG(group), MISSING(interface) ? NULL : ARG(interface)); + +END_METHOD + +BEGIN_METHOD(UdpSocketMulticast_Leave, GB_STRING group; GB_STRING interface) + + handle_multicast_membership(THIS, FALSE, ARG(group), MISSING(interface) ? NULL : ARG(interface)); + +END_METHOD + /*************************************************************** Here we declare the public interface of UdpSocket class ***************************************************************/ + +GB_DESC CUdpSocketMulticastDesc[] = +{ + GB_DECLARE_VIRTUAL(".UdpSocket.Multicast"), + + GB_PROPERTY("Loop", "b", UdpSocketMulticast_Loop), + GB_PROPERTY("Ttl", "i", UdpSocketMulticast_Ttl), + GB_PROPERTY("Interface", "s", UdpSocketMulticast_Interface), + + GB_METHOD("Join", NULL, UdpSocketMulticast_Join, "(Group)s[(Interface)s])"), + GB_METHOD("Leave", NULL, UdpSocketMulticast_Leave, "(Group)s[(Interface)s])"), + + GB_END_DECLARE +}; + GB_DESC CUdpSocketDesc[] = { GB_DECLARE("UdpSocket", sizeof(CUDPSOCKET)), @@ -647,7 +773,7 @@ GB_DESC CUdpSocketDesc[] = GB_METHOD("_new", NULL, UdpSocket_new, NULL), GB_METHOD("_free", NULL, UdpSocket_free, NULL), GB_METHOD("Bind", NULL, UdpSocket_Bind, NULL), - GB_METHOD("Peek","s",UdpSocket_Peek,NULL), + GB_METHOD("Peek", "s", UdpSocket_Peek,NULL), GB_PROPERTY_READ("Status", "i", UdpSocket_Status), GB_PROPERTY_READ("SourceHost", "s", UdpSocket_SourceHost), @@ -663,6 +789,8 @@ GB_DESC CUdpSocketDesc[] = GB_PROPERTY("Broadcast", "b", UdpSocket_Broadcast), GB_PROPERTY("Timeout", "i", Socket_Timeout), + + GB_PROPERTY_SELF("Multicast", ".UdpSocket.Multicast"), GB_CONSTANT("_IsControl", "b", TRUE), GB_CONSTANT("_IsVirtual", "b", TRUE), diff --git a/gb.net/src/CUdpSocket.h b/gb.net/src/CUdpSocket.h index 53fd58386..ddf1c1ee2 100644 --- a/gb.net/src/CUdpSocket.h +++ b/gb.net/src/CUdpSocket.h @@ -46,15 +46,18 @@ typedef CSOCKET_COMMON common; NET_ADDRESS addr; char *thost; - int tport; char *tpath; - int broadcast; char *buffer; int buffer_pos; int buffer_len; char *path; char *host; - int port; + char *mc_interface; + unsigned short tport; + unsigned short port; + unsigned char mc_loop; + unsigned char mc_ttl; + unsigned broadcast : 1; } CUDPSOCKET;