aboutsummaryrefslogtreecommitdiffstats
path: root/libaxl_send_handshake.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libaxl_send_handshake.c115
1 files changed, 115 insertions, 0 deletions
diff --git a/libaxl_send_handshake.c b/libaxl_send_handshake.c
new file mode 100644
index 0000000..80d48aa
--- /dev/null
+++ b/libaxl_send_handshake.c
@@ -0,0 +1,115 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+static int
+send_all(int fd, void *restrict data, size_t n, int flags, size_t *restrict progressp)
+{
+ char *bs = data;
+ ssize_t r;
+
+ while (n) {
+ r = liberror_send(fd, bs, n, flags, "<display server>");
+ if (r < 0)
+ return -1;
+ bs += r, n -= (size_t)r;
+ *progressp += (size_t)r;
+ }
+
+ return 0;
+}
+
+int
+libaxl_send_handshake(LIBAXL_CONTEXT *restrict ctx, const char *auth_name, size_t auth_name_len,
+ const char *auth_data, size_t auth_data_len, int flags)
+{
+ LIBAXL_CONNECTION *conn = ctx->conn;
+ LIBAXL_CONTEXT *pending, **pendingp;
+ size_t len, o = 0;
+ char *buf;
+
+ RLOCK_CONNECTION_SEND(conn);
+ pending = conn->pending_out;
+ conn->info.protocol_version_major = LIBAXL_PROTOCOL_MAJOR;
+ conn->info.protocol_version_minor = LIBAXL_PROTOCOL_MINOR;
+ conn->info.protocol_version = LIBAXL_PROTOCOL_VERSION;
+ conn->info.protocol_byte_order = LIBAXL_MSB_FIRST; /* TODO Use LIBAXL_LSB_FIRST if preferable */
+ RUNLOCK_CONNECTION_SEND(conn);
+ if (pending) {
+ liberror_save_backtrace(NULL);
+ liberror_set_error(pending == ctx
+ ? "Request from the previous call is pending to be flush"
+ : "A request from another thread is pending to be flushed",
+ "libaxl_send_handshake", "errno", EALREADY);
+ errno = EALREADY;
+ return LIBAXL_ERROR_SYSTEM;
+ }
+
+ if (auth_name_len > UINT16_MAX) {
+ liberror_save_backtrace(NULL);
+ liberror_set_error("Authorisation protocol name is too long", "libaxl_send_handshake", "errno", EINVAL);
+ errno = EINVAL;
+ return LIBAXL_ERROR_SYSTEM;
+ }
+
+ if (auth_data_len > UINT16_MAX) {
+ liberror_save_backtrace(NULL);
+ liberror_set_error("Authorisation data is too long", "libaxl_send_handshake", "errno", EINVAL);
+ errno = EINVAL;
+ return LIBAXL_ERROR_SYSTEM;
+ }
+
+ buf = ctx->out_buf;
+ len = 12 + (auth_name_len + 3) / 4 * 4 + (auth_data_len + 3) / 4 * 4;
+ if (len > ctx->out_buf_size) {
+ buf = liberror_realloc(buf, len);
+ if (!buf)
+ return LIBAXL_ERROR_SYSTEM;
+ ctx->out_buf = buf;
+ ctx->out_buf_size = len;
+ }
+
+ buf[o++] = 'B'; /* TODO Use 'l' (LSB first) if preferable */
+ buf[o++] = 0;
+ *(uint16_t *)&buf[o] = htons(LIBAXL_PROTOCOL_MAJOR);
+ o += 2;
+ *(uint16_t *)&buf[o] = htons(LIBAXL_PROTOCOL_MINOR);
+ o += 2;
+ *(uint16_t *)&buf[o] = htons((uint16_t)auth_name_len);
+ o += 2;
+ *(uint16_t *)&buf[o] = htons((uint16_t)auth_data_len);
+ o += 2;
+ buf[o++] = 0;
+ buf[o++] = 0;
+ memcpy(&buf[o], auth_name, auth_name_len);
+ for (o += auth_name_len; o % 4; o++)
+ buf[o] = 0;
+ memcpy(&buf[o], auth_data, auth_data_len);
+ for (o += auth_data_len; o % 4; o++)
+ buf[o] = 0;
+
+ ctx->out_length = o;
+ ctx->out_progress = 0;
+
+ WLOCK_CONNECTION_SEND(conn);
+
+ if (send_all(conn->fd, ctx->out_buf, ctx->out_length, flags, &ctx->out_progress)) {
+ ctx->next_pending_out = NULL;
+ pendingp = &conn->pending_out;
+ while (*pendingp)
+ pendingp = &(*pendingp)->next_pending_out;
+ *pendingp = ctx;
+ ctx->refcount += 1;
+
+ WUNLOCK_CONNECTION_SEND(conn);
+
+ liberror_save_backtrace(NULL);
+ liberror_set_error("Handshake has been buffered and is ready to be sent",
+ "libaxl_send_request", "errno", EINPROGRESS);
+ errno = EINPROGRESS;
+ return LIBAXL_ERROR_SYSTEM;
+ }
+
+ WUNLOCK_CONNECTION_SEND(conn);
+
+ return 0;
+}