LCOV - code coverage report
Current view: top level - src - sock.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 6 156 3.8 %
Date: 2023-08-10 00:00:00 Functions: 3 18 16.7 %

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

Generated by: LCOV version 1.14