From 60edcf453539716afa50f8350531634e45e697cd Mon Sep 17 00:00:00 2001
From: Vadim Zeitlin <vadim@wxwidgets.org>
Date: Wed, 4 Jul 2007 21:33:11 +0000
Subject: [PATCH] added support for broadcasting to UDP sockets (patch 1740266)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@47126 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/changes.txt            |  1 +
 docs/latex/wx/ipaddr.tex    | 17 ++++++++++
 docs/latex/wx/socket.tex    |  6 ++++
 include/wx/gsocket.h        |  1 +
 include/wx/msw/gsockmsw.h   |  4 +++
 include/wx/sckaddr.h        |  5 +++
 include/wx/socket.h         |  4 ++-
 include/wx/unix/gsockunx.h  |  4 +++
 src/common/sckaddr.cpp      | 10 ++++++
 src/common/socket.cpp       | 22 +++++++++++++
 src/mac/carbon/cfsocket.cpp |  5 +++
 src/mac/carbon/gsocket.cpp  |  5 +++
 src/msw/gsocket.cpp         | 63 ++++++++++++++++++++++++++++++-------
 src/unix/gsocket.cpp        | 56 ++++++++++++++++++++++++++-------
 14 files changed, 180 insertions(+), 23 deletions(-)

diff --git a/docs/changes.txt b/docs/changes.txt
index 020280fbec..60494b7ba3 100644
--- a/docs/changes.txt
+++ b/docs/changes.txt
@@ -109,6 +109,7 @@ All:
 - Added functions for atomically inc/decrementing integers (Armel Asselin)
 - wxLogInterposer has been added to replace wxLogPassThrough and new
   wxLogInterposerTemp was added
+- Added support for broadcasting to UDP sockets (Andrew Vincent)
 
 All (GUI):
 
diff --git a/docs/latex/wx/ipaddr.tex b/docs/latex/wx/ipaddr.tex
index 390e72c36e..be39274d30 100644
--- a/docs/latex/wx/ipaddr.tex
+++ b/docs/latex/wx/ipaddr.tex
@@ -98,6 +98,23 @@ On IPV6 implementations, ::
 
 Returns true on success, false if something went wrong.
 
+%
+% BroadcastAddress
+%
+
+\membersection{wxIPaddress::BroadcastAddress}\label{wxIPaddressbroadcastaddress}
+
+\func{virtual bool}{BroadcastAddress}{\void}
+
+Internally, this is the same as setting the IP address
+to {\bf INADDR\_BROADCAST}.
+
+On IPV4 implementations, 255.255.255.255
+
+\wxheading{Return value}
+
+Returns true on success, false if something went wrong.
+
 %
 % LocalHost
 %
diff --git a/docs/latex/wx/socket.tex b/docs/latex/wx/socket.tex
index de3c5c5a88..ac48755b04 100644
--- a/docs/latex/wx/socket.tex
+++ b/docs/latex/wx/socket.tex
@@ -524,6 +524,8 @@ The following flags can be used:
 \twocolitem{{\bf wxSOCKET\_WAITALL}}{Wait for all required data to be read/written unless an error occurs.}
 \twocolitem{{\bf wxSOCKET\_BLOCK}}{Block the GUI (do not yield) while reading/writing data.}
 \twocolitem{{\bf wxSOCKET\_REUSEADDR}}{Allows the use of an in-use port (wxServerSocket only)}
+\twocolitem{{\bf wxSOCKET\_BROADCAST}}{Switches the socket to broadcast mode}
+\twocolitem{{\bf wxSOCKET\_NOBIND}}{Stops the socket from being bound to a specific adapter (normally used in conjunction with {\bf wxSOCKET\_BROADCAST})}
 \end{twocollist}
 
 A brief overview on how to use these flags follows.
@@ -568,6 +570,10 @@ your platform's implementation of setsockopt(). Note that on BSD-based systems (
 use of wxSOCKET\_REUSEADDR implies SO\_REUSEPORT in addition to SO\_REUSEADDR to be consistent
 with Windows.
 
