LCOV - code coverage report
Current view: top level - src - sock.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 6 158 3.8 %
Date: 2024-02-03 11:54:46 Functions: 3 18 16.7 %

          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           0 :     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 1.14