LCOV - code coverage report
Current view: top level - AP_HAL/utility - Socket.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 206 372 55.4 %
Date: 2026-09-09 11:02:08 Functions: 42 62 67.7 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : /*
      16             :   simple socket handling class for systems with BSD socket API
      17             :  */
      18             : 
      19             : #include <AP_HAL/AP_HAL.h>
      20             : #include <AP_Networking/AP_Networking_Config.h>
      21             : #if AP_NETWORKING_SOCKETS_ENABLED || defined(AP_SOCKET_NATIVE_ENABLED)
      22             : 
      23             : #ifndef SOCKET_CLASS_NAME
      24             : #define SOCKET_CLASS_NAME SocketAPM
      25             : #endif
      26             : 
      27             : #ifndef IN_SOCKET_NATIVE_CPP
      28             : #include "Socket.hpp"
      29             : #endif
      30             : 
      31             : #if AP_NETWORKING_BACKEND_CHIBIOS || AP_NETWORKING_BACKEND_PPP
      32             : #include <lwip/sockets.h>
      33             : #else
      34             : // SITL or Linux
      35             : #include <fcntl.h>
      36             : #include <unistd.h>
      37             : #include <sys/ioctl.h>
      38             : #include <sys/socket.h>
      39             : #if defined(AP_SOCKET_NATIVE_ENABLED)
      40             : #include <sys/stat.h>
      41             : #include <sys/un.h>
      42             : #endif
      43             : #include <netinet/in.h>
      44             : #include <netinet/tcp.h>
      45             : #include <arpa/inet.h>
      46             : #include <sys/select.h>
      47             : #include <sys/time.h>
      48             : #endif
      49             : 
      50             : #include <errno.h>
      51             : #include <stdlib.h>
      52             : 
      53             : #if AP_NETWORKING_BACKEND_CHIBIOS || AP_NETWORKING_BACKEND_PPP
      54             : #define CALL_PREFIX(x) ::lwip_##x
      55             : #else
      56             : #define CALL_PREFIX(x) ::x
      57             : #endif
      58             : 
      59             : #ifndef MSG_NOSIGNAL
      60             : #define MSG_NOSIGNAL 0
      61             : #endif
      62             : 
      63             : #if defined(AP_SOCKET_NATIVE_ENABLED)
      64             : struct UnixSocketPath {
      65             :     int fd;
      66             :     char *path;
      67             :     dev_t device;
      68             :     ino_t inode;
      69             :     UnixSocketPath *next;
      70             : };
      71             : 
      72             : static UnixSocketPath *unix_socket_paths;
      73             : static bool unix_socket_cleanup_registered;
      74             : 
      75           0 : static bool unix_path_matches(const char *path, dev_t device, ino_t inode)
      76             : {
      77             :     struct stat path_stat;
      78           0 :     return lstat(path, &path_stat) == 0 &&
      79           0 :            S_ISSOCK(path_stat.st_mode) &&
      80           0 :            path_stat.st_dev == device &&
      81           0 :            path_stat.st_ino == inode;
      82             : }
      83             : 
      84           0 : static void unlink_unix_path(const UnixSocketPath &socket_path)
      85             : {
      86           0 :     if (unix_path_matches(socket_path.path, socket_path.device, socket_path.inode)) {
      87           0 :         unlink(socket_path.path);
      88             :     }
      89           0 : }
      90             : 
      91        3337 : static void forget_unix_path(int fd)
      92             : {
      93        3337 :     UnixSocketPath **entry = &unix_socket_paths;
      94        3337 :     while (*entry != nullptr) {
      95           0 :         if ((*entry)->fd == fd) {
      96           0 :             UnixSocketPath *removed = *entry;
      97           0 :             *entry = removed->next;
      98           0 :             unlink_unix_path(*removed);
      99           0 :             free(removed->path);
     100           0 :             free(removed);
     101           0 :             return;
     102             :         }
     103           0 :         entry = &(*entry)->next;
     104             :     }
     105             : }
     106             : 
     107           0 : static void remember_unix_path(int fd, const char *path)
     108             : {
     109           0 :     if (!unix_socket_cleanup_registered) {
     110           0 :         if (atexit(SOCKET_CLASS_NAME::cleanup_unix_paths) != 0) {
     111           0 :             return;
     112             :         }
     113           0 :         unix_socket_cleanup_registered = true;
     114             :     }
     115             : 
     116             :     struct stat path_stat;
     117           0 :     if (lstat(path, &path_stat) != 0 || !S_ISSOCK(path_stat.st_mode)) {
     118           0 :         return;
     119             :     }
     120             : 
     121           0 :     char *path_copy = strdup(path);
     122           0 :     UnixSocketPath *entry = (UnixSocketPath *)calloc(1, sizeof(*entry));
     123           0 :     if (path_copy == nullptr || entry == nullptr) {
     124           0 :         free(path_copy);
     125           0 :         free(entry);
     126           0 :         return;
     127             :     }
     128             : 
     129           0 :     entry->fd = fd;
     130           0 :     entry->path = path_copy;
     131           0 :     entry->device = path_stat.st_dev;
     132           0 :     entry->inode = path_stat.st_ino;
     133           0 :     entry->next = unix_socket_paths;
     134           0 :     unix_socket_paths = entry;
     135             : }
     136             : #endif
     137             : 
     138             : /*
     139             :   constructor
     140             :  */
     141       15221 : SOCKET_CLASS_NAME::SOCKET_CLASS_NAME(bool _datagram) :
     142             :     SOCKET_CLASS_NAME(_datagram,
     143       15221 :               CALL_PREFIX(socket)(AF_INET, _datagram?SOCK_DGRAM:SOCK_STREAM, 0))
     144             : {
     145             :     static_assert(sizeof(SOCKET_CLASS_NAME::last_in_addr) >= sizeof(struct sockaddr_in), "last_in_addr must be at least sockaddr_in size");
     146       15221 : }
     147             : 
     148       15229 : SOCKET_CLASS_NAME::SOCKET_CLASS_NAME(bool _datagram, int _fd) :
     149       15229 :     datagram(_datagram),
     150       15229 :     fd(_fd),
     151       15229 :     multicast_interface_address(0)  // INADDR_ANY; routing table chooses
     152             : {
     153             : #ifdef FD_CLOEXEC
     154       15229 :     CALL_PREFIX(fcntl)(fd, F_SETFD, FD_CLOEXEC);
     155             : #endif
     156       15229 :     if (!datagram) {
     157        6691 :         int one = 1;
     158        6691 :         CALL_PREFIX(setsockopt)(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
     159             :     }
     160       15229 : }
     161             : 
     162        3727 : SOCKET_CLASS_NAME::~SOCKET_CLASS_NAME()
     163             : {
     164        3727 :     close();
     165        3727 : }
     166             : 
     167        2501 : void SOCKET_CLASS_NAME::make_sockaddr(const char *address, uint16_t port, struct sockaddr_in &sockaddr)
     168             : {
     169        2501 :     memset(&sockaddr, 0, sizeof(sockaddr));
     170             : 
     171             : #ifdef HAVE_SOCK_SIN_LEN
     172             :     sockaddr.sin_len = sizeof(sockaddr);
     173             : #endif
     174        2501 :     sockaddr.sin_port = htons(port);
     175        2501 :     sockaddr.sin_family = AF_INET;
     176        2501 :     sockaddr.sin_addr.s_addr = htonl(inet_str_to_addr(address));
     177        2501 : }
     178             : 
     179             : #if !defined(HAL_BOOTLOADER_BUILD) || AP_NETWORKING_CAN_MCAST_ENABLED
     180             : /*
     181             :   connect the socket
     182             :  */
     183          17 : bool SOCKET_CLASS_NAME::connect(const char *address, uint16_t port)
     184             : {
     185          17 :     if (fd == -1) {
     186           0 :         return false;
     187             :     }
     188             :     struct sockaddr_in sockaddr;
     189             :     int ret;
     190          17 :     int one = 1;
     191          17 :     make_sockaddr(address, port, sockaddr);
     192             : 
     193          17 :     if (datagram && is_multicast_address(sockaddr)) {
     194             :         /*
     195             :           connect fd_in as a multicast UDP socket
     196             :          */
     197           6 :         fd_in = CALL_PREFIX(socket)(AF_INET, SOCK_DGRAM, 0);
     198           6 :         if (fd_in == -1) {
     199           0 :             return false;
     200             :         }
     201           6 :         struct sockaddr_in sockaddr_mc = sockaddr;
     202           6 :         struct ip_mreq mreq {};
     203             : #ifdef FD_CLOEXEC
     204           6 :         CALL_PREFIX(fcntl)(fd_in, F_SETFD, FD_CLOEXEC);
     205             : #endif
     206           6 :         IGNORE_RETURN(CALL_PREFIX(setsockopt)(fd_in, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)));
     207             : 
     208             : #if defined(__CYGWIN__) || defined(__CYGWIN64__) || defined(CYGWIN_BUILD)
     209             :         /*
     210             :           on cygwin you need to bind to INADDR_ANY then use the multicast
     211             :           IP_ADD_MEMBERSHIP to get on the right address
     212             :         */
     213             :         sockaddr_mc.sin_addr.s_addr = htonl(INADDR_ANY);
     214             : #endif
     215             :     
     216           6 :         ret = CALL_PREFIX(bind)(fd_in, (struct sockaddr *)&sockaddr_mc, sizeof(sockaddr));
     217           6 :         if (ret == -1) {
     218           0 :             goto fail_multi;
     219             :         }
     220             : 
     221           6 :         mreq.imr_multiaddr.s_addr = sockaddr.sin_addr.s_addr;
     222           6 :         mreq.imr_interface.s_addr = multicast_interface_address;
     223             : 
     224           6 :         ret = CALL_PREFIX(setsockopt)(fd_in, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
     225           6 :         if (ret == -1) {
     226           0 :             goto fail_multi;
     227             :         }
     228             : 
     229           6 :         if (multicast_interface_address != 0) {
     230             :             /*
     231             :               also send on that interface; without this the routing
     232             :               table chooses, which for a multicast address with no
     233             :               specific route means whichever interface the default
     234             :               route uses
     235             :              */
     236           5 :             struct in_addr ifaddr {};
     237           5 :             ifaddr.s_addr = multicast_interface_address;
     238           5 :             ret = CALL_PREFIX(setsockopt)(fd, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, sizeof(ifaddr));
     239           5 :             if (ret == -1) {
     240           0 :                 goto fail_multi;
     241             :             }
     242             :         }
     243             :     }
     244             : 
     245          17 :     if (datagram && sockaddr.sin_addr.s_addr == INADDR_BROADCAST) {
     246             :         // setup for bi-directional UDP broadcast
     247           1 :         set_broadcast();
     248           1 :         reuseaddress();
     249             :     }
     250             : 
     251          17 :     ret = CALL_PREFIX(connect)(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
     252          17 :     if (ret != 0) {
     253           0 :         if (errno == EINPROGRESS) {
     254           0 :             pending_connect = true;
     255             :         }
     256           0 :         return false;
     257             :     }
     258          17 :     connected = true;
     259             : 
     260          17 :     if (datagram && sockaddr.sin_addr.s_addr == INADDR_BROADCAST) {
     261             :         // for bi-directional UDP broadcast we need 2 sockets
     262             :         struct sockaddr_in send_addr;
     263           1 :         socklen_t send_len = sizeof(send_addr);
     264           1 :         if (CALL_PREFIX(getsockname)(fd, (struct sockaddr *)&send_addr, &send_len) == -1) {
     265           0 :             return false;
     266             :         }
     267           1 :         fd_in = CALL_PREFIX(socket)(AF_INET, SOCK_DGRAM, 0);
     268           1 :         if (fd_in == -1) {
     269           0 :             goto fail_multi;
     270             :         }
     271           1 :         CALL_PREFIX(setsockopt)(fd_in, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
     272             :         // 2nd socket needs to be bound to wildcard
     273           1 :         send_addr.sin_addr.s_addr = INADDR_ANY;
     274           1 :         ret = CALL_PREFIX(bind)(fd_in, (struct sockaddr *)&send_addr, sizeof(send_addr));
     275           1 :         if (ret == -1) {
     276           0 :             goto fail_multi;
     277             :         }
     278             :     }
     279          17 :     return true;
     280             : 
     281           0 : fail_multi:
     282           0 :     CALL_PREFIX(close)(fd_in);
     283           0 :     fd_in = -1;
     284           0 :     return false;
     285             : }
     286             : #endif // !defined(HAL_BOOTLOADER_BUILD) || AP_NETWORKING_CAN_MCAST_ENABLED
     287             : 
     288             : /*
     289             :   connect the socket with a timeout
     290             :  */
     291         386 : bool SOCKET_CLASS_NAME::connect_timeout(const char *address, uint16_t port, uint32_t timeout_ms)
     292             : {
     293         386 :     if (fd == -1) {
     294           0 :         return false;
     295             :     }
     296             :     struct sockaddr_in sockaddr;
     297         386 :     make_sockaddr(address, port, sockaddr);
     298             : 
     299         386 :     set_blocking(false);
     300             : 
     301         386 :     int ret = CALL_PREFIX(connect)(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
     302         386 :     if (ret == 0) {
     303             :         // instant connect?
     304           0 :         return true;
     305             :     }
     306         386 :     if (errno != EINPROGRESS) {
     307           0 :         return false;
     308             :     }
     309         386 :     bool pollret = pollout(timeout_ms);
     310         386 :     if (!pollret) {
     311           0 :         return false;
     312             :     }
     313         386 :     int sock_error = 0;
     314         386 :     socklen_t len = sizeof(sock_error);
     315         386 :     if (CALL_PREFIX(getsockopt)(fd, SOL_SOCKET, SO_ERROR, (void*)&sock_error, &len) != 0) {
     316           0 :         return false;
     317             :     }
     318         386 :     connected = sock_error == 0;
     319         386 :     return connected;
     320             : }
     321             : 
     322             : /*
     323             :   bind the socket
     324             :  */
     325        2098 : bool SOCKET_CLASS_NAME::bind(const char *address, uint16_t port)
     326             : {
     327        2098 :     if (fd == -1) {
     328           0 :         return false;
     329             :     }
     330             :     struct sockaddr_in sockaddr;
     331        2098 :     make_sockaddr(address, port, sockaddr);
     332             : 
     333        2098 :     reuseaddress();
     334        2098 :     if (CALL_PREFIX(bind)(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != 0) {
     335           0 :         return false;
     336             :     }
     337        2098 :     return true;
     338             : }
     339             : 
     340             : #if defined(AP_SOCKET_NATIVE_ENABLED)
     341             : /*
     342             :   connect a native Unix domain socket
     343             :  */
     344           0 : bool SOCKET_CLASS_NAME::connect_unix(const char *path)
     345             : {
     346           0 :     if (path == nullptr || path[0] == '\0') {
     347           0 :         errno = EINVAL;
     348           0 :         return false;
     349             :     }
     350             : 
     351           0 :     struct sockaddr_un sockaddr {};
     352           0 :     if (strlen(path) >= sizeof(sockaddr.sun_path)) {
     353           0 :         errno = ENAMETOOLONG;
     354           0 :         return false;
     355             :     }
     356           0 :     sockaddr.sun_family = AF_UNIX;
     357           0 :     strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1);
     358             : 
     359           0 :     const int unix_fd = CALL_PREFIX(socket)(AF_UNIX, datagram ? SOCK_DGRAM : SOCK_STREAM, 0);
     360           0 :     if (unix_fd == -1) {
     361           0 :         return false;
     362             :     }
     363             : #ifdef FD_CLOEXEC
     364           0 :     CALL_PREFIX(fcntl)(unix_fd, F_SETFD, FD_CLOEXEC);
     365             : #endif
     366             : 
     367           0 :     if (CALL_PREFIX(connect)(unix_fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) == -1) {
     368           0 :         const int connect_errno = errno;
     369           0 :         CALL_PREFIX(close)(unix_fd);
     370           0 :         errno = connect_errno;
     371           0 :         return false;
     372             :     }
     373             : 
     374           0 :     close();
     375           0 :     fd = unix_fd;
     376           0 :     connected = true;
     377           0 :     pending_connect = false;
     378           0 :     return true;
     379             : }
     380             : 
     381             : /*
     382             :   bind a native Unix domain datagram socket
     383             :  */
     384           0 : bool SOCKET_CLASS_NAME::bind_unix(const char *path)
     385             : {
     386           0 :     if (!datagram || path == nullptr || path[0] == '\0') {
     387           0 :         errno = EINVAL;
     388           0 :         return false;
     389             :     }
     390             : 
     391           0 :     struct sockaddr_un sockaddr {};
     392           0 :     if (strlen(path) >= sizeof(sockaddr.sun_path)) {
     393           0 :         errno = ENAMETOOLONG;
     394           0 :         return false;
     395             :     }
     396           0 :     sockaddr.sun_family = AF_UNIX;
     397           0 :     strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1);
     398             : 
     399           0 :     const int unix_fd = CALL_PREFIX(socket)(AF_UNIX, SOCK_DGRAM, 0);
     400           0 :     if (unix_fd == -1) {
     401           0 :         return false;
     402             :     }
     403             : #ifdef FD_CLOEXEC
     404           0 :     CALL_PREFIX(fcntl)(unix_fd, F_SETFD, FD_CLOEXEC);
     405             : #endif
     406             : 
     407           0 :     int ret = CALL_PREFIX(bind)(unix_fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
     408           0 :     if (ret == -1 && errno == EADDRINUSE) {
     409             :         struct stat path_stat;
     410           0 :         if (lstat(path, &path_stat) == 0 && S_ISSOCK(path_stat.st_mode)) {
     411           0 :             const int probe_fd = CALL_PREFIX(socket)(AF_UNIX, SOCK_DGRAM, 0);
     412           0 :             const int connect_ret = probe_fd == -1 ? -1 : CALL_PREFIX(connect)(
     413             :                 probe_fd,
     414             :                 (struct sockaddr *)&sockaddr,
     415           0 :                 sizeof(sockaddr));
     416           0 :             const int connect_errno = errno;
     417           0 :             if (probe_fd != -1) {
     418           0 :                 CALL_PREFIX(close)(probe_fd);
     419             :             }
     420           0 :             if (connect_ret == -1 && connect_errno == ECONNREFUSED &&
     421           0 :                 unix_path_matches(path, path_stat.st_dev, path_stat.st_ino) &&
     422           0 :                 unlink(path) == 0) {
     423           0 :                 ret = CALL_PREFIX(bind)(unix_fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
     424             :             }
     425             :         }
     426             :     }
     427           0 :     if (ret == -1) {
     428           0 :         const int bind_errno = errno;
     429           0 :         CALL_PREFIX(close)(unix_fd);
     430           0 :         errno = bind_errno;
     431           0 :         return false;
     432             :     }
     433             : 
     434           0 :     if (fd != -1) {
     435             : #if defined(AP_SOCKET_NATIVE_ENABLED)
     436           0 :         forget_unix_path(fd);
     437             : #endif
     438           0 :         CALL_PREFIX(close)(fd);
     439             :     }
     440           0 :     fd = unix_fd;
     441           0 :     remember_unix_path(fd, path);
     442           0 :     return true;
     443             : }
     444             : 
     445           0 : void SOCKET_CLASS_NAME::register_unix_path(int fd, const char *path)
     446             : {
     447           0 :     remember_unix_path(fd, path);
     448           0 : }
     449             : 
     450        1640 : void SOCKET_CLASS_NAME::cleanup_unix_paths()
     451             : {
     452        1640 :     for (UnixSocketPath *entry = unix_socket_paths; entry != nullptr; entry = entry->next) {
     453           0 :         unlink_unix_path(*entry);
     454             :     }
     455        1640 : }
     456             : #endif
     457             : 
     458             : 
     459             : /*
     460             :   set SO_REUSEADDR
     461             :  */
     462        4208 : bool SOCKET_CLASS_NAME::reuseaddress(void) const
     463             : {
     464        4208 :     if (fd == -1) {
     465           0 :         return false;
     466             :     }
     467        4208 :     int one = 1;
     468        4208 :     return (CALL_PREFIX(setsockopt)(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != -1);
     469             : }
     470             : 
     471             : /*
     472             :   set blocking state
     473             :  */
     474        2880 : bool SOCKET_CLASS_NAME::set_blocking(bool blocking) const
     475             : {
     476        2880 :     if (fd == -1) {
     477           0 :         return false;
     478             :     }
     479             :     int fcntl_ret;
     480        2880 :     if (blocking) {
     481         390 :         fcntl_ret = CALL_PREFIX(fcntl)(fd, F_SETFL, CALL_PREFIX(fcntl)(fd, F_GETFL, 0) & ~O_NONBLOCK);
     482         390 :         if (fd_in != -1) {
     483           0 :             fcntl_ret |= CALL_PREFIX(fcntl)(fd_in, F_SETFL, CALL_PREFIX(fcntl)(fd_in, F_GETFL, 0) & ~O_NONBLOCK);
     484             :         }
     485             :     } else {
     486        2490 :         fcntl_ret = CALL_PREFIX(fcntl)(fd, F_SETFL, CALL_PREFIX(fcntl)(fd, F_GETFL, 0) | O_NONBLOCK);
     487        2490 :         if (fd_in != -1) {
     488           0 :             fcntl_ret |= CALL_PREFIX(fcntl)(fd_in, F_SETFL, CALL_PREFIX(fcntl)(fd_in, F_GETFL, 0) | O_NONBLOCK);
     489             :         }
     490             :     }
     491        2880 :     return fcntl_ret != -1;
     492             : }
     493             : 
     494             : /*
     495             :   set cloexec state
     496             :  */
     497        2089 : bool SOCKET_CLASS_NAME::set_cloexec() const
     498             : {
     499        2089 :     if (fd == -1) {
     500           0 :         return false;
     501             :     }
     502             : #ifdef FD_CLOEXEC
     503        2089 :     return (CALL_PREFIX(fcntl)(fd, F_SETFD, FD_CLOEXEC) != -1);
     504             : #else
     505             :     return false;
     506             : #endif
     507             : }
     508             : 
     509             : /*
     510             :   send some data
     511             :  */
     512       67244 : ssize_t SOCKET_CLASS_NAME::send(const void *buf, size_t size) const
     513             : {
     514       67244 :     if (fd == -1) {
     515           0 :         return -1;
     516             :     }
     517       67244 :     return CALL_PREFIX(send)(fd, buf, size, MSG_NOSIGNAL);
     518             : }
     519             : 
     520             : /*
     521             :   send some data with address as a uint32_t
     522             :  */
     523        4678 : ssize_t SOCKET_CLASS_NAME::sendto(const void *buf, size_t size, uint32_t address, uint16_t port)
     524             : {
     525        4678 :     if (fd == -1) {
     526           0 :         return -1;
     527             :     }
     528        4678 :     struct sockaddr_in sockaddr = {};
     529             : 
     530             : #ifdef HAVE_SOCK_SIN_LEN
     531             :     sockaddr.sin_len = sizeof(sockaddr);
     532             : #endif
     533        4678 :     sockaddr.sin_port = htons(port);
     534        4678 :     sockaddr.sin_family = AF_INET;
     535        4678 :     sockaddr.sin_addr.s_addr = htonl(address);
     536             : 
     537        4678 :     return CALL_PREFIX(sendto)(fd, buf, size, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
     538             : }
     539             : 
     540             : /*
     541             :   send some data with address as a string
     542             :  */
     543           0 : ssize_t SOCKET_CLASS_NAME::sendto(const void *buf, size_t size, const char *address, uint16_t port)
     544             : {
     545           0 :     if (fd == -1) {
     546           0 :         return -1;
     547             :     }
     548             :     struct sockaddr_in sockaddr;
     549           0 :     make_sockaddr(address, port, sockaddr);
     550           0 :     return CALL_PREFIX(sendto)(fd, buf, size, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
     551             : }
     552             : 
     553             : /*
     554             :   receive some data
     555             :  */
     556    39381408 : ssize_t SOCKET_CLASS_NAME::recv(void *buf, size_t size, uint32_t timeout_ms)
     557             : {
     558    39381408 :     if (!pollin(timeout_ms)) {
     559    36583911 :         errno = EWOULDBLOCK;
     560    36583911 :         return -1;
     561             :     }
     562     2797516 :     socklen_t len = sizeof(struct sockaddr_in);
     563     2797516 :     int fin = get_read_fd();
     564             :     ssize_t ret;
     565     2797501 :     uint32_t in_addr[4] = {};
     566     2797501 :     ret = CALL_PREFIX(recvfrom)(fin, buf, size, MSG_DONTWAIT, (sockaddr *)&in_addr[0], &len);
     567     2797501 :     if (ret > 0) {
     568             :         // only update last_in_addr if we received data
     569     2793560 :         memcpy(last_in_addr, in_addr, sizeof(last_in_addr));
     570             :     } else {
     571        3941 :         if (!datagram && connected && ret == 0) {
     572             :             // remote host has closed connection
     573           2 :             connected = false;
     574             :         }
     575        3941 :         return ret;
     576             :     }
     577     2793560 :     if (fd_in != -1) {
     578             :         /*
     579             :           for multicast check we are not receiving from ourselves
     580             :          */
     581             :         struct sockaddr_in send_addr;
     582       28132 :         socklen_t send_len = sizeof(send_addr);
     583       28132 :         if (CALL_PREFIX(getsockname)(fd, (struct sockaddr *)&send_addr, &send_len) != 0) {
     584       27709 :             return -1;
     585             :         }
     586       28132 :         const struct sockaddr_in &sin = *(struct sockaddr_in *)&last_in_addr[0];
     587       28132 :         if (sin.sin_port == send_addr.sin_port &&
     588       27709 :             sin.sin_family == send_addr.sin_family &&
     589       27709 :             sin.sin_addr.s_addr == send_addr.sin_addr.s_addr) {
     590             :             // discard packets from ourselves
     591       27709 :             return -1;
     592             :         }
     593             :     }
     594     2765851 :     return ret;
     595             : }
     596             : 
     597             : /*
     598             :   return the IP address and port of the last received packet
     599             :  */
     600           0 : void SOCKET_CLASS_NAME::last_recv_address(const char *&ip_addr, uint16_t &port) const
     601             : {
     602             :     static char buf[IP4_STR_LEN];
     603           0 :     auto *str = last_recv_address(buf, sizeof(buf), port);
     604           0 :     ip_addr = str;
     605           0 : }
     606             : 
     607             : /*
     608             :   return the IP address and port of the last received packet, using caller supplied buffer
     609             :  */
     610           5 : const char *SOCKET_CLASS_NAME::last_recv_address(char *ip_addr_buf, uint8_t buflen, uint16_t &port) const
     611             : {
     612           5 :     const struct sockaddr_in &sin = *(struct sockaddr_in *)&last_in_addr[0];
     613             : 
     614           5 :     const char *ret = inet_addr_to_str(ntohl(sin.sin_addr.s_addr), ip_addr_buf, buflen);
     615           5 :     if (ret == nullptr) {
     616           0 :         return nullptr;
     617             :     }
     618           5 :     port = ntohs(sin.sin_port);
     619           5 :     return ret;
     620             : }
     621             : 
     622             : /*
     623             :   return the IP address and port of the last received packet
     624             :  */
     625       11815 : bool SOCKET_CLASS_NAME::last_recv_address(uint32_t &ip_addr, uint16_t &port) const
     626             : {
     627       11815 :     const struct sockaddr_in &sin = *(struct sockaddr_in *)&last_in_addr[0];
     628       11815 :     if (sin.sin_family != AF_INET) {
     629           2 :         return false;
     630             :     }
     631       11813 :     ip_addr = ntohl(sin.sin_addr.s_addr);
     632       11813 :     port = ntohs(sin.sin_port);
     633       11813 :     if (ip_addr == 0 ||
     634       11813 :         port == 0) {
     635           0 :         return false;
     636             :     }
     637       11813 :     return true;
     638             : }
     639             : 
     640           1 : void SOCKET_CLASS_NAME::set_broadcast(void) const
     641             : {
     642           1 :     if (fd == -1) {
     643           0 :         return;
     644             :     }
     645           1 :     int one = 1;
     646           1 :     CALL_PREFIX(setsockopt)(fd,SOL_SOCKET,SO_BROADCAST,(char *)&one,sizeof(one));
     647             : }
     648             : 
     649             : /*
     650             :   return true if there is pending data for input
     651             :  */
     652    39389406 : bool SOCKET_CLASS_NAME::pollin(uint32_t timeout_ms)
     653             : {
     654             :     fd_set fds;
     655             :     struct timeval tv;
     656             : 
     657   669619379 :     FD_ZERO(&fds);
     658    39389406 :     int fin = get_read_fd();
     659    39389408 :     if (fin == -1) {
     660           0 :         return false;
     661             :     }
     662    39389408 :     FD_SET(fin, &fds);
     663             : 
     664    39389408 :     tv.tv_sec = timeout_ms / 1000;
     665    39389408 :     tv.tv_usec = (timeout_ms % 1000) * 1000UL;
     666             : 
     667    39389408 :     if (CALL_PREFIX(select)(fin+1, &fds, nullptr, nullptr, &tv) != 1) {
     668    36591927 :         return false;
     669             :     }
     670     2797507 :     return true;
     671             : }
     672             : 
     673             : 
     674             : /*
     675             :   return true if there is room for output data
     676             :  */
     677         386 : bool SOCKET_CLASS_NAME::pollout(uint32_t timeout_ms)
     678             : {
     679         386 :     if (fd == -1) {
     680           0 :         return false;
     681             :     }
     682             :     fd_set fds;
     683             :     struct timeval tv;
     684             : 
     685        6562 :     FD_ZERO(&fds);
     686         386 :     FD_SET(fd, &fds);
     687             : 
     688         386 :     tv.tv_sec = timeout_ms / 1000;
     689         386 :     tv.tv_usec = (timeout_ms % 1000) * 1000UL;
     690             : 
     691         386 :     if (CALL_PREFIX(select)(fd+1, nullptr, &fds, nullptr, &tv) != 1) {
     692           0 :         return false;
     693             :     }
     694             : 
     695         386 :     if (pending_connect) {
     696           0 :         int sock_error = 0;
     697           0 :         socklen_t len = sizeof(sock_error);
     698           0 :         if (CALL_PREFIX(getsockopt)(fd, SOL_SOCKET, SO_ERROR, (void*)&sock_error, &len) == 0 &&
     699           0 :             sock_error == 0) {
     700           0 :             connected = true;
     701             :         }
     702           0 :         pending_connect = false;
     703             :     }
     704             : 
     705         386 :     return true;
     706             : }
     707             : 
     708             : /* 
     709             :    start listening for new tcp connections
     710             :  */
     711           8 : bool SOCKET_CLASS_NAME::listen(uint16_t backlog) const
     712             : {
     713           8 :     if (fd == -1) {
     714           0 :         return false;
     715             :     }
     716           8 :     return CALL_PREFIX(listen)(fd, (int)backlog) == 0;
     717             : }
     718             : 
     719             : /*
     720             :   accept a new connection. Only valid for TCP connections after
     721             :   listen has been used. A new socket is returned
     722             : */
     723        3244 : SOCKET_CLASS_NAME *SOCKET_CLASS_NAME::accept(uint32_t timeout_ms)
     724             : {
     725        3244 :     if (fd == -1) {
     726           0 :         return nullptr;
     727             :     }
     728        3244 :     if (!pollin(timeout_ms)) {
     729        3235 :         return nullptr;
     730             :     }
     731             : 
     732           8 :     struct sockaddr_in &sin = *(struct sockaddr_in *)&last_in_addr[0];
     733           8 :     socklen_t len = sizeof(sin);
     734           8 :     int newfd = CALL_PREFIX(accept)(fd, (sockaddr *)&sin, &len);
     735           8 :     if (newfd == -1) {
     736           0 :         return nullptr;
     737             :     }
     738           8 :     auto *ret = NEW_NOTHROW SOCKET_CLASS_NAME(false, newfd);
     739           8 :     if (ret != nullptr) {
     740           8 :         ret->connected = true;
     741           8 :         ret->reuseaddress();
     742             :     }
     743           8 :     return ret;
     744             : }
     745             : 
     746             : /*
     747             :   return true if an address is in the multicast range
     748             :  */
     749           8 : bool SOCKET_CLASS_NAME::is_multicast_address(struct sockaddr_in &addr) const
     750             : {
     751           8 :     const uint32_t mc_lower = 0xE0000000; // 224.0.0.0
     752           8 :     const uint32_t mc_upper = 0xEFFFFFFF; // 239.255.255.255
     753           8 :     const uint32_t haddr = ntohl(addr.sin_addr.s_addr);
     754           8 :     return haddr >= mc_lower && haddr <= mc_upper;
     755             : }
     756             : 
     757        3729 : void SOCKET_CLASS_NAME::close(void)
     758             : {
     759        3729 :     if (fd != -1) {
     760             : #if defined(AP_SOCKET_NATIVE_ENABLED)
     761        3337 :         forget_unix_path(fd);
     762             : #endif
     763        3727 :         CALL_PREFIX(close)(fd);
     764        3727 :         fd = -1;
     765             :     }
     766        3729 :     if (fd_in != -1) {
     767           0 :         CALL_PREFIX(close)(fd_in);
     768           0 :         fd_in = -1;
     769             :     }
     770        3729 : }
     771             : 
     772             : /*
     773             :   duplicate a socket, giving a new object with the same contents,
     774             :   the fd in the old object is set to -1
     775             :  */
     776           0 : SOCKET_CLASS_NAME *SOCKET_CLASS_NAME::duplicate(void)
     777             : {
     778           0 :     auto *ret = NEW_NOTHROW SOCKET_CLASS_NAME(datagram, fd);
     779           0 :     if (ret == nullptr) {
     780           0 :         return nullptr;
     781             :     }
     782           0 :     ret->fd_in = fd_in;
     783           0 :     ret->connected = connected;
     784           0 :     fd = -1;
     785           0 :     fd_in = -1;
     786           0 :     return ret;
     787             : }
     788             : 
     789             : // access to inet_ntop, takes host order ipv4 as uint32_t
     790         401 : const char *SOCKET_CLASS_NAME::inet_addr_to_str(uint32_t addr, char *dst, uint16_t len)
     791             : {
     792         401 :     addr = htonl(addr);
     793         401 :     return CALL_PREFIX(inet_ntop)(AF_INET, (void*)&addr, dst, len);
     794             : }
     795             : 
     796             : // access to inet_pton, returns host order ipv4 as uint32_t
     797       10893 : uint32_t SOCKET_CLASS_NAME::inet_str_to_addr(const char *ipstr)
     798             : {
     799       10893 :     uint32_t ret = 0;
     800       10893 :     CALL_PREFIX(inet_pton)(AF_INET, ipstr, &ret);
     801       10893 :     return ntohl(ret);
     802             : 
     803             : }
     804             : 
     805             : #endif // AP_NETWORKING_SOCKETS_ENABLED || defined(AP_SOCKET_NATIVE_ENABLED)

Generated by: LCOV version 1.14