+The {\bf wxSOCKET\_BROADCAST} flag controls the use of the SO\_BROADCAST standard
+setsockopt() flag. This flag allows the socket to use the broadcast address, and is generally
+used in conjunction with {\bf wxSOCKET\_NOBIND} and \helpref{wxIPaddress::BroadcastAddress}{wxipaddressbroadcastaddress}.
+
 So:
 
 {\bf wxSOCKET\_NONE} will try to read at least SOME data, no matter how much.
diff --git a/include/wx/gsocket.h b/include/wx/gsocket.h
index ffd70673d6..ade7734433 100644
--- a/include/wx/gsocket.h
+++ b/include/wx/gsocket.h
@@ -150,6 +150,7 @@ GAddressType GAddress_GetFamily(GAddress *address);
  */
 
 GSocketError GAddress_INET_SetHostName(GAddress *address, const char *hostname);
+GSocketError GAddress_INET_SetBroadcastAddress(GAddress *address);
 GSocketError GAddress_INET_SetAnyAddress(GAddress *address);
 GSocketError GAddress_INET_SetHostAddress(GAddress *address,
                                           unsigned long hostaddr);
diff --git a/include/wx/msw/gsockmsw.h b/include/wx/msw/gsockmsw.h
index 859ed60513..a32589d602 100644
--- a/include/wx/msw/gsockmsw.h
+++ b/include/wx/msw/gsockmsw.h
@@ -63,6 +63,8 @@ public:
   GSocketError SetServer();
   GSocket *WaitConnection();
   bool SetReusable();
+  bool SetBroadcast();
+  bool DontDoBind();
   GSocketError Connect(GSocketStream stream);
   GSocketError SetNonOriented();
   int Read(char *buffer, int size);
@@ -101,6 +103,8 @@ public:
   bool m_stream;
   bool m_establishing;
   bool m_reusable;
+  bool m_broadcast;
+  bool m_dobind;
   struct timeval m_timeout;
 
   /* Callbacks */
diff --git a/include/wx/sckaddr.h b/include/wx/sckaddr.h
index 98e1c0d4e7..f24627bd5f 100644
--- a/include/wx/sckaddr.h
+++ b/include/wx/sckaddr.h
@@ -64,6 +64,7 @@ public:
   virtual bool IsLocalHost() const = 0;
 
   virtual bool AnyAddress() = 0;
+  virtual bool BroadcastAddress() = 0;
 
   virtual wxString IPAddress() const = 0;
 
@@ -93,6 +94,8 @@ public:
 
   // any (0.0.0.0)
   virtual bool AnyAddress();
+  // all (255.255.255.255)
+  virtual bool BroadcastAddress();
 
   virtual wxString Hostname() const;
   wxString OrigHostname() { return m_origHostname; }
@@ -149,6 +152,8 @@ public:
 
   // any (0000:0000:0000:0000:0000:0000:0000:0000 (::))
   virtual bool AnyAddress();
+  // all (?)
+  virtual bool BroadcastAddress();
 
   // 3ffe:ffff:0100:f101:0210:a4ff:fee3:9566
   virtual wxString IPAddress() const;
diff --git a/include/wx/socket.h b/include/wx/socket.h
index 3c61361a56..ea55c060db 100644
--- a/include/wx/socket.h
+++ b/include/wx/socket.h
@@ -71,7 +71,9 @@ enum
   wxSOCKET_NOWAIT = 1,
   wxSOCKET_WAITALL = 2,
   wxSOCKET_BLOCK = 4,
-  wxSOCKET_REUSEADDR = 8
+  wxSOCKET_REUSEADDR = 8,
+  wxSOCKET_BROADCAST = 16,
+  wxSOCKET_NOBIND = 32
 };
 
 enum wxSocketType
