Line data Source code
1 : // Copyright (c) 2012-2024 Made to Order Software Corp. All Rights Reserved 2 : // 3 : // https://snapwebsites.org/project/eventdispatcher 4 : // contact@m2osw.com 5 : // 6 : // This program is free software; you can redistribute it and/or modify 7 : // it under the terms of the GNU General Public License as published by 8 : // the Free Software Foundation; either version 2 of the License, or 9 : // (at your option) any later version. 10 : // 11 : // This program is distributed in the hope that it will be useful, 12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 : // GNU General Public License for more details. 15 : // 16 : // You should have received a copy of the GNU General Public License 17 : // along with this program; if not, write to the Free Software 18 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 : 20 : /** \file 21 : * \brief Event dispatch class. 22 : * 23 : * Class used to handle events. 24 : */ 25 : 26 : 27 : // self 28 : // 29 : #include "eventdispatcher/local_dgram_base.h" 30 : 31 : #include "eventdispatcher/exception.h" 32 : 33 : 34 : // C 35 : // 36 : #include <sys/stat.h> 37 : 38 : 39 : // last include 40 : // 41 : #include <snapdev/poison.h> 42 : 43 : 44 : 45 : 46 : namespace ed 47 : { 48 : 49 : 50 : 51 : /** \brief Initialize a UDP base object. 52 : * 53 : * This function initializes the UDP base object using the address and the 54 : * port as specified. 55 : * 56 : * The port is expected to be a host side port number (i.e. 59200). 57 : * 58 : * The \p addr parameter is a textual address. It may be an IPv4 or IPv6 59 : * address and it can represent a host name or an address defined with 60 : * just numbers. If the address cannot be resolved then an error occurs 61 : * and the constructor throws. 62 : * 63 : * \note 64 : * The socket is open in this process. If you fork() and exec() then the 65 : * socket gets closed by the operating system (i.e. close on exec()). 66 : * 67 : * \warning 68 : * We only make use of the first address found by getaddrinfo(). All 69 : * the other addresses are ignored. 70 : * 71 : * \todo 72 : * Add a constructor that supports a libaddr::addr object instead of 73 : * just a string address. 74 : * 75 : * \exception invalid_parameter 76 : * The \p addr parameter is empty or the port is out of the supported range. 77 : * 78 : * \exception runtime_error 79 : * The server could not be initialized properly. Either the address cannot be 80 : * resolved, the port is incompatible or not available, or the socket could 81 : * not be created. 82 : * 83 : * \param[in] address The address to connect/listen to. 84 : * \param[in] sequential Whether the packets have to be 100% sequential. 85 : * \param[in] close_on_exec Whether the socket has to be closed on execve(). 86 : */ 87 5 : local_dgram_base::local_dgram_base( 88 : addr::addr_unix const & address 89 : , bool sequential 90 5 : , bool close_on_exec) 91 5 : : f_address(address) 92 : { 93 5 : int type(sequential ? SOCK_SEQPACKET : SOCK_DGRAM); 94 5 : if(close_on_exec) 95 : { 96 5 : type |= SOCK_CLOEXEC; 97 : } 98 : 99 5 : f_socket.reset(socket(AF_UNIX, type, 0)); 100 5 : if(f_socket == nullptr) 101 : { 102 0 : throw runtime_error( 103 : "could not create socket for: \"" 104 0 : + f_address.to_uri() 105 0 : + "\"."); 106 : } 107 : 108 : // on Linux we can restrict the permissions immediately 109 : // 110 5 : fchmod(f_socket.get(), S_IRUSR | S_IWUSR); 111 : 112 : // get the "MTU" maximum size right away for 113 : // (1) it is really fast; and 114 : // (2) it is going to work right before a first write() but may not 115 : // be if a write() was not yet fully processed 116 : // 117 5 : socklen_t optlen; 118 5 : optlen = sizeof(f_mtu_size); 119 5 : int const r(getsockopt( 120 10 : f_socket.get() 121 : , SOL_SOCKET 122 : , SO_SNDBUF 123 5 : , &f_mtu_size 124 5 : , &optlen)); 125 5 : if(r != 0) 126 : { 127 0 : throw runtime_error( 128 : "could not retrieve \"MTU\" size for: \"" 129 0 : + f_address.to_uri() 130 0 : + "\"."); 131 : } 132 5 : } 133 : 134 : 135 : /** \brief The local datagram destructor. 136 : * 137 : * To avoid potential errors with virtual destruction, we have a virtual 138 : * destructor in this base class. 139 : */ 140 5 : local_dgram_base::~local_dgram_base() 141 : { 142 5 : } 143 : 144 : 145 : /** \brief Retrieve a copy of the socket identifier. 146 : * 147 : * This function return the socket identifier as returned by the socket() 148 : * function. This can be used to change some flags. 149 : * 150 : * \return The socket used by this UDP client. 151 : */ 152 18 : int local_dgram_base::get_socket() const 153 : { 154 18 : return f_socket.get(); 155 : } 156 : 157 : 158 : /** \brief Set whether this UDP socket is to be used to broadcast messages. 159 : * 160 : * This function sets the BROADCAST flagon the socket. This is important 161 : * because by default it is expected that the socket is not used in 162 : * broadcast mode. This makes sure that was your intention. 163 : * 164 : * \note 165 : * We do not try to automatically set the flag for (1) the OS implementation 166 : * expects the end user application to systematically set the flag if 167 : * required and (2) it's complicated to know whether the address represents 168 : * the broadcast address (i.e. you need to get the info on the corresponding 169 : * interface to get the network mask, see whether the interface supports 170 : * broadcasting, etc.) We'll eventually implement that test in our 171 : * libaddr library one day. However, that would be a test we use in the 172 : * send() function to catch errors early (i.e. determine whether the 173 : * socket can be sent to in the current state). 174 : * 175 : * \param[in] state Whether to set (true) or remove (false) the broadcast 176 : * flag on this Unix datagram socket. 177 : */ 178 0 : void local_dgram_base::set_broadcast(bool state) 179 : { 180 0 : int const value(state ? 1 : 0); 181 0 : setsockopt(f_socket.get(), SOL_SOCKET, SO_BROADCAST, &value, sizeof(value)); 182 0 : } 183 : 184 : 185 : /** \brief Retrieve the size of the MTU on that connection. 186 : * 187 : * The "MTU" of the AF_UNIX message is defined by the largest allocatable 188 : * page of memory. This is defined in this file: 189 : * 190 : * /proc/sys/net/core/wmem_max 191 : * 192 : * Note that to get the maximum size of your message, you want to use 193 : * the get_mss_size() instead. The MTU size is the entire packet including 194 : * headers. 195 : * 196 : * \return -1 if the MTU could not be retrieved, the MTU's size otherwise. 197 : */ 198 0 : int local_dgram_base::get_mtu_size() const 199 : { 200 0 : return f_mtu_size; 201 : } 202 : 203 : 204 : /** \brief Determine the size of the data buffer we can use. 205 : * 206 : * This function gets the MTU and then subtract the possible header data 207 : * of the packet to 208 : * 209 : * \return The size of the MMU, which is the MTU minus the headers. 210 : */ 211 0 : int local_dgram_base::get_mss_size() const 212 : { 213 0 : int const mss(get_mtu_size()); 214 0 : return mss < 32 ? -1 : mss - 32; // it looks like the header uses 32 bytes 215 : } 216 : 217 : 218 : /** \brief Retrieve a copy of the address. 219 : * 220 : * This function returns a copy of the address as it was specified in the 221 : * constructor. This does not return a canonicalized version of the address. 222 : * 223 : * The address cannot be modified. If you need to send data on a different 224 : * address, create a new UDP client. 225 : * 226 : * \return A string with a copy of the constructor input address. 227 : */ 228 0 : addr::addr_unix local_dgram_base::get_address() const 229 : { 230 0 : return f_address; 231 : } 232 : 233 : 234 : 235 : } // namespace ed 236 : // vim: ts=4 sw=4 et