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 69int 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 86int 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 */ 103static 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 158static 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 224error_out: 225 if (protocol) { 226 free(*protocol); 227 *protocol = NULL; 228 } 229 230 return 0; 231} 232 233int 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 239static int _xcb_open_tcp(const char *host, char *protocol, const unsigned short port); 240#ifndef _WIN32 241static int _xcb_open_unix(char *protocol, const char *file); 242#endif /* !WIN32 */ 243#ifdef HAVE_ABSTRACT_SOCKETS 244static int _xcb_open_abstract(char *protocol, const char *file, size_t filelen); 245#endif 246 247static 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 337static 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 356static 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 368static 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 462static 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 494static 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 520xcb_connection_t *xcb_connect(const char *displayname, int *screenp) 521{ 522 return xcb_connect_to_display_with_auth_info(displayname, NULL, screenp); 523} 524 525xcb_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 580out: 581 free(host); 582 free(protocol); 583 return c; 584} 585