diff --git a/include/wx/unix/gsockunx.h b/include/wx/unix/gsockunx.h
index c4a5ba3773..438b725520 100644
--- a/include/wx/unix/gsockunx.h
+++ b/include/wx/unix/gsockunx.h
@@ -52,6 +52,8 @@ public:
     GSocketError SetServer();
     GSocket *WaitConnection();
     bool SetReusable();
+    bool SetBroadcast();
+    bool DontDoBind();
     GSocketError Connect(GSocketStream stream);
     GSocketError SetNonOriented();
     int Read(char *buffer, int size);
@@ -91,6 +93,8 @@ public:
   bool m_stream;
   bool m_establishing;
   bool m_reusable;
+  bool m_broadcast;
+  bool m_dobind;
   unsigned long m_timeout;
 
   /* Callbacks */
diff --git a/src/common/sckaddr.cpp b/src/common/sckaddr.cpp
index 81bafbdf71..8e79847d83 100644
--- a/src/common/sckaddr.cpp
+++ b/src/common/sckaddr.cpp
@@ -178,6 +178,11 @@ bool wxIPV4address::IsLocalHost() const
   return (Hostname() == wxT("localhost") || IPAddress() == wxT("127.0.0.1"));
 }
 
+bool wxIPV4address::BroadcastAddress()
+{
+  return (GAddress_INET_SetBroadcastAddress(m_address) == GSOCK_NOERROR);
+}
+
 bool wxIPV4address::AnyAddress()
 {
   return (GAddress_INET_SetAnyAddress(m_address) == GSOCK_NOERROR);
@@ -275,6 +280,11 @@ bool wxIPV6address::IsLocalHost() const
   return (Hostname() == wxT("localhost") || IPAddress() == wxT("127.0.0.1"));
 }
 
+bool wxIPV6address::BroadcastAddress()
+{
+  return (GAddress_INET_SetBroadcastAddress(m_address) == GSOCK_NOERROR);
+}
+
 bool wxIPV6address::AnyAddress()
 {
   return (GAddress_INET_SetAnyAddress(m_address) == GSOCK_NOERROR);
diff --git a/src/common/socket.cpp b/src/common/socket.cpp
index e36bfe9768..7e82eb01c6 100644
--- a/src/common/socket.cpp
+++ b/src/common/socket.cpp
@@ -1105,6 +1105,12 @@ wxSocketServer::wxSocketServer(const wxSockAddress& addr_man,
     if (GetFlags() & wxSOCKET_REUSEADDR) {
         m_socket->SetReusable();
     }
+    if (GetFlags() & wxSOCKET_BROADCAST) {
+        m_socket->SetBroadcast();
+    }
+    if (GetFlags() & wxSOCKET_NOBIND) {
+        m_socket->DontDoBind();
+    }
 
     if (m_socket->SetServer() != GSOCK_NOERROR)
     {
@@ -1275,6 +1281,14 @@ bool wxSocketClient::DoConnect(wxSockAddress& addr_man, wxSockAddress* local, bo
   {
     m_socket->SetReusable();
   }
+  if (GetFlags() & wxSOCKET_BROADCAST)
+  {
+    m_socket->SetBroadcast();
+  }
+  if (GetFlags() & wxSOCKET_NOBIND)
+  {
+    m_socket->DontDoBind();
+  }
 
   // If no local address was passed and one has been set, use the one that was Set
   if (!local && m_localAddress.GetAddress())
@@ -1355,6 +1369,14 @@ wxDatagramSocket::wxDatagramSocket( const wxSockAddress& addr,
     {
         m_socket->SetReusable();
     }
+    if (GetFlags() & wxSOCKET_BROADCAST)
+    {
+        m_socket->SetBroadcast();
+    }
+    if (GetFlags() & wxSOCKET_NOBIND)
+    {
+        m_socket->DontDoBind();
+    }
     if ( m_socket->SetNonOriented() != GSOCK_NOERROR )
     {
         delete m_socket;
diff --git a/src/mac/carbon/cfsocket.cpp b/src/mac/carbon/cfsocket.cpp
index a8a331f03d..8a2383c7f5 100644
--- a/src/mac/carbon/cfsocket.cpp
+++ b/src/mac/carbon/cfsocket.cpp
@@ -1562,6 +1562,11 @@ GSocketError GAddress_INET_SetHostName(GAddress *address, const char *hostname)
   return GSOCK_NOERROR;
 }
 
+GSocketError GAddress_INET_SetBroadcastAddress(GAddress *address)
+{
+  return GAddress_INET_SetHostAddress(address, INADDR_BROADCAST);
+}
+
 GSocketError GAddress_INET_SetAnyAddress(GAddress *address)
 {
   return GAddress_INET_SetHostAddress(address, INADDR_ANY);
diff --git a/src/mac/carbon/gsocket.cpp b/src/mac/carbon/gsocket.cpp
index 04441b27df..1b14f7d3a0 100644
--- a/src/mac/carbon/gsocket.cpp
+++ b/src/mac/carbon/gsocket.cpp
@@ -1314,6 +1314,11 @@ GSocketError GAddress_INET_SetHostName(GAddress *address, const char *hostname)
   return GSOCK_NOERROR;
 }
 
+GSocketError GAddress_INET_SetBroadcastAddress(GAddress *address)
+{
+  return GAddress_INET_SetHostAddress(address, INADDR_BROADCAST);
+}
+
 GSocketError GAddress_INET_SetAnyAddress(GAddress *address)
 {
   return GAddress_INET_SetHostAddress(address, INADDR_ANY);
diff --git a/src/msw/gsocket.cpp b/src/msw/gsocket.cpp
index 2edc3ab247..63da499669 100644
--- a/src/msw/gsocket.cpp
+++ b/src/msw/gsocket.cpp
@@ -177,6 +177,8 @@ GSocket::GSocket()
   m_timeout.tv_usec = 0;
   m_establishing    = false;
   m_reusable        = false;
+  m_broadcast		= false;
+  m_dobind          = true;
 
   assert(gs_gui_functions);
   /* Per-socket GUI-specific initialization */
@@ -529,6 +531,34 @@ bool GSocket::SetReusable()
     return false;
 }
 
+/* GSocket_SetBroadcast:
+*  Simply sets the m_broadcast flag on the socket. GSocket_SetServer will
+*  make the appropriate setsockopt() call.
+*  Implemented as a GSocket function because clients (ie, wxSocketServer)
+*  don't have access to the GSocket struct information.
+*  Returns true if the flag was set correctly, false if an error occurred
+*  (ie, if the parameter was NULL)
+*/
+bool GSocket::SetBroadcast()
+{
+    /* socket must not be in use/already bound */
+    if (m_fd == INVALID_SOCKET) {
+        m_broadcast = true;
+        return true;
+    }
+    return false;
+}
+
+bool GSocket::DontDoBind()
+{
+    /* socket must not be in use/already bound */
+    if (m_fd == INVALID_SOCKET) {
+        m_dobind = false;
+        return true;
+    }
+    return false;
+}
+
 /* Client specific parts */
 
 /* GSocket_Connect:
@@ -706,18 +736,24 @@ GSocketError GSocket::SetNonOriented()
   {
     setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&arg, sizeof(arg));
   }
-
-  /* Bind to the local address,
-   * and retrieve the actual address bound.
-   */
-  if ((bind(m_fd, m_local->m_addr, m_local->m_len) != 0) ||
-      (getsockname(m_fd,
-                   m_local->m_addr,
-                   (WX_SOCKLEN_T *)&m_local->m_len) != 0))
+  if (m_broadcast)
   {
-    Close();
-    m_error = GSOCK_IOERR;
-    return GSOCK_IOERR;
+    setsockopt(m_fd, SOL_SOCKET, SO_BROADCAST, (const char*)&arg, sizeof(arg));
+  }
+  if (m_dobind)
+  {
+    /* Bind to the local address,
+     * and retrieve the actual address bound.
+     */
+    if ((bind(m_fd, m_local->m_addr, m_local->m_len) != 0) ||
+        (getsockname(m_fd,
+                     m_local->m_addr,
+                     (WX_SOCKLEN_T *)&m_local->m_len) != 0))
+    {
+      Close();
+      m_error = GSOCK_IOERR;
+      return GSOCK_IOERR;
+    }
   }
 
   return GSOCK_NOERROR;
@@ -1413,6 +1449,11 @@ GSocketError GAddress_INET_SetHostName(GAddress *address, const char *hostname)
   return GSOCK_NOERROR;
 }
 
+GSocketError GAddress_INET_SetBroadcastAddress(GAddress *address)
+{
+  return GAddress_INET_SetHostAddress(address, INADDR_BROADCAST);
+}
+
 GSocketError GAddress_INET_SetAnyAddress(GAddress *address)
 {
   return GAddress_INET_SetHostAddress(address, INADDR_ANY);
diff --git a/src/unix/gsocket.cpp b/src/unix/gsocket.cpp
index e2423cdd2c..13a3b5b12c 100644
--- a/src/unix/gsocket.cpp
+++ b/src/unix/gsocket.cpp
@@ -533,6 +533,8 @@ GSocket::GSocket()
   m_gui_dependent       = NULL;
   m_non_blocking        = false;
   m_reusable            = false;
+  m_broadcast           = false;
+  m_dobind              = true;
   m_timeout             = 10*60*1000;
                                 /* 10 minutes * 60 sec * 1000 millisec */
   m_establishing        = false;
@@ -903,6 +905,26 @@ bool GSocket::SetReusable()
     return false;
 }
 
+bool GSocket::SetBroadcast()
+{
+    /* socket must not be in use/already bound */
+    if (m_fd == INVALID_SOCKET) {
+        m_broadcast = true;
+        return true;
+    }
+    return false;
+}
+
+bool GSocket::DontDoBind()
+{
+    /* socket must not be in use/already bound */
+    if (m_fd == INVALID_SOCKET) {
+        m_dobind = false;
+        return true;
+    }
+    return false;
+}
+
 /* Client specific parts */
 
 /* GSocket_Connect:
@@ -1119,19 +1141,25 @@ GSocketError GSocket::SetNonOriented()
 #endif
   }
 
-  /* Bind to the local address,
-   * and retrieve the actual address bound.
-   */
-  if ((bind(m_fd, m_local->m_addr, m_local->m_len) != 0) ||
-      (getsockname(m_fd,
-                   m_local->m_addr,
-                   (WX_SOCKLEN_T *) &m_local->m_len) != 0))
+  if (m_broadcast)
   {
-    Close();
-    m_error = GSOCK_IOERR;
-    return GSOCK_IOERR;
+    setsockopt(m_fd, SOL_SOCKET, SO_BROADCAST, (const char*)&arg, sizeof(arg));
+  }
+  if (m_dobind)
+  {
+      /* Bind to the local address,
+       * and retrieve the actual address bound.
+       */
+      if ((bind(m_fd, m_local->m_addr, m_local->m_len) != 0) ||
+          (getsockname(m_fd,
+                       m_local->m_addr,
+                       (WX_SOCKLEN_T *) &m_local->m_len) != 0))
+      {
+        Close();
+        m_error = GSOCK_IOERR;
+        return GSOCK_IOERR;
+      }
   }
-
   return GSOCK_NOERROR;
 }
 
@@ -2068,6 +2096,12 @@ GSocketError GAddress_INET_SetHostName(GAddress *address, const char *hostname)
   return GSOCK_NOERROR;
 }
 
+
+GSocketError GAddress_INET_SetBroadcastAddress(GAddress *address)
+{
+  return GAddress_INET_SetHostAddress(address, INADDR_BROADCAST);
+}
+
 GSocketError GAddress_INET_SetAnyAddress(GAddress *address)
 {
   return GAddress_INET_SetHostAddress(address, INADDR_ANY);
-- 
2.47.2

