Home | History | Annotate | Line # | Download | only in src
      1 /* Copyright (C) 2001-2004 Bart Massey and Jamey Sharp.
      2  *
      3  * Permission is hereby granted, free of charge, to any person obtaining a
      4  * copy of this software and associated documentation files (the "Software"),
      5  * to deal in the Software without restriction, including without limitation
      6  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      7  * and/or sell copies of the Software, and to permit persons to whom the
      8  * Software is furnished to do so, subject to the following conditions:
      9  *
     10  * The above copyright notice and this permission notice shall be included in
     11  * all copies or substantial portions of the Software.
     12  *
     13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     16  * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
     17  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     18  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     19  *
     20  * Except as contained in this notice, the names of the authors or their
     21  * institutions shall not be used in advertising or otherwise to promote the
     22  * sale, use or other dealings in this Software without prior written
     23  * authorization from the authors.
     24  */
     25 
     26 /* Utility functions implementable using only public APIs. */
     27 
     28 #ifdef HAVE_CONFIG_H
     29 #include "config.h"
     30 #endif
     31 
     32 #include <assert.h>
     33 #include <sys/types.h>
     34 #include <limits.h>
     35 #include <errno.h>
     36 #include <stdio.h>
     37 #include <stdlib.h>
     38 #include <stddef.h>
     39 #include <string.h>
     40 
     41 #ifdef _WIN32
     42 #include "xcb_windefs.h"
     43 #else
     44 #include <unistd.h>
     45 #include <arpa/inet.h>
     46 #include <sys/socket.h>
     47 #include <sys/un.h>
     48 #include <netinet/in.h>
     49 #include <netinet/tcp.h>
     50 #include <fcntl.h>
     51 #include <netdb.h>
     52 #endif /* _WIN32 */
     53 
     54 #include "xcb.h"
     55 #include "xcbext.h"
     56 #include "xcbint.h"
     57 
     58 #if defined(HAVE_TSOL_LABEL_H) && defined(HAVE_IS_SYSTEM_LABELED)
     59 # include <tsol/label.h>
     60 # include <sys/stat.h>
     61 #endif
     62 
     63 #include <sys/stat.h>
     64 
     65 #ifndef __has_builtin
     66 # define  __has_builtin(x) 0
     67 #endif
     68 
     69 int xcb_popcount(uint32_t mask)
     70 {
     71 #if __has_builtin(__builtin_popcount)
     72     return __builtin_popcount(mask);
     73 #else
     74     /*
     75      * Count the number of bits set to 1 in a 32-bit word.
     76      * Algorithm from MIT AI Lab Memo 239: "HAKMEM", ITEM 169.
     77      * https://dspace.mit.edu/handle/1721.1/6086
     78      */
     79     uint32_t y;
     80     y = (mask >> 1) & 033333333333;
     81     y = mask - y - ((y >> 1) & 033333333333);
     82     return ((y + (y >> 3)) & 030707070707) % 077;
     83 #endif
     84 }
     85 
     86 int xcb_sumof(uint8_t *list, int len)
     87 {
     88   int i, s = 0;
     89   for(i=0; i<len; i++) {
     90     s += *list;
     91     list++;
     92   }
     93   return s;
     94 }
     95 
     96 /* Return true and parse if name matches <path to socket>[.<screen>]
     97  * Upon success:
     98  *     host = <path to socket>
     99  *     protocol = "unix"
    100  *     display = 0
    101  *     screen = <screen>
    102  */
    103 static int _xcb_parse_display_path_to_socket(const char *name, char **host, char **protocol,
    104                                              int *displayp, int *screenp)
    105 {
    106     struct stat sbuf;
    107     char path[PATH_MAX];
    108     size_t len;
    109     int _screen = 0, res;
    110 
    111     len = strlen(name);
    112     if (len >= sizeof(path))
    113         return 0;
    114     memcpy(path, name, len + 1);
    115     res = stat(path, &sbuf);
    116     if (0 != res) {
    117         unsigned long lscreen;
    118 	char *dot, *endptr;
    119         if (res != -1 || (errno != ENOENT && errno != ENOTDIR))
    120             return 0;
    121         dot = strrchr(path, '.');
    122         if (!dot || dot[1] < '1' || dot[1] > '9')
    123             return 0;
    124         *dot = '\0';
    125         errno = 0;
    126         lscreen = strtoul(dot + 1, &endptr, 10);
    127         if (lscreen > INT_MAX || !endptr || *endptr || errno)
    128             return 0;
    129         if (0 != stat(path, &sbuf))
    130             return 0;
    131         _screen = (int)lscreen;
    132     }
    133 
    134     if (host) {
    135         *host = strdup(path);
    136         if (!*host)
    137             return 0;
    138     }
    139 
    140     if (protocol) {
    141         *protocol = strdup("unix");
    142         if (!*protocol) {
    143             if (host)
    144                 free(*host);
    145             return 0;
    146         }
    147     }
    148 
    149     if (displayp)
    150         *displayp = 0;
    151 
    152     if (screenp)
    153         *screenp = _screen;
    154 
    155     return 1;
    156 }
    157 
    158 static int _xcb_parse_display(const char *name, char **host, char **protocol,
    159                       int *displayp, int *screenp)
    160 {
    161     int len, display, screen;
    162     char *slash, *colon, *dot, *end;
    163 
    164     if(!name || !*name)
    165         name = getenv("DISPLAY");
    166     if(!name)
    167         return 0;
    168 
    169     /* First check for <path to socket>[.<screen>] */
    170     if (name[0] == '/')
    171         return _xcb_parse_display_path_to_socket(name, host, protocol, displayp, screenp);
    172 
    173     if (strncmp(name, "unix:", 5) == 0)
    174         return _xcb_parse_display_path_to_socket(name + 5, host, protocol, displayp, screenp);
    175 
    176     slash = strrchr(name, '/');
    177 
    178     if (slash) {
    179         len = slash - name;
    180         if (protocol) {
    181             *protocol = malloc(len + 1);
    182             if(!*protocol)
    183                 return 0;
    184             memcpy(*protocol, name, len);
    185             (*protocol)[len] = '\0';
    186         }
    187         name = slash + 1;
    188     } else
    189         if (protocol)
    190             *protocol = NULL;
    191 
    192     colon = strrchr(name, ':');
    193     if(!colon)
    194         goto error_out;
    195     len = colon - name;
    196     ++colon;
    197     display = strtoul(colon, &dot, 10);
    198     if(dot == colon)
    199         goto error_out;
    200     if(*dot == '\0')
    201         screen = 0;
    202     else
    203     {
    204         if(*dot != '.')
    205             goto error_out;
    206         ++dot;
    207         screen = strtoul(dot, &end, 10);
    208         if(end == dot || *end != '\0')
    209             goto error_out;
    210     }
    211     /* At this point, the display string is fully parsed and valid, but
    212      * the caller's memory is untouched. */
    213 
    214     *host = malloc(len + 1);
    215     if(!*host)
    216         goto error_out;
    217     memcpy(*host, name, len);
    218     (*host)[len] = '\0';
    219     *displayp = display;
    220     if(screenp)
    221         *screenp = screen;
    222     return 1;
    223 
    224 error_out:
    225     if (protocol) {
    226         free(*protocol);
    227         *protocol = NULL;
    228     }
    229 
    230     return 0;
    231 }
    232 
    233 int xcb_parse_display(const char *name, char **host, int *displayp,
    234                              int *screenp)
    235 {
    236     return _xcb_parse_display(name, host, NULL, displayp, screenp);
    237 }
    238 
    239 static int _xcb_open_tcp(const char *host, char *protocol, const unsigned short port);
    240 #ifndef _WIN32
    241 static int _xcb_open_unix(char *protocol, const char *file);
    242 #endif /* !WIN32 */
    243 #ifdef HAVE_ABSTRACT_SOCKETS
    244 static int _xcb_open_abstract(char *protocol, const char *file, size_t filelen);
    245 #endif
    246 
    247 static int _xcb_open(const char *host, char *protocol, const int display)
    248 {
    249     int fd;
    250 #ifdef __hpux
    251     static const char unix_base[] = "/usr/spool/sockets/X11/";
    252 #else
    253     static const char unix_base[] = "/tmp/.X11-unix/X";
    254 #endif
    255     const char *base = unix_base;
    256     size_t filelen;
    257     char *file = NULL;
    258     int actual_filelen;
    259 
    260 #ifndef _WIN32
    261     if (protocol && strcmp("unix", protocol) == 0 && host && host[0] == '/') {
    262         /* Full path to socket provided, ignore everything else */
    263         filelen = strlen(host) + 1;
    264         if (filelen > INT_MAX)
    265             return -1;
    266         file = malloc(filelen);
    267         if (file == NULL)
    268             return -1;
    269         memcpy(file, host, filelen);
    270         actual_filelen = (int)(filelen - 1);
    271     } else {
    272 #endif
    273         /* If protocol or host is "unix", fall through to Unix socket code below */
    274         if ((!protocol || (strcmp("unix",protocol) != 0)) &&
    275             (*host != '\0') && (strcmp("unix",host) != 0))
    276         {
    277             /* display specifies TCP */
    278             unsigned short port = X_TCP_PORT + display;
    279             return _xcb_open_tcp(host, protocol, port);
    280         }
    281 
    282 #ifndef _WIN32
    283 #if defined(HAVE_TSOL_LABEL_H) && defined(HAVE_IS_SYSTEM_LABELED)
    284         /* Check special path for Unix sockets under Solaris Trusted Extensions */
    285         if (is_system_labeled())
    286         {
    287             const char *tsol_base = "/var/tsol/doors/.X11-unix/X";
    288             char tsol_socket[PATH_MAX];
    289             struct stat sbuf;
    290 
    291             snprintf(tsol_socket, sizeof(tsol_socket), "%s%d", tsol_base, display);
    292 
    293             if (stat(tsol_socket, &sbuf) == 0)
    294                 base = tsol_base;
    295             else if (errno != ENOENT)
    296                 return 0;
    297         }
    298 #endif
    299 
    300         filelen = strlen(base) + 1 + sizeof(display) * 3 + 1;
    301         file = malloc(filelen);
    302         if(file == NULL)
    303             return -1;
    304 
    305         /* display specifies Unix socket */
    306         actual_filelen = snprintf(file, filelen, "%s%d", base, display);
    307 
    308         if(actual_filelen < 0)
    309         {
    310             free(file);
    311             return -1;
    312         }
    313         /* snprintf may truncate the file */
    314         filelen = MIN(actual_filelen, filelen - 1);
    315 #ifdef HAVE_ABSTRACT_SOCKETS
    316         fd = _xcb_open_abstract(protocol, file, filelen);
    317         if (fd >= 0 || (errno != ENOENT && errno != ECONNREFUSED))
    318         {
    319             free(file);
    320             return fd;
    321         }
    322 #endif
    323     }
    324     fd = _xcb_open_unix(protocol, file);
    325     free(file);
    326 
    327     if (fd < 0 && !protocol && *host == '\0') {
    328             unsigned short port = X_TCP_PORT + display;
    329             fd = _xcb_open_tcp(host, protocol, port);
    330     }
    331 
    332     return fd;
    333 #endif /* !_WIN32 */
    334     return -1; /* if control reaches here then something has gone wrong */
    335 }
    336 
    337 static int _xcb_socket(int family, int type, int proto)
    338 {
    339     int fd;
    340 
    341 #ifdef SOCK_CLOEXEC
    342     fd = socket(family, type | SOCK_CLOEXEC, proto);
    343     if (fd == -1 && errno == EINVAL)
    344 #endif
    345     {
    346         fd = socket(family, type, proto);
    347 #ifndef _WIN32
    348         if (fd >= 0)
    349             fcntl(fd, F_SETFD, FD_CLOEXEC);
    350 #endif
    351     }
    352     return fd;
    353 }
    354 
    355 
    356 static int _xcb_do_connect(int fd, const struct sockaddr* addr, int addrlen) {
    357     int on = 1;
    358 
    359     if(fd < 0)
    360         return -1;
    361 
    362     setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
    363     setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
    364 
    365     return connect(fd, addr, addrlen);
    366 }
    367 
    368 static int _xcb_open_tcp(const char *host, char *protocol, const unsigned short port)
    369 {
    370     int fd = -1;
    371 #if HAVE_GETADDRINFO
    372     struct addrinfo hints;
    373     char service[6]; /* "65535" with the trailing '\0' */
    374     struct addrinfo *results, *addr;
    375     char *bracket;
    376 #endif
    377 
    378     if (protocol && strcmp("tcp",protocol) && strcmp("inet",protocol)
    379 #ifdef AF_INET6
    380                  && strcmp("inet6",protocol)
    381 #endif
    382         )
    383         return -1;
    384 
    385     if (*host == '\0')
    386         host = "localhost";
    387 
    388 #if HAVE_GETADDRINFO
    389     memset(&hints, 0, sizeof(hints));
    390 #ifdef AI_NUMERICSERV
    391     hints.ai_flags |= AI_NUMERICSERV;
    392 #endif
    393     hints.ai_family = AF_UNSPEC;
    394     hints.ai_socktype = SOCK_STREAM;
    395 
    396 #ifdef AF_INET6
    397     /* Allow IPv6 addresses enclosed in brackets. */
    398     if(host[0] == '[' && (bracket = strrchr(host, ']')) && bracket[1] == '\0')
    399     {
    400         *bracket = '\0';
    401         ++host;
    402         hints.ai_flags |= AI_NUMERICHOST;
    403         hints.ai_family = AF_INET6;
    404     }
    405 #endif
    406 
    407     snprintf(service, sizeof(service), "%hu", port);
    408     if(getaddrinfo(host, service, &hints, &results))
    409         /* FIXME: use gai_strerror, and fill in error connection */
    410         return -1;
    411 
    412     for(addr = results; addr; addr = addr->ai_next)
    413     {
    414         fd = _xcb_socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
    415         if (_xcb_do_connect(fd, addr->ai_addr, addr->ai_addrlen) >= 0)
    416             break;
    417 #ifdef _WIN32
    418         closesocket(fd);
    419 #else
    420         close(fd);
    421 #endif
    422         fd = -1;
    423     }
    424     freeaddrinfo(results);
    425     return fd;
    426 #else
    427     {
    428         struct hostent* _h;
    429         struct sockaddr_in _s;
    430         struct in_addr ** _c;
    431 
    432         if((_h = gethostbyname(host)) == NULL)
    433             return -1;
    434 
    435         _c = (struct in_addr**)_h->h_addr_list;
    436         fd = -1;
    437 
    438         while(*_c) {
    439             _s.sin_family = AF_INET;
    440             _s.sin_port = htons(port);
    441             _s.sin_addr = *(*_c);
    442 
    443             fd = _xcb_socket(_s.sin_family, SOCK_STREAM, 0);
    444             if(_xcb_do_connect(fd, (struct sockaddr*)&_s, sizeof(_s)) >= 0)
    445                 break;
    446 
    447 #ifdef _WIN32
    448             closesocket(fd);
    449 #else
    450             close(fd);
    451 #endif
    452             fd = -1;
    453             ++_c;
    454         }
    455 
    456         return fd;
    457     }
    458 #endif
    459 }
    460 
    461 #ifndef _WIN32
    462 static int _xcb_open_unix(char *protocol, const char *file)
    463 {
    464     int fd;
    465     struct sockaddr_un addr;
    466     socklen_t len = sizeof(int);
    467     int val;
    468 
    469     if (protocol && strcmp("unix",protocol))
    470         return -1;
    471 
    472     strcpy(addr.sun_path, file);
    473     addr.sun_family = AF_UNIX;
    474 #ifdef HAVE_SOCKADDR_SUN_LEN
    475     addr.sun_len = SUN_LEN(&addr);
    476 #endif
    477     fd = _xcb_socket(AF_UNIX, SOCK_STREAM, 0);
    478     if(fd == -1)
    479         return -1;
    480     if(getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &val, &len) == 0 && val < 64 * 1024)
    481     {
    482         val = 64 * 1024;
    483         setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &val, sizeof(int));
    484     }
    485     if(connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
    486         close(fd);
    487         return -1;
    488     }
    489     return fd;
    490 }
    491 #endif /* !_WIN32 */
    492 
    493 #ifdef HAVE_ABSTRACT_SOCKETS
    494 static int _xcb_open_abstract(char *protocol, const char *file, size_t filelen)
    495 {
    496     int fd;
    497     struct sockaddr_un addr = {0};
    498     socklen_t namelen;
    499 
    500     if (protocol && strcmp("unix",protocol))
    501         return -1;
    502 
    503     strcpy(addr.sun_path + 1, file);
    504     addr.sun_family = AF_UNIX;
    505     namelen = offsetof(struct sockaddr_un, sun_path) + 1 + filelen;
    506 #ifdef HAVE_SOCKADDR_SUN_LEN
    507     addr.sun_len = 1 + filelen;
    508 #endif
    509     fd = _xcb_socket(AF_UNIX, SOCK_STREAM, 0);
    510     if (fd == -1)
    511         return -1;
    512     if (connect(fd, (struct sockaddr *) &addr, namelen) == -1) {
    513         close(fd);
    514         return -1;
    515     }
    516     return fd;
    517 }
    518 #endif
    519 
    520 xcb_connection_t *xcb_connect(const char *displayname, int *screenp)
    521 {
    522     return xcb_connect_to_display_with_auth_info(displayname, NULL, screenp);
    523 }
    524 
    525 xcb_connection_t *xcb_connect_to_display_with_auth_info(const char *displayname, xcb_auth_info_t *auth, int *screenp)
    526 {
    527     int fd, display = 0;
    528     char *host = NULL;
    529     char *protocol = NULL;
    530     xcb_auth_info_t ourauth;
    531     xcb_connection_t *c;
    532 
    533     int parsed = _xcb_parse_display(displayname, &host, &protocol, &display, screenp);
    534 
    535     if(!parsed) {
    536         c = _xcb_conn_ret_error(XCB_CONN_CLOSED_PARSE_ERR);
    537         goto out;
    538     }
    539 
    540 #ifdef _WIN32
    541     WSADATA wsaData;
    542     if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
    543         c = _xcb_conn_ret_error(XCB_CONN_ERROR);
    544         goto out;
    545     }
    546 #endif
    547 
    548     fd = _xcb_open(host, protocol, display);
    549 
    550     if(fd == -1) {
    551         c = _xcb_conn_ret_error(XCB_CONN_ERROR);
    552 #ifdef _WIN32
    553         WSACleanup();
    554 #endif
    555         goto out;
    556     }
    557 
    558     if(auth) {
    559         c = xcb_connect_to_fd(fd, auth);
    560     }
    561     else if(_xcb_get_auth_info(fd, &ourauth, display))
    562     {
    563         c = xcb_connect_to_fd(fd, &ourauth);
    564         free(ourauth.name);
    565         free(ourauth.data);
    566     }
    567     else
    568         c = xcb_connect_to_fd(fd, 0);
    569 
    570     if(c->has_error)
    571         goto out;
    572 
    573     /* Make sure requested screen number is in bounds for this server */
    574     if((screenp != NULL) && (*screenp >= (int) c->setup->roots_len)) {
    575         xcb_disconnect(c);
    576         c = _xcb_conn_ret_error(XCB_CONN_CLOSED_INVALID_SCREEN);
    577         goto out;
    578     }
    579 
    580 out:
    581     free(host);
    582     free(protocol);
    583     return c;
    584 }
    585