LCOV - code coverage report
Current view: top level - src - sock.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 3.8 % 157 6
Test Date: 2024-07-22 12:36:40 Functions: 16.7 % 18 3

            Line data    Source code
       1              : /* SPDX-License-Identifier: MIT OR GPL-3.0-only */
       2              : /* sock.c
       3              : ** strophe XMPP client library -- socket abstraction implementation
       4              : **
       5              : ** Copyright (C) 2005-2009 Collecta, Inc.
       6              : **
       7              : **  This software is provided AS-IS with no warranty, either express
       8              : **  or implied.
       9              : **
      10              : ** This program is dual licensed under the MIT or GPLv3 licenses.
      11              : */
      12              : 
      13              : /** @file
      14              :  *  Socket abstraction.
      15              :  */
      16              : 
      17              : #include <stdio.h>
      18              : #include <stdlib.h>
      19              : #include <string.h>
      20              : #include <sys/types.h>
      21              : 
      22              : #ifdef _WIN32
      23              : #include <winsock2.h>
      24              : #include <ws2tcpip.h>
      25              : #include <iphlpapi.h>
      26              : #include <mstcpip.h> /* tcp_keepalive */
      27              : #else
      28              : #include <arpa/inet.h>
      29              : #include <errno.h>
      30              : #include <unistd.h>
      31              : #include <sys/socket.h>
      32              : #include <netinet/in.h>
      33              : #include <netinet/tcp.h>
      34              : #include <netdb.h>
      35              : #include <fcntl.h>
      36              : #endif
      37              : 
      38              : #include "common.h"
      39              : #include "resolver.h"
      40              : 
      41              : const struct conn_interface sock_intf = {
      42              :     sock_read,
      43              :     sock_write,
      44              :     /* no flush */
      45              :     conn_int_nop,
      46              :     /* no pending */
      47              :     conn_int_nop,
      48              :     sock_error,
      49              :     sock_is_recoverable,
      50              :     NULL,
      51              : };
      52              : 
      53              : struct _xmpp_sock_t {
      54              :     xmpp_ctx_t *ctx;
      55              :     xmpp_conn_t *conn;
      56              :     struct addrinfo *ainfo_list;
      57              :     struct addrinfo *ainfo_cur;
      58              :     resolver_srv_rr_t *srv_rr_list;
      59              :     resolver_srv_rr_t *srv_rr_cur;
      60              :     const char *host;
      61              :     unsigned short port;
      62              : };
      63              : 
      64            2 : void sock_initialize(void)
      65              : {
      66              : #ifdef _WIN32
      67              :     WSADATA wsad;
      68              :     WSAStartup(0x0101, &wsad);
      69              : #endif
      70            2 : }
      71              : 
      72            2 : void sock_shutdown(void)
      73              : {
      74              : #ifdef _WIN32
      75              :     WSACleanup();
      76              : #endif
      77            2 : }
      78              : 
      79            0 : int sock_error(struct conn_interface *intf)
      80              : {
      81            0 :     UNUSED(intf);
      82              : #ifdef _WIN32
      83              :     return WSAGetLastError();
      84              : #else
      85            0 :     return errno;
      86              : #endif
      87              : }
      88              : 
      89              : static int _in_progress(int error)
      90              : {
      91              : #ifdef _WIN32
      92              :     return (error == WSAEWOULDBLOCK || error == WSAEINPROGRESS);
      93              : #else
      94              :     return (error == EINPROGRESS);
      95              : #endif
      96              : }
      97              : 
      98            0 : static void sock_getaddrinfo(xmpp_sock_t *xsock)
      99              : {
     100            0 :     char service[6];
     101            0 :     struct addrinfo hints;
     102            0 :     int rc;
     103              : 
     104            0 :     if (xsock->ainfo_list) {
     105            0 :         freeaddrinfo(xsock->ainfo_list);
     106            0 :         xsock->ainfo_list = NULL;
     107              :     }
     108              : 
     109            0 :     if (xsock->srv_rr_cur) {
     110              :         /* Cache host and port for debug logs. */
     111            0 :         xsock->host = xsock->srv_rr_cur->target;
     112            0 :         xsock->port = xsock->srv_rr_cur->port;
     113              : 
     114            0 :         strophe_snprintf(service, 6, "%u", xsock->srv_rr_cur->port);
     115            0 :         memset(&hints, 0, sizeof(struct addrinfo));
     116            0 :         hints.ai_family = AF_UNSPEC;
     117              : #ifdef AI_ADDRCONFIG
     118            0 :         hints.ai_flags = AI_ADDRCONFIG;
     119              : #endif /* AI_ADDRCONFIG */
     120            0 :         hints.ai_protocol = IPPROTO_TCP;
     121            0 :         hints.ai_socktype = SOCK_STREAM;
     122              : 
     123            0 :         rc = getaddrinfo(xsock->srv_rr_cur->target, service, &hints,
     124              :                          &xsock->ainfo_list);
     125            0 :         if (rc != 0) {
     126            0 :             strophe_debug(xsock->ctx, "sock", "getaddrinfo() failed with %d",
     127              :                           rc);
     128            0 :             xsock->ainfo_list = NULL;
     129              :         }
     130              :     }
     131              : 
     132            0 :     xsock->ainfo_cur = xsock->ainfo_list;
     133            0 : }
     134              : 
     135            0 : xmpp_sock_t *sock_new(xmpp_conn_t *conn,
     136              :                       const char *domain,
     137              :                       const char *host,
     138              :                       unsigned short port)
     139              : {
     140            0 :     xmpp_ctx_t *ctx = conn->ctx;
     141            0 :     xmpp_sock_t *xsock;
     142            0 :     int found = XMPP_DOMAIN_NOT_FOUND;
     143              : 
     144            0 :     xsock = strophe_alloc(ctx, sizeof(*xsock));
     145            0 :     if (!xsock) {
     146              :         return NULL;
     147              :     }
     148              : 
     149            0 :     xsock->ctx = ctx;
     150            0 :     xsock->conn = conn;
     151            0 :     xsock->host = NULL;
     152            0 :     xsock->port = 0;
     153              : 
     154            0 :     if (!host) {
     155            0 :         found = resolver_srv_lookup(ctx, "xmpp-client", "tcp", domain,
     156              :                                     &xsock->srv_rr_list);
     157            0 :         if (XMPP_DOMAIN_NOT_FOUND == found)
     158            0 :             strophe_debug(ctx, "sock",
     159              :                           "SRV lookup failed, connecting via domain.");
     160              :     }
     161            0 :     if (XMPP_DOMAIN_NOT_FOUND == found) {
     162              :         /* Resolution failed or the host is provided explicitly. */
     163            0 :         xsock->srv_rr_list =
     164            0 :             resolver_srv_rr_new(ctx, host ? host : domain, port, 0, 0);
     165              :     }
     166            0 :     xsock->srv_rr_cur = xsock->srv_rr_list;
     167              : 
     168            0 :     xsock->ainfo_list = NULL;
     169            0 :     sock_getaddrinfo(xsock);
     170            0 :     if (xsock->srv_rr_cur)
     171            0 :         xsock->srv_rr_cur = xsock->srv_rr_cur->next;
     172              : 
     173              :     return xsock;
     174              : }
     175              : 
     176            8 : void sock_free(xmpp_sock_t *xsock)
     177              : {
     178            8 :     if (!xsock)
     179              :         return;
     180              : 
     181            0 :     if (xsock->ainfo_list)
     182            0 :         freeaddrinfo(xsock->ainfo_list);
     183            0 :     if (xsock->srv_rr_list)
     184            0 :         resolver_srv_free(xsock->ctx, xsock->srv_rr_list);
     185            0 :     strophe_free(xsock->ctx, xsock);
     186              : }
     187              : 
     188            0 : static const char *_sockaddr2str(struct sockaddr *sa, char *buf, size_t buflen)
     189              : {
     190            0 :     buf[0] = '\0';
     191              : 
     192            0 :     switch (sa->sa_family) {
     193            0 :     case AF_INET:
     194            0 :         inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr, buf, buflen);
     195            0 :         break;
     196            0 :     case AF_INET6:
     197            0 :         inet_ntop(AF_INET6, &((struct sockaddr_in6 *)sa)->sin6_addr, buf,
     198              :                   buflen);
     199            0 :         break;
     200              :     default:
     201            0 :         strophe_snprintf(buf, buflen, "<Unknown>");
     202              :     }
     203            0 :     return buf;
     204              : }
     205              : 
     206            0 : sock_t sock_connect(xmpp_sock_t *xsock)
     207              : {
     208            0 :     struct addrinfo *ainfo;
     209            0 :     sock_t sock;
     210            0 :     int rc = 0;
     211            0 :     char buf[64];
     212              : 
     213            0 :     do {
     214            0 :         if (!xsock->ainfo_cur) {
     215            0 :             sock_getaddrinfo(xsock);
     216            0 :             if (xsock->srv_rr_cur)
     217            0 :                 xsock->srv_rr_cur = xsock->srv_rr_cur->next;
     218              :         }
     219            0 :         if (!xsock->ainfo_cur) {
     220              :             /* We tried all available addresses. */
     221            0 :             return INVALID_SOCKET;
     222              :         }
     223              : 
     224            0 :         ainfo = xsock->ainfo_cur;
     225            0 :         strophe_debug(xsock->ctx, "sock", "Connecting to %s:%u via %s",
     226            0 :                       xsock->host, xsock->port,
     227              :                       _sockaddr2str(ainfo->ai_addr, buf, sizeof(buf)));
     228              : 
     229            0 :         sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
     230            0 :         if (sock != INVALID_SOCKET) {
     231            0 :             if (xsock->conn->sockopt_cb) {
     232              :                 /* Don't allow user to overwrite sockfd value. */
     233            0 :                 sock_t sock_copy = sock;
     234            0 :                 rc = xsock->conn->sockopt_cb(xsock->conn, &sock_copy);
     235            0 :                 if (rc != 0) {
     236            0 :                     strophe_debug(xsock->ctx, "sock",
     237              :                                   "User's setsockopt callback"
     238              :                                   "failed with %d (errno=%d)",
     239            0 :                                   rc, errno);
     240              :                 }
     241              :             }
     242            0 :             if (rc == 0)
     243            0 :                 rc = sock_set_nonblocking(sock);
     244            0 :             if (rc == 0)
     245            0 :                 rc = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
     246              :             /* Assume only connect() can cause "in progress" error. */
     247            0 :             if (rc != 0 && !_in_progress(sock_error(NULL))) {
     248            0 :                 sock_close(sock);
     249              :                 sock = INVALID_SOCKET;
     250              :             }
     251              :         }
     252            0 :         strophe_debug(xsock->ctx, "sock", "sock_connect() result %d", sock);
     253              : 
     254            0 :         xsock->ainfo_cur = xsock->ainfo_cur->ai_next;
     255            0 :     } while (sock == INVALID_SOCKET);
     256              : 
     257              :     return sock;
     258              : }
     259              : 
     260            0 : int sock_set_keepalive(sock_t sock,
     261              :                        int timeout,
     262              :                        int interval,
     263              :                        int count,
     264              :                        unsigned int user_timeout)
     265              : {
     266            0 :     int ret;
     267            0 :     int optval = (timeout && interval) ? 1 : 0;
     268              : 
     269            0 :     UNUSED(count);
     270            0 :     UNUSED(user_timeout);
     271              : 
     272              : #ifdef _WIN32
     273              :     struct tcp_keepalive ka;
     274              :     DWORD dw = 0;
     275              : 
     276              :     ka.onoff = optval;
     277              :     ka.keepalivetime = timeout * 1000;
     278              :     ka.keepaliveinterval = interval * 1000;
     279              :     ret = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), NULL, 0, &dw,
     280              :                    NULL, NULL);
     281              : #else
     282            0 :     ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
     283            0 :     if (ret < 0)
     284              :         return ret;
     285              : 
     286            0 :     if (optval) {
     287              : #ifdef TCP_KEEPIDLE
     288            0 :         ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &timeout,
     289              :                          sizeof(timeout));
     290              : #elif defined(TCP_KEEPALIVE)
     291              :         /* QNX receives `struct timeval' as argument, but it seems OSX does int
     292              :          */
     293              :         ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, &timeout,
     294              :                          sizeof(timeout));
     295              : #endif /* TCP_KEEPIDLE */
     296            0 :         if (ret < 0)
     297              :             return ret;
     298              : #ifdef TCP_KEEPINTVL
     299            0 :         ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &interval,
     300              :                          sizeof(interval));
     301            0 :         if (ret < 0)
     302              :             return ret;
     303              : #endif /* TCP_KEEPINTVL */
     304              :     }
     305              : 
     306            0 :     if (count) {
     307              : #ifdef TCP_KEEPCNT
     308            0 :         ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));
     309            0 :         if (ret < 0)
     310              :             return ret;
     311              : #endif /* TCP_KEEPCNT */
     312              :     }
     313              : 
     314            0 :     if (user_timeout) {
     315              : #ifdef TCP_USER_TIMEOUT
     316            0 :         ret = setsockopt(sock, IPPROTO_TCP, TCP_USER_TIMEOUT, &user_timeout,
     317              :                          sizeof(user_timeout));
     318            0 :         if (ret < 0)
     319              :             return ret;
     320              : #elif defined(TCP_RXT_CONNDROPTIME)
     321              :         int rxt = user_timeout / 1000;
     322              :         ret = setsockopt(sock, IPPROTO_TCP, TCP_RXT_CONNDROPTIME, &rxt,
     323              :                          sizeof(rxt));
     324              :         if (ret < 0)
     325              :             return ret;
     326              : #endif /* TCP_USER_TIMEOUT */
     327              :     }
     328              : 
     329              : #endif /* _WIN32 */
     330              : 
     331              :     return ret;
     332              : }
     333              : 
     334              : /** Example sockopt callback function
     335              :  *  An example function that can be used to set reasonable default keepalive
     336              :  *  options on sockets when registered for a connection with
     337              :  *  xmpp_conn_set_sockopt_callback()
     338              :  *
     339              :  *  @param conn a Strophe connection object
     340              :  *  @param socket pointer to a socket descriptor
     341              :  *
     342              :  *  @see xmpp_sockopt_callback for details on the `socket` parameter
     343              :  *  @ingroup Connections
     344              :  */
     345            0 : int xmpp_sockopt_cb_keepalive(xmpp_conn_t *conn, void *socket)
     346              : {
     347            0 :     sock_t sock = *((sock_t *)socket);
     348              : 
     349            0 :     return sock_set_keepalive(
     350              :         sock, conn->ka_timeout, conn->ka_interval, conn->ka_count,
     351              :         conn->ka_count
     352            0 :             ? (conn->ka_timeout + conn->ka_interval * conn->ka_count) * 1000
     353              :             : 0);
     354              : }
     355              : 
     356            0 : int sock_close(sock_t sock)
     357              : {
     358              : #ifdef _WIN32
     359              :     return closesocket(sock);
     360              : #else
     361            0 :     return close(sock);
     362              : #endif
     363              : }
     364              : 
     365            0 : static int _sock_set_blocking_mode(sock_t sock, int blocking)
     366              : {
     367              : #ifdef _WIN32
     368              :     u_long nonblock = blocking ? 0 : 1;
     369              :     return ioctlsocket(sock, FIONBIO, &nonblock);
     370              : #else
     371            0 :     int rc;
     372              : 
     373            0 :     rc = fcntl(sock, F_GETFL, NULL);
     374            0 :     if (rc >= 0) {
     375            0 :         rc = blocking ? rc & (~O_NONBLOCK) : rc | O_NONBLOCK;
     376            0 :         rc = fcntl(sock, F_SETFL, rc);
     377              :     }
     378            0 :     return rc;
     379              : #endif
     380              : }
     381              : 
     382            0 : int sock_set_blocking(sock_t sock)
     383              : {
     384            0 :     return _sock_set_blocking_mode(sock, 1);
     385              : }
     386              : 
     387            0 : int sock_set_nonblocking(sock_t sock)
     388              : {
     389            0 :     return _sock_set_blocking_mode(sock, 0);
     390              : }
     391              : 
     392            0 : int sock_read(struct conn_interface *intf, void *buff, size_t len)
     393              : {
     394            0 :     return recv(intf->conn->sock, buff, len, 0);
     395              : }
     396              : 
     397            0 : int sock_write(struct conn_interface *intf, const void *buff, size_t len)
     398              : {
     399            0 :     return send(intf->conn->sock, buff, len, 0);
     400              : }
     401              : 
     402            0 : int sock_is_recoverable(struct conn_interface *intf, int error)
     403              : {
     404            0 :     UNUSED(intf);
     405              : #ifdef _WIN32
     406              :     return (error == WSAEINTR || error == WSAEWOULDBLOCK ||
     407              :             error == WSAEINPROGRESS);
     408              : #else
     409            0 :     return (error == EAGAIN || error == EINTR);
     410              : #endif
     411              : }
     412              : 
     413            0 : int sock_connect_error(sock_t sock)
     414              : {
     415            0 :     struct sockaddr_storage ss;
     416            0 :     struct sockaddr *sa = (struct sockaddr *)&ss;
     417            0 :     socklen_t len;
     418            0 :     char temp;
     419              : 
     420            0 :     memset(&ss, 0, sizeof(ss));
     421            0 :     len = sizeof(ss);
     422            0 :     sa->sa_family = AF_UNSPEC;
     423              : 
     424              :     /* we don't actually care about the peer name, we're just checking if
     425              :      * we're connected or not */
     426            0 :     if (getpeername(sock, sa, &len) == 0) {
     427            0 :         return 0;
     428              :     }
     429              : 
     430              :     /* it's possible that the error wasn't ENOTCONN, so if it wasn't,
     431              :      * return that */
     432              : #ifdef _WIN32
     433              :     if (sock_error(NULL) != WSAENOTCONN)
     434              :         return sock_error(NULL);
     435              : #else
     436            0 :     if (sock_error(NULL) != ENOTCONN)
     437            0 :         return sock_error(NULL);
     438              : #endif
     439              : 
     440              :     /* load the correct error into errno through error slippage */
     441            0 :     recv(sock, &temp, 1, 0);
     442              : 
     443            0 :     return sock_error(NULL);
     444              : }
        

Generated by: LCOV version 2.0-1