LCOV - code coverage report
Current view: top level - src - sock.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 3.8 % 158 6
Test Date: 2024-07-18 00:00:00 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;
     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 :             rc = 0;
     232            0 :             if (xsock->conn->sockopt_cb) {
     233              :                 /* Don't allow user to overwrite sockfd value. */
     234            0 :                 sock_t sock_copy = sock;
     235            0 :                 rc = xsock->conn->sockopt_cb(xsock->conn, &sock_copy);
     236            0 :                 if (rc != 0) {
     237            0 :                     strophe_debug(xsock->ctx, "sock",
     238              :                                   "User's setsockopt callback"
     239              :                                   "failed with %d (errno=%d)",
     240            0 :                                   rc, errno);
     241              :                 }
     242              :             }
     243            0 :             if (rc == 0)
     244            0 :                 rc = sock_set_nonblocking(sock);
     245            0 :             if (rc == 0)
     246            0 :                 rc = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
     247              :             /* Assume only connect() can cause "in progress" error. */
     248            0 :             if (rc != 0 && !_in_progress(sock_error(NULL))) {
     249            0 :                 sock_close(sock);
     250              :                 sock = INVALID_SOCKET;
     251              :             }
     252              :         }
     253            0 :         strophe_debug(xsock->ctx, "sock", "sock_connect() result %d", sock);
     254              : 
     255            0 :         xsock->ainfo_cur = xsock->ainfo_cur->ai_next;
     256            0 :     } while (sock == INVALID_SOCKET);
     257              : 
     258              :     return sock;
     259              : }
     260              : 
     261            0 : int sock_set_keepalive(sock_t sock,
     262              :                        int timeout,
     263              :                        int interval,
     264              :                        int count,
     265              :                        unsigned int user_timeout)
     266              : {
     267            0 :     int ret;
     268            0 :     int optval = (timeout && interval) ? 1 : 0;
     269              : 
     270            0 :     UNUSED(count);
     271            0 :     UNUSED(user_timeout);
     272              : 
     273              : #ifdef _WIN32
     274              :     struct tcp_keepalive ka;
     275              :     DWORD dw = 0;
     276              : 
     277              :     ka.onoff = optval;
     278              :     ka.keepalivetime = timeout * 1000;
     279              :     ka.keepaliveinterval = interval * 1000;
     280              :     ret = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), NULL, 0, &dw,
     281              :                    NULL, NULL);
     282              : #else
     283            0 :     ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
     284            0 :     if (ret < 0)
     285              :         return ret;
     286              : 
     287            0 :     if (optval) {
     288              : #ifdef TCP_KEEPIDLE
     289            0 :         ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &timeout,
     290              :                          sizeof(timeout));
     291              : #elif defined(TCP_KEEPALIVE)
     292              :         /* QNX receives `struct timeval' as argument, but it seems OSX does int
     293              :          */
     294              :         ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, &timeout,
     295              :                          sizeof(timeout));
     296              : #endif /* TCP_KEEPIDLE */
     297            0 :         if (ret < 0)
     298              :             return ret;
     299              : #ifdef TCP_KEEPINTVL
     300            0 :         ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &interval,
     301              :                          sizeof(interval));
     302            0 :         if (ret < 0)
     303              :             return ret;
     304              : #endif /* TCP_KEEPINTVL */
     305              :     }
     306              : 
     307            0 :     if (count) {
     308              : #ifdef TCP_KEEPCNT
     309            0 :         ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));
     310            0 :         if (ret < 0)
     311              :             return ret;
     312              : #endif /* TCP_KEEPCNT */
     313              :     }
     314              : 
     315            0 :     if (user_timeout) {
     316              : #ifdef TCP_USER_TIMEOUT
     317            0 :         ret = setsockopt(sock, IPPROTO_TCP, TCP_USER_TIMEOUT, &user_timeout,
     318              :                          sizeof(user_timeout));
     319            0 :         if (ret < 0)
     320              :             return ret;
     321              : #elif defined(TCP_RXT_CONNDROPTIME)
     322              :         int rxt = user_timeout / 1000;
     323              :         ret = setsockopt(sock, IPPROTO_TCP, TCP_RXT_CONNDROPTIME, &rxt,
     324              :                          sizeof(rxt));
     325              :         if (ret < 0)
     326              :             return ret;
     327              : #endif /* TCP_USER_TIMEOUT */
     328              :     }
     329              : 
     330              : #endif /* _WIN32 */
     331              : 
     332              :     return ret;
     333              : }
     334              : 
     335              : /** Example sockopt callback function
     336              :  *  An example function that can be used to set reasonable default keepalive
     337              :  *  options on sockets when registered for a connection with
     338              :  *  xmpp_conn_set_sockopt_callback()
     339              :  *
     340              :  *  @param conn a Strophe connection object
     341              :  *  @param socket pointer to a socket descriptor
     342              :  *
     343              :  *  @see xmpp_sockopt_callback for details on the `socket` parameter
     344              :  *  @ingroup Connections
     345              :  */
     346            0 : int xmpp_sockopt_cb_keepalive(xmpp_conn_t *conn, void *socket)
     347              : {
     348            0 :     sock_t sock = *((sock_t *)socket);
     349              : 
     350            0 :     return sock_set_keepalive(
     351              :         sock, conn->ka_timeout, conn->ka_interval, conn->ka_count,
     352              :         conn->ka_count
     353            0 :             ? (conn->ka_timeout + conn->ka_interval * conn->ka_count) * 1000
     354              :             : 0);
     355              : }
     356              : 
     357            0 : int sock_close(sock_t sock)
     358              : {
     359              : #ifdef _WIN32
     360              :     return closesocket(sock);
     361              : #else
     362            0 :     return close(sock);
     363              : #endif
     364              : }
     365              : 
     366            0 : static int _sock_set_blocking_mode(sock_t sock, int blocking)
     367              : {
     368              : #ifdef _WIN32
     369              :     u_long nonblock = blocking ? 0 : 1;
     370              :     return ioctlsocket(sock, FIONBIO, &nonblock);
     371              : #else
     372            0 :     int rc;
     373              : 
     374            0 :     rc = fcntl(sock, F_GETFL, NULL);
     375            0 :     if (rc >= 0) {
     376            0 :         rc = blocking ? rc & (~O_NONBLOCK) : rc | O_NONBLOCK;
     377            0 :         rc = fcntl(sock, F_SETFL, rc);
     378              :     }
     379            0 :     return rc;
     380              : #endif
     381              : }
     382              : 
     383            0 : int sock_set_blocking(sock_t sock)
     384              : {
     385            0 :     return _sock_set_blocking_mode(sock, 1);
     386              : }
     387              : 
     388            0 : int sock_set_nonblocking(sock_t sock)
     389              : {
     390            0 :     return _sock_set_blocking_mode(sock, 0);
     391              : }
     392              : 
     393            0 : int sock_read(struct conn_interface *intf, void *buff, size_t len)
     394              : {
     395            0 :     return recv(intf->conn->sock, buff, len, 0);
     396              : }
     397              : 
     398            0 : int sock_write(struct conn_interface *intf, const void *buff, size_t len)
     399              : {
     400            0 :     return send(intf->conn->sock, buff, len, 0);
     401              : }
     402              : 
     403            0 : int sock_is_recoverable(struct conn_interface *intf, int error)
     404              : {
     405            0 :     UNUSED(intf);
     406              : #ifdef _WIN32
     407              :     return (error == WSAEINTR || error == WSAEWOULDBLOCK ||
     408              :             error == WSAEINPROGRESS);
     409              : #else
     410            0 :     return (error == EAGAIN || error == EINTR);
     411              : #endif
     412              : }
     413              : 
     414            0 : int sock_connect_error(sock_t sock)
     415              : {
     416            0 :     struct sockaddr_storage ss;
     417            0 :     struct sockaddr *sa = (struct sockaddr *)&ss;
     418            0 :     socklen_t len;
     419            0 :     char temp;
     420              : 
     421            0 :     memset(&ss, 0, sizeof(ss));
     422            0 :     len = sizeof(ss);
     423            0 :     sa->sa_family = AF_UNSPEC;
     424              : 
     425              :     /* we don't actually care about the peer name, we're just checking if
     426              :      * we're connected or not */
     427            0 :     if (getpeername(sock, sa, &len) == 0) {
     428            0 :         return 0;
     429              :     }
     430              : 
     431              :     /* it's possible that the error wasn't ENOTCONN, so if it wasn't,
     432              :      * return that */
     433              : #ifdef _WIN32
     434              :     if (sock_error(NULL) != WSAENOTCONN)
     435              :         return sock_error(NULL);
     436              : #else
     437            0 :     if (sock_error(NULL) != ENOTCONN)
     438            0 :         return sock_error(NULL);
     439              : #endif
     440              : 
     441              :     /* load the correct error into errno through error slippage */
     442            0 :     recv(sock, &temp, 1, 0);
     443              : 
     444            0 :     return sock_error(NULL);
     445              : }
        

Generated by: LCOV version 2.0-1