aboutsummaryrefslogtreecommitdiffstats
path: root/libaxl_connect_without_handshake.c
blob: ca4fbedf8fded10a6f240d075f22a9162c273f3c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/* See LICENSE file for copyright and license details. */
#include "common.h"

static int
connect_tcp_ip(const char *host, int display)
{
	uint16_t port = libaxl_get_tcp_port(display);
	int fd;

	abort(); /* XXX */

	setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &(int){1}, sizeof(int));
	setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &(int){1}, sizeof(int));

	return -1;
}

static int
connect_unix(const char *path)
{
	struct sockaddr_un addr;
	int fd;

	fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
	if (fd < 0) {
		liberror_save_backtrace(NULL);
		liberror_set_error_errno(strerror(errno), "socket", errno);
		return -1;
	}

	if (strlen(path) >= sizeof(addr.sun_path)) {
		liberror_save_backtrace(NULL);
		/* We could fix this with an O_PATH fd to the file,
		 * but we will not as other libraries probably do
		 * not do that, and there is something very wrong
		 * with your setup if the name is too long for
		 * `struct sockaddr_un`. */
		close(fd);
		errno = ENAMETOOLONG;
		liberror_set_error_errno("Path to X display socket is too long", "libaxl_connect_without_handshake", ENAMETOOLONG);
		return -1;
	}

	addr.sun_family = AF_LOCAL;
	stpcpy(addr.sun_path, path);
	if (connect(fd, (void *)&addr, (socklen_t)sizeof(addr))) {
		liberror_save_backtrace(NULL);
		liberror_set_error_errno(strerror(errno), "connect", errno);
		return -1;
	}

	return fd;
}

static int
connect_unix_abstract(const char *path, size_t len)
{
	struct sockaddr_un addr;
	int fd;

	fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
	if (fd < 0) {
		liberror_save_backtrace(NULL);
		liberror_set_error_errno(strerror(errno), "socket", errno);
		return -1;
	}

	addr.sun_family = AF_LOCAL;
	memcpy(addr.sun_path, path, len);
	if (connect(fd, (void *)&addr, (socklen_t)len)) {
		liberror_save_backtrace(NULL);
		liberror_set_error_errno(strerror(errno), "connect", errno);
		return -1;
	}

	return fd;
}

LIBAXL_CONNECTION *
libaxl_connect_without_handshake(const char *host, const char *protocol, int display, int screen)
{
	LIBAXL_CONNECTION *conn;
	char path[sizeof("@/tmp/.X11-unix/X-") + 3 * sizeof(int)];
	int fd, len;

	if ((!protocol || !*protocol) && (!host || !*host)) {
		len = sprintf(path, "%c/tmp/.X11-unix/X%i", 0, display);
		fd = connect_unix(&path[1]);
		if (fd < 0) {
			fd = connect_unix_abstract(path, (size_t)len);
			if (fd >= 0) {
				liberror_pop_error();
			} else {
				fd = connect_tcp_ip("localhost", display);
				if (fd >= 0) {
					liberror_pop_error();
					liberror_pop_error();
				}
			}
		}

	} else if (!protocol || !*protocol ||
	           !strcasecmp(protocol, "tcp") || !strcasecmp(protocol, "inet") ||
	           !strcasecmp(protocol, "tcp6") || !strcasecmp(protocol, "inet6")) {
		fd = connect_tcp_ip(host, display);

	} else if (!strcmp(protocol, "unix")) {
		fd = connect_unix(host);

	} else {
		liberror_save_backtrace(NULL);
		liberror_set_error("Display server uses unsupported underlaying protocol",
		                   "libaxl_connect_without_handshake", "libaxl", LIBAXL_ERROR_PROTOCOL_NOT_SUPPORTED);
		return NULL;
	}

	if (fd < 0)
		return NULL;

	conn = libaxl_create(fd);
	if (conn)
		conn->info.default_screen_number = screen < 0 ? 0 : screen;

	return conn;
}