aboutsummaryrefslogtreecommitdiffstats
path: root/libaxl_connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'libaxl_connect.c')
-rw-r--r--libaxl_connect.c263
1 files changed, 263 insertions, 0 deletions
diff --git a/libaxl_connect.c b/libaxl_connect.c
new file mode 100644
index 0000000..ee4edd8
--- /dev/null
+++ b/libaxl_connect.c
@@ -0,0 +1,263 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+enum {
+ FamilyInternet = 0,
+ FamilyDECnet = 1,
+ FamilyChaos = 2,
+ FamilyInternet6 = 6,
+ FamilyLocal = 256
+};
+
+static char *
+path_in_home(const char *filename)
+{
+ const char *home;
+ char *ret;
+
+ home = getenv("HOME");
+ if (!home || !*home) { /* TODO */
+ abort();
+ }
+
+ ret = liberror_malloc(strlen(home) + strlen(filename) + 2);
+ if (!ret)
+ return NULL;
+
+ stpcpy(stpcpy(stpcpy(ret, home), "/"), filename);
+
+ return ret;
+}
+
+static char *
+get_auth_file(int *freep)
+{
+ char *xauthfile = getenv("XAUTHORITY");
+ if (!xauthfile || !*xauthfile) {
+ xauthfile = path_in_home(".Xauthority");
+ *freep = 1;
+ } else {
+ *freep = 0;
+ }
+ return xauthfile;
+}
+
+static int
+next_auth(int authfd, char **authbufp, size_t *bufsizep, size_t *lenp, size_t *havep)
+{
+ ssize_t r;
+ size_t got = *havep, need = 4;
+ int stage;
+ void *new;
+
+ *lenp = 0;
+
+ for (stage = 0; stage < 4; stage++) {
+ while (got < need) {
+ if (need > *bufsizep) {
+ new = liberror_realloc(*authbufp, need);
+ if (!new)
+ return -1;
+ *authbufp = new;
+ *bufsizep = need;
+ }
+ r = read(authfd, &(*authbufp)[got], *bufsizep - got);
+ if (r < 0) {
+ liberror_save_backtrace(NULL);
+ liberror_set_error_errno(strerror(errno), "read", errno);
+ return -1;
+ } else if (!r) {
+ return 0;
+ }
+ got += (size_t)r;
+ }
+ need += (size_t)ntohs(*(uint16_t *)&(*authbufp)[need - 2]) + (stage < 3 ? 2 : 0);
+ }
+
+ *lenp = need;
+ if (*havep > need)
+ *havep -= need;
+ else
+ *havep = got - need;
+
+ return 0;
+}
+
+static int
+get_auth(const char *xauthfile, int sockfd, const char *host, const char *protocol, int display,
+ char **authnamep, size_t *authnamelenp, char **authdatap, size_t *authdatalenp, char **authbufp)
+{
+ int authfd, family, saved_errno;
+ char hostname[HOST_NAME_MAX + 1], number[2 + 3 * sizeof(int)];
+ struct sockaddr_storage sockaddr;
+ socklen_t sockaddrlen = (socklen_t)sizeof(sockaddr);
+ size_t bufsize = 128, len, have = 0, off, numberlen, hostnamelen;
+ uint16_t partlen;
+
+ (void) host;
+ (void) protocol;
+
+ *authnamep = *authdatap = *authbufp = NULL;
+ *authnamelenp = *authdatalenp = 0;
+
+ numberlen = (size_t)sprintf(number, "%i", display);
+
+ if (gethostname(hostname, HOST_NAME_MAX))
+ stpcpy(hostname, "localhost");
+ hostnamelen = strlen(hostname);
+
+ if (getpeername(sockfd, (void *)&sockaddr, &sockaddrlen)) {
+ liberror_save_backtrace(NULL);
+ liberror_set_error_errno(strerror(errno), "getsockname", errno);
+ return -1;
+ }
+
+ switch (sockaddr.ss_family) {
+ case AF_LOCAL:
+ family = FamilyLocal;
+ break;
+ case AF_INET:
+ family = FamilyInternet; /* TODO */
+ return 0;
+ case AF_INET6:
+ family = FamilyInternet6; /* TODO */
+ return 0;
+ default:
+ return 0;
+ }
+
+ *authbufp = liberror_malloc(bufsize);
+ if (!*authbufp)
+ return -1;
+
+ authfd = open(xauthfile, O_RDONLY);
+ if (authfd < 0 && errno != ENOENT) {
+ liberror_save_backtrace(NULL);
+ liberror_set_error_errno(strerror(errno), "open", errno);
+ return -1;
+ } else if (authfd < 0) {
+ return 0;
+ }
+
+ for (;; memmove(*authbufp, &(*authbufp)[len], have)) {
+ if (next_auth(authfd, authbufp, &bufsize, &len, &have)) {
+ liberror_save_backtrace(NULL);
+ liberror_set_error_errno(strerror(errno), "read", errno);
+ saved_errno = errno;
+ close(authfd);
+ errno = saved_errno;
+ return -1;
+ } else if (!len) {
+ break;
+ }
+
+ if (*(uint16_t *)&(*authbufp)[0] != htons(family))
+ continue;
+ if (*(uint16_t *)&(*authbufp)[2] != htons((uint16_t)hostnamelen))
+ continue;
+ if (memcmp(&(*authbufp)[4], hostname, hostnamelen))
+ continue;
+ off = 4 + (size_t)hostnamelen;
+ partlen = ntohs(*(uint16_t *)&(*authbufp)[off]);
+ off += 2;
+ if (partlen != numberlen)
+ continue;
+ if (memcmp(&(*authbufp)[off], number, numberlen))
+ continue;
+ off += numberlen;
+
+ *authnamelenp = (size_t)ntohs(*(uint16_t *)&(*authbufp)[off]);
+ off += 2;
+ *authnamep = &(*authbufp)[off];
+ off += *authnamelenp;
+ *authdatalenp = (size_t)ntohs(*(uint16_t *)&(*authbufp)[off]);
+ off += 2;
+ *authdatap = &(*authbufp)[off];
+
+ break;
+ }
+
+ close(authfd);
+ return 0;
+}
+
+LIBAXL_CONNECTION *
+libaxl_connect(const char *restrict display, char **restrict reasonp)
+{
+ struct liberror_state error_state;
+ LIBAXL_CONNECTION *conn = NULL;
+ LIBAXL_CONTEXT *ctx = NULL;
+ int xauthfile_free = 0, dispnum, screen, major, minor, r;
+ char *xauthfile = NULL, *host = NULL, *protocol = NULL;
+ char *authname = NULL, *authdata = NULL, *authbuf = NULL;
+ size_t authnamelen, authdatalen;
+ int saved_errno = errno;
+
+ if (reasonp)
+ *reasonp = NULL;
+
+ r = libaxl_parse_display(display, &host, &protocol, &dispnum, &screen);
+ if (r)
+ goto fail;
+
+ xauthfile = get_auth_file(&xauthfile_free);
+ if (!xauthfile)
+ goto fail;
+
+ conn = libaxl_connect_without_handshake(host, protocol, dispnum, screen);
+ if (!conn)
+ goto fail;
+
+ ctx = libaxl_context_create(conn);
+ if (!ctx)
+ goto fail;
+
+ if (get_auth(xauthfile, conn->fd, host, protocol, dispnum, &authname, &authnamelen, &authdata, &authdatalen, &authbuf))
+ goto fail;
+
+ r = libaxl_send_handshake(ctx, authname, authnamelen, authdata, authdatalen, MSG_NOSIGNAL);
+ if (r) {
+ if (r == LIBAXL_ERROR_SYSTEM && errno == EINPROGRESS) {
+ for (;;) {
+ r = libaxl_flush(conn, MSG_NOSIGNAL);
+ if (r != LIBAXL_ERROR_SYSTEM || errno != EINPROGRESS)
+ break;
+ liberror_pop_error();
+ }
+ }
+ if (r)
+ goto fail;
+ liberror_pop_error();
+ }
+
+ errno = saved_errno;
+
+ r = libaxl_receive_handshake(ctx, &major, &minor, reasonp, MSG_NOSIGNAL);
+ switch (r) {
+ case LIBAXL_HANDSHAKE_FAILED: /* TODO */
+ case LIBAXL_HANDSHAKE_AUTHENTICATE: /* TODO */
+ abort();
+ break;
+
+ case LIBAXL_HANDSHAKE_SUCCESS:
+ break;
+
+ default:
+ goto fail;
+ }
+
+ libaxl_context_free(ctx);
+ return conn;
+
+fail:
+ liberror_start(&error_state);
+ if (xauthfile_free)
+ free(xauthfile);
+ free(authbuf);
+ free(host);
+ free(protocol);
+ libaxl_context_free(ctx);
+ libaxl_close(conn);
+ liberror_end(&error_state);
+ return NULL;
+}