summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/plugins/relay/CMakeLists.txt2
-rw-r--r--src/plugins/relay/api/relay-api-msg.c2
-rw-r--r--src/plugins/relay/api/remote/relay-remote-network.c1089
-rw-r--r--src/plugins/relay/api/remote/relay-remote-network.h26
-rw-r--r--src/plugins/relay/irc/relay-irc.c2
-rw-r--r--src/plugins/relay/relay-client.c105
-rw-r--r--src/plugins/relay/relay-client.h15
-rw-r--r--src/plugins/relay/relay-command.c24
-rw-r--r--src/plugins/relay/relay-http.c14
-rw-r--r--src/plugins/relay/relay-raw.c244
-rw-r--r--src/plugins/relay/relay-raw.h11
-rw-r--r--src/plugins/relay/relay-remote.c225
-rw-r--r--src/plugins/relay/relay-remote.h12
-rw-r--r--src/plugins/relay/relay-websocket.c155
-rw-r--r--src/plugins/relay/relay-websocket.h18
-rw-r--r--src/plugins/relay/relay.c2
-rw-r--r--src/plugins/relay/relay.h13
-rw-r--r--src/plugins/relay/weechat/relay-weechat-msg.c6
18 files changed, 1688 insertions, 277 deletions
diff --git a/src/plugins/relay/CMakeLists.txt b/src/plugins/relay/CMakeLists.txt
index 724191947..280b155ec 100644
--- a/src/plugins/relay/CMakeLists.txt
+++ b/src/plugins/relay/CMakeLists.txt
@@ -49,6 +49,8 @@ if(ENABLE_CJSON)
api/relay-api.c api/relay-api.h
api/relay-api-msg.c api/relay-api-msg.h
api/relay-api-protocol.c api/relay-api-protocol.h
+ # API relay remote
+ api/remote/relay-remote-network.c api/remote/relay-remote-network.h
)
endif()
diff --git a/src/plugins/relay/api/relay-api-msg.c b/src/plugins/relay/api/relay-api-msg.c
index 1fdf3afe6..cb87d7045 100644
--- a/src/plugins/relay/api/relay-api-msg.c
+++ b/src/plugins/relay/api/relay-api-msg.c
@@ -172,7 +172,7 @@ relay_api_msg_send_json_internal (struct t_relay_client *client,
string = cJSON_PrintUnformatted (json);
num_bytes = relay_client_send (
client,
- RELAY_CLIENT_MSG_STANDARD,
+ RELAY_MSG_STANDARD,
string,
(string) ? strlen (string) : 0,
NULL); /* raw_message */
diff --git a/src/plugins/relay/api/remote/relay-remote-network.c b/src/plugins/relay/api/remote/relay-remote-network.c
new file mode 100644
index 000000000..f5a11c1f1
--- /dev/null
+++ b/src/plugins/relay/api/remote/relay-remote-network.c
@@ -0,0 +1,1089 @@
+/*
+ * relay-remote-network.c - network functions for relay remote
+ *
+ * Copyright (C) 2024 Sébastien Helleu <flashcode@flashtux.org>
+ *
+ * This file is part of WeeChat, the extensible chat client.
+ *
+ * WeeChat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * WeeChat is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with WeeChat. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <gcrypt.h>
+#include <gnutls/gnutls.h>
+#include <cjson/cJSON.h>
+
+#include "../../../weechat-plugin.h"
+#include "../../relay.h"
+#include "../../relay-auth.h"
+#include "../../relay-http.h"
+#include "../../relay-raw.h"
+#include "../../relay-remote.h"
+#include "../../relay-websocket.h"
+#include "../relay-api.h"
+
+
+/*
+ * Gets URL to an API resource.
+ *
+ * For example if remote URL is "https://localhost:9000" and the resource is
+ * "handshake", it returns: "https://localhost:9000/api/handshake".
+ *
+ * Note: result must be free after use.
+ */
+
+char *
+relay_remote_network_get_url_resource (struct t_relay_remote *remote,
+ const char *resource)
+{
+ const char *ptr_url;
+ char url[4096];
+
+ if (!remote || !resource || !resource[0])
+ return NULL;
+
+ ptr_url = weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL]);
+ if (!ptr_url || !ptr_url[0])
+ return NULL;
+
+ snprintf (url, sizeof (url),
+ "%s%sapi/%s",
+ ptr_url,
+ (ptr_url[strlen (ptr_url) - 1] == '/') ? "" : "/",
+ resource);
+
+ return strdup (url);
+}
+
+/*
+ * Close connection with remote.
+ */
+
+void
+relay_remote_network_close_connection (struct t_relay_remote *remote)
+{
+ if (!remote)
+ return;
+ if (remote->hook_fd)
+ {
+ weechat_unhook (remote->hook_fd);
+ remote->hook_fd = NULL;
+ }
+ if (remote->hook_connect)
+ {
+ weechat_unhook (remote->hook_connect);
+ remote->hook_connect = NULL;
+ }
+ if (remote->sock != -1)
+ {
+#ifdef _WIN32
+ closesocket (remote->sock);
+#else
+ close (remote->sock);
+#endif /* _WIN32 */
+ remote->sock = -1;
+ }
+ relay_websocket_deflate_free (remote->ws_deflate);
+ remote->ws_deflate = NULL;
+}
+
+/*
+ * Disconnects from remote.
+ */
+
+void
+relay_remote_network_disconnect (struct t_relay_remote *remote)
+{
+ if (!remote)
+ return;
+
+ relay_remote_network_close_connection (remote);
+ relay_remote_set_status (remote, RELAY_STATUS_DISCONNECTED);
+ weechat_printf (NULL, "remote[%s]: disconnected", remote->name);
+}
+
+/*
+ * Checks if authentication via websocket handshake was successful.
+ *
+ * Returns:
+ * 1: authentication successful
+ * 0: authentication has failed
+ */
+
+int
+relay_remote_network_check_auth (struct t_relay_remote *remote,
+ const char *buffer)
+{
+ struct t_relay_http_response *http_resp;
+ cJSON *json_body, *json_error;
+ const char *msg_error, *msg_resp_error, *ptr_ws_accept;
+ char *key, hash[160 / 8], sec_websocket_accept[128];
+ int length, accept_ok, hash_size;
+
+ http_resp = NULL;
+ msg_error = NULL;
+ msg_resp_error = NULL;
+ accept_ok = 0;
+
+ http_resp = relay_http_parse_response (buffer);
+ if (!http_resp)
+ {
+ msg_error = _("invalid response from remote");
+ goto error;
+ }
+
+ if (http_resp->body)
+ {
+ json_body = cJSON_Parse (http_resp->body);
+ if (json_body)
+ {
+ json_error = cJSON_GetObjectItem (json_body, "error");
+ if (json_error && cJSON_IsString (json_error))
+ msg_resp_error = cJSON_GetStringValue (json_error);
+ }
+ }
+
+ if ((http_resp->return_code != 101)
+ || (weechat_strcasecmp (http_resp->message, "Switching Protocols") != 0))
+ {
+ if (http_resp->return_code == 401)
+ msg_error = _("authentication failed with remote");
+ else
+ msg_error = _("invalid response from remote");
+ goto error;
+ }
+
+ if (remote->websocket_key)
+ {
+ ptr_ws_accept = weechat_hashtable_get (http_resp->headers,
+ "sec-websocket-accept");
+ if (ptr_ws_accept)
+ {
+ length = strlen (remote->websocket_key) + strlen (WEBSOCKET_GUID) + 1;
+ key = malloc (length);
+ if (key)
+ {
+ snprintf (key, length,
+ "%s%s", remote->websocket_key, WEBSOCKET_GUID);
+ if (weechat_crypto_hash (key, strlen (key), "sha1",
+ hash, &hash_size))
+ {
+ if (weechat_string_base_encode ("64", hash, hash_size,
+ sec_websocket_accept) > 0)
+ {
+ if (strcmp (ptr_ws_accept, sec_websocket_accept) == 0)
+ accept_ok = 1;
+ }
+ }
+ free (key);
+ }
+ }
+ }
+
+ relay_websocket_parse_extensions (
+ weechat_hashtable_get (http_resp->headers, "sec-websocket-extensions"),
+ remote->ws_deflate);
+
+ if (!accept_ok)
+ {
+ msg_error = _("invalid websocket response (handshake error)");
+ goto error;
+ }
+
+ relay_http_response_free (http_resp);
+
+ return 1;
+
+error:
+ weechat_printf (
+ NULL,
+ _("%sremote[%s]: error: %s%s%s%s"),
+ weechat_prefix ("error"),
+ remote->name,
+ msg_error,
+ (msg_resp_error) ? " (" : "",
+ (msg_resp_error) ? msg_resp_error : "",
+ (msg_resp_error) ? ")" : "");
+ if (http_resp)
+ relay_http_response_free (http_resp);
+ return 0;
+}
+
+/*
+ * Sends data to the remote.
+ *
+ * Returns the number of bytes sent to the remote.
+ */
+
+int
+relay_remote_network_send_data (struct t_relay_remote *remote,
+ const char *data, int data_size)
+{
+ if (remote->tls)
+ {
+ return (remote->sock >= 0) ?
+ gnutls_record_send (remote->gnutls_sess, data, data_size) :
+ data_size;
+ }
+ else
+ {
+ return (remote->sock >= 0) ?
+ send (remote->sock, data, data_size, 0) :
+ data_size;
+ }
+
+}
+
+/*
+ * Sends data to the remote.
+ * If the remote is connected, encapsulate data in a websocket frame.
+ *
+ * Returns the number of bytes sent to the remote.
+ */
+
+int
+relay_remote_network_send (struct t_relay_remote *remote,
+ enum t_relay_msg_type msg_type,
+ const char *data, int data_size)
+{
+ const char *ptr_data;
+ char *websocket_frame;
+ unsigned long long length_frame;
+ int opcode, flags, num_sent;
+
+ ptr_data = data;
+ websocket_frame = NULL;
+
+ if (remote->status == RELAY_STATUS_CONNECTED)
+ {
+ /* encapsulate data in a websocket frame */
+ switch (msg_type)
+ {
+ case RELAY_MSG_PING:
+ opcode = WEBSOCKET_FRAME_OPCODE_PING;
+ break;
+ case RELAY_MSG_PONG:
+ opcode = WEBSOCKET_FRAME_OPCODE_PONG;
+ break;
+ case RELAY_MSG_CLOSE:
+ opcode = WEBSOCKET_FRAME_OPCODE_CLOSE;
+ break;
+ default:
+ opcode = WEBSOCKET_FRAME_OPCODE_TEXT;
+ break;
+ }
+ websocket_frame = relay_websocket_encode_frame (
+ remote->ws_deflate, opcode, 1, data, data_size, &length_frame);
+ if (websocket_frame)
+ {
+ ptr_data = websocket_frame;
+ data_size = length_frame;
+ }
+ }
+
+ num_sent = relay_remote_network_send_data (remote, ptr_data, data_size);
+
+ if (websocket_frame)
+ free (websocket_frame);
+
+ if (num_sent >= 0)
+ {
+ flags = RELAY_RAW_FLAG_SEND;
+ if ((msg_type == RELAY_MSG_PING)
+ || (msg_type == RELAY_MSG_PONG)
+ || (msg_type == RELAY_MSG_CLOSE))
+ {
+ flags |= RELAY_RAW_FLAG_BINARY;
+ }
+ relay_raw_print_remote (remote, msg_type, flags, data, data_size);
+ }
+
+ return num_sent;
+}
+
+/*
+ * Reads text buffer from a remote.
+ */
+
+void
+relay_remote_network_recv_text (struct t_relay_remote *remote,
+ const char *buffer, int buffer_size)
+{
+ char request[1024];
+
+ relay_raw_print_remote (remote, RELAY_MSG_STANDARD,
+ RELAY_RAW_FLAG_RECV,
+ buffer, buffer_size);
+
+ if (remote->status == RELAY_STATUS_AUTHENTICATING)
+ {
+ if (!relay_remote_network_check_auth (remote, buffer))
+ {
+ relay_remote_network_disconnect (remote);
+ return;
+ }
+ relay_remote_set_status (remote, RELAY_STATUS_CONNECTED);
+ snprintf (request, sizeof (request),
+ "{\"request\": \"GET /api/buffers\"}");
+ relay_remote_network_send (remote, RELAY_MSG_STANDARD,
+ request, strlen (request));
+ }
+}
+
+/*
+ * Reads websocket frames.
+ */
+
+void
+relay_remote_network_read_websocket_frames (struct t_relay_remote *remote,
+ struct t_relay_websocket_frame *frames,
+ int num_frames)
+{
+ int i;
+
+ if (!frames || (num_frames <= 0))
+ return;
+
+ for (i = 0; i < num_frames; i++)
+ {
+ if (frames[i].payload_size == 0)
+ {
+ /*
+ * When decoded length is 0, assume remote sent a PONG frame.
+ *
+ * RFC 6455 Section 5.5.3:
+ *
+ * "A Pong frame MAY be sent unsolicited. This serves as a
+ * unidirectional heartbeat. A response to an unsolicited
+ * Pong frame is not expected."
+ */
+ continue;
+ }
+ switch (frames[i].opcode)
+ {
+ case RELAY_MSG_PING:
+ /* print message in raw buffer */
+ relay_raw_print_remote (remote, RELAY_MSG_PING,
+ RELAY_RAW_FLAG_RECV | RELAY_RAW_FLAG_BINARY,
+ frames[i].payload,
+ frames[i].payload_size);
+ /* answer with a PONG */
+ relay_remote_network_send (remote,
+ RELAY_MSG_PONG,
+ frames[i].payload,
+ frames[i].payload_size);
+ break;
+ case RELAY_MSG_CLOSE:
+ /* print message in raw buffer */
+ relay_raw_print_remote (remote, RELAY_MSG_CLOSE,
+ RELAY_RAW_FLAG_RECV | RELAY_RAW_FLAG_BINARY,
+ frames[i].payload,
+ frames[i].payload_size);
+ /* answer with a CLOSE */
+ relay_remote_network_send (remote,
+ RELAY_MSG_CLOSE,
+ frames[i].payload,
+ frames[i].payload_size);
+ /* close the connection */
+ relay_remote_network_disconnect (remote);
+ /* ignore any other message after the close */
+ return;
+ default:
+ if (frames[i].payload)
+ {
+ relay_remote_network_recv_text (remote,
+ frames[i].payload,
+ frames[i].payload_size);
+ }
+ break;
+ }
+ }
+}
+
+/*
+ * Reads a buffer of bytes from a remote.
+ */
+
+void
+relay_remote_network_recv_buffer (struct t_relay_remote *remote,
+ const char *buffer, int buffer_size)
+{
+ struct t_relay_websocket_frame *frames;
+ char *partial_ws_frame;
+ int rc, i, num_frames, partial_ws_frame_size;
+
+ /* if authenticating is in progress, check if it was successful */
+ if (remote->status == RELAY_STATUS_AUTHENTICATING)
+ {
+ relay_remote_network_recv_text (remote, buffer, buffer_size);
+ }
+ else if (remote->status == RELAY_STATUS_CONNECTED)
+ {
+ partial_ws_frame = NULL;
+ partial_ws_frame_size = 0;
+ rc = relay_websocket_decode_frame (
+ (const unsigned char *)buffer,
+ buffer_size,
+ 0, /* expect_masked_frame */
+ remote->ws_deflate,
+ &frames,
+ &num_frames,
+ &partial_ws_frame,
+ &partial_ws_frame_size);
+ if (!rc)
+ {
+ /* fatal error when decoding frame: close connection */
+ for (i = 0; i < num_frames; i++)
+ {
+ if (frames[i].payload)
+ free (frames[i].payload);
+ }
+ free (frames);
+ weechat_printf (
+ NULL,
+ _("%sremote[%s]: error decoding websocket frame"),
+ weechat_prefix ("error"),
+ remote->name);
+ relay_remote_network_disconnect (remote);
+ return;
+ }
+ relay_remote_network_read_websocket_frames (remote, frames, num_frames);
+ }
+}
+
+/*
+ * Callback for fd hook.
+ */
+
+int
+relay_remote_network_recv_cb (const void *pointer, void *data, int fd)
+{
+ struct t_relay_remote *remote;
+ static char buffer[4096 + 2];
+ int num_read, end_recv;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) fd;
+
+ remote = (struct t_relay_remote *)pointer;
+ if (!remote)
+ return WEECHAT_RC_ERROR;
+
+ end_recv = 0;
+ while (!end_recv)
+ {
+ end_recv = 1;
+
+ if (remote->tls)
+ {
+ if (!remote->gnutls_sess)
+ return WEECHAT_RC_ERROR;
+ num_read = gnutls_record_recv (remote->gnutls_sess, buffer,
+ sizeof (buffer) - 2);
+ }
+ else
+ {
+ num_read = recv (remote->sock, buffer, sizeof (buffer) - 2, 0);
+ }
+
+ if (num_read > 0)
+ {
+ buffer[num_read] = '\0';
+ if (remote->tls
+ && (gnutls_record_check_pending (remote->gnutls_sess) > 0))
+ {
+ /*
+ * if there are unread data in the gnutls buffers,
+ * go on with recv
+ */
+ end_recv = 0;
+ }
+ relay_remote_network_recv_buffer (remote, buffer, num_read);
+ }
+ else
+ {
+ if (remote->tls)
+ {
+ if ((num_read == 0)
+ || ((num_read != GNUTLS_E_AGAIN)
+ && (num_read != GNUTLS_E_INTERRUPTED)))
+ {
+ weechat_printf (
+ NULL,
+ _("%sremote[%s]: reading data on socket: error %d %s"),
+ weechat_prefix ("error"),
+ remote->name,
+ num_read,
+ (num_read == 0) ? _("(connection closed by peer)") :
+ gnutls_strerror (num_read));
+ relay_remote_network_disconnect (remote);
+ }
+ }
+ else
+ {
+ if ((num_read == 0)
+ || ((errno != EAGAIN) && (errno != EWOULDBLOCK)))
+ {
+ weechat_printf (
+ NULL,
+ _("%sremote[%s]: reading data on socket: error %d %s"),
+ weechat_prefix ("error"),
+ remote->name,
+ errno,
+ (num_read == 0) ? _("(connection closed by peer)") :
+ strerror (errno));
+ relay_remote_network_disconnect (remote);
+ }
+ }
+ }
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Connects to remote using websocket, with authentication.
+ */
+
+void
+relay_remote_network_connect_ws_auth (struct t_relay_remote *remote)
+{
+ char *password, *totp_secret, *totp;
+ char *salt_password, salt[64], str_auth[4096], str_auth_base64[4096];
+ char str_http[8192], str_totp[128];
+ char hash[512 / 8], hash_hexa[((512 / 8) * 2) + 1];
+ char ws_key[16], ws_key_base64[64];
+ int length, hash_size;
+
+ relay_remote_set_status (remote, RELAY_STATUS_AUTHENTICATING);
+
+ password = NULL;
+ totp_secret = NULL;
+ str_auth[0] = '\0';
+ str_totp[0] = '\0';
+
+ password = weechat_string_eval_expression (
+ weechat_config_string (remote->options[RELAY_REMOTE_OPTION_PASSWORD]),
+ NULL, NULL, NULL);
+ if (!password)
+ goto end;
+ totp_secret = weechat_string_eval_expression (
+ weechat_config_string (remote->options[RELAY_REMOTE_OPTION_TOTP_SECRET]),
+ NULL, NULL, NULL);
+ if (!totp_secret)
+ goto end;
+
+ switch (remote->password_hash_algo)
+ {
+ case RELAY_AUTH_PASSWORD_HASH_PLAIN:
+ snprintf (str_auth, sizeof (str_auth), "plain:%s", password);
+ break;
+ case RELAY_AUTH_PASSWORD_HASH_SHA256:
+ case RELAY_AUTH_PASSWORD_HASH_SHA512:
+ length = strlen (password) + 64;
+ salt_password = malloc (length);
+ if (salt_password)
+ {
+ snprintf (salt_password, length,
+ "%ld%s",
+ time (NULL),
+ password);
+ if (weechat_crypto_hash (
+ salt_password, strlen (salt_password),
+ relay_auth_password_hash_algo_name[remote->password_hash_algo],
+ hash, &hash_size))
+ {
+ weechat_string_base_encode ("16", hash, hash_size, hash_hexa);
+ snprintf (str_auth, sizeof (str_auth),
+ "hash:%s", hash_hexa);
+ }
+ free (salt_password);
+ }
+ break;
+ case RELAY_AUTH_PASSWORD_HASH_PBKDF2_SHA256:
+ case RELAY_AUTH_PASSWORD_HASH_PBKDF2_SHA512:
+ snprintf (salt, sizeof (salt), "%ld", time (NULL));
+ if (weechat_crypto_hash_pbkdf2 (
+ password,
+ strlen (password),
+ relay_auth_password_hash_algo_name[remote->password_hash_algo] + 7,
+ salt, strlen (salt),
+ remote->password_hash_iterations,
+ hash, &hash_size))
+ {
+ weechat_string_base_encode ("16", hash, hash_size, hash_hexa);
+ snprintf (str_auth, sizeof (str_auth),
+ "hash:%s:%s:%d:%s",
+ relay_auth_password_hash_algo_name[remote->password_hash_algo],
+ salt,
+ remote->password_hash_iterations,
+ hash_hexa);
+ }
+ break;
+ }
+
+ if (!str_auth[0])
+ {
+ weechat_printf (NULL, _("%sremote[%s]: failed to build authentication"),
+ weechat_prefix ("error"), remote->name);
+ relay_remote_network_disconnect (remote);
+ goto end;
+ }
+
+ /* generate random websocket key (16 bytes) */
+ gcry_create_nonce (ws_key, sizeof (ws_key));
+ weechat_string_base_encode ("64", ws_key, sizeof (ws_key), ws_key_base64);
+ if (remote->websocket_key)
+ free (remote->websocket_key);
+ remote->websocket_key = strdup (ws_key_base64);
+
+ weechat_string_base_encode ("64", str_auth, strlen (str_auth), str_auth_base64);
+
+ if (totp_secret && totp_secret[0])
+ {
+ /* generate the TOTP with the secret */
+ totp = weechat_info_get ("totp_generate", totp_secret);
+ if (totp)
+ {
+ snprintf (str_totp, sizeof (str_totp),
+ "x-weechat-totp: %s\r\n",
+ totp);
+ free (totp);
+ }
+ }
+
+ snprintf (
+ str_http, sizeof (str_http),
+ "GET /api HTTP/1.1\r\n"
+ "Authorization: Basic %s\r\n"
+ "%s"
+ "Sec-WebSocket-Version: 13\r\n"
+ "Sec-WebSocket-Key: %s\r\n"
+ "Connection: Upgrade\r\n"
+ "Upgrade: websocket\r\n"
+ "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n"
+ "Host: %s:%d\r\n"
+ "\r\n",
+ str_auth_base64,
+ str_totp,
+ ws_key_base64,
+ remote->address,
+ remote->port);
+ relay_remote_network_send (remote, RELAY_MSG_STANDARD,
+ str_http, strlen (str_http));
+
+end:
+ if (password)
+ free (password);
+ if (totp_secret)
+ free (totp_secret);
+}
+
+/*
+ * Callback for connect hook.
+ */
+
+int
+relay_remote_network_connect_cb (const void *pointer, void *data, int status,
+ int gnutls_rc, int sock, const char *error,
+ const char *ip_address)
+{
+ struct t_relay_remote *remote;
+ /*int dhkey_size;*/
+
+ /* make C compiler happy */
+ (void) data;
+
+ remote = (struct t_relay_remote *)pointer;
+
+ remote->hook_connect = NULL;
+
+ remote->sock = sock;
+
+ switch (status)
+ {
+ case WEECHAT_HOOK_CONNECT_OK:
+ weechat_printf (NULL, _("remote[%s]: connected to %s/%d (%s)"),
+ remote->name, remote->address, remote->port,
+ ip_address);
+ remote->hook_fd = weechat_hook_fd (remote->sock, 1, 0, 0,
+ &relay_remote_network_recv_cb,
+ remote, NULL);
+ /* authenticate with remote relay */
+ relay_remote_network_connect_ws_auth (remote);
+ break;
+ case WEECHAT_HOOK_CONNECT_ADDRESS_NOT_FOUND:
+ weechat_printf (NULL, _("%sremote[%s]: address \"%s\" not found"),
+ weechat_prefix ("error"), remote->name,
+ remote->address);
+ if (error && error[0])
+ {
+ weechat_printf (NULL, _("%sremote[%s]: error: %s"),
+ weechat_prefix ("error"), remote->name, error);
+ }
+ break;
+ case WEECHAT_HOOK_CONNECT_IP_ADDRESS_NOT_FOUND:
+ weechat_printf (NULL, _("%sremote[%s]: IP address not found"),
+ weechat_prefix ("error"), remote->name);
+ if (error && error[0])
+ {
+ weechat_printf (NULL, _("%sremote[%s]: error: %s"),
+ weechat_prefix ("error"), remote->name, error);
+ }
+ break;
+ case WEECHAT_HOOK_CONNECT_CONNECTION_REFUSED:
+ weechat_printf (NULL, _("%sremote[%s]: connection refused"),
+ weechat_prefix ("error"), remote->name);
+ if (error && error[0])
+ {
+ weechat_printf (NULL, _("%sremote[%s]: error: %s"),
+ weechat_prefix ("error"), remote->name, error);
+ }
+ break;
+ case WEECHAT_HOOK_CONNECT_PROXY_ERROR:
+ weechat_printf (
+ NULL,
+ _("%sremote[%s]: proxy fails to establish connection to server (check "
+ "username/password if used and if server address/port is "
+ "allowed by proxy)"),
+ weechat_prefix ("error"), remote->name);
+ if (error && error[0])
+ {
+ weechat_printf (NULL, _("%sremote[%s]: error: %s"),
+ weechat_prefix ("error"), remote->name, error);
+ }
+ break;
+ case WEECHAT_HOOK_CONNECT_LOCAL_HOSTNAME_ERROR:
+ weechat_printf (NULL, _("%sremote[%s]: unable to set local hostname/IP"),
+ weechat_prefix ("error"), remote->name);
+ if (error && error[0])
+ {
+ weechat_printf (NULL, _("%sremote[%s]: error: %s"),
+ weechat_prefix ("error"), remote->name, error);
+ }
+ break;
+ case WEECHAT_HOOK_CONNECT_GNUTLS_INIT_ERROR:
+ weechat_printf (NULL, _("%sremote[%s]: TLS init error"),
+ weechat_prefix ("error"), remote->name);
+ if (error && error[0])
+ {
+ weechat_printf (NULL, _("%sremote[%s]: error: %s"),
+ weechat_prefix ("error"), remote->name, error);
+ }
+ break;
+ case WEECHAT_HOOK_CONNECT_GNUTLS_HANDSHAKE_ERROR:
+ weechat_printf (NULL, _("%sremote[%s]: TLS handshake failed"),
+ weechat_prefix ("error"), remote->name);
+ if (error && error[0])
+ {
+ weechat_printf (NULL, _("%sremote[%s]: error: %s"),
+ weechat_prefix ("error"), remote->name, error);
+ }
+ if (gnutls_rc == GNUTLS_E_DH_PRIME_UNACCEPTABLE)
+ {
+ /* dhkey_size = weechat_config_integer ( */
+ /* remote->options[RELAY_REMOTE_OPTION_TLS_DHKEY_SIZE]); */
+ /* weechat_printf ( */
+ /* NULL, */
+ /* _("%sremote[%s]: you should play with option " */
+ /* "relay.remote.%s.tls_dhkey_size (current value is %d, try " */
+ /* "a lower value like %d or %d)"), */
+ /* weechat_prefix ("error"), */
+ /* remote->name, */
+ /* remote->name, */
+ /* dhkey_size, */
+ /* dhkey_size / 2, */
+ /* dhkey_size / 4); */
+ }
+ break;
+ case WEECHAT_HOOK_CONNECT_MEMORY_ERROR:
+ weechat_printf (NULL, _("%sremote[%s]: not enough memory"),
+ weechat_prefix ("error"), remote->name);
+ if (error && error[0])
+ {
+ weechat_printf (NULL, _("%sremote[%s]: error: %s"),
+ weechat_prefix ("error"), remote->name, error);
+ }
+ break;
+ case WEECHAT_HOOK_CONNECT_TIMEOUT:
+ weechat_printf (NULL, _("%sremote[%s]: timeout"),
+ weechat_prefix ("error"), remote->name);
+ if (error && error[0])
+ {
+ weechat_printf (NULL, _("%sremote[%s]: error: %s"),
+ weechat_prefix ("error"), remote->name, error);
+ }
+ break;
+ case WEECHAT_HOOK_CONNECT_SOCKET_ERROR:
+ weechat_printf (NULL, _("%sremote[%s]: unable to create socket"),
+ weechat_prefix ("error"), remote->name);
+ if (error && error[0])
+ {
+ weechat_printf (NULL, _("%sremote[%s]: error: %s"),
+ weechat_prefix ("error"), remote->name, error);
+ }
+ break;
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Callback for handshake URL.
+ */
+
+int
+relay_remote_network_url_handshake_cb (const void *pointer,
+ void *data,
+ const char *url,
+ struct t_hashtable *options,
+ struct t_hashtable *output)
+{
+ struct t_relay_remote *remote;
+ const char *ptr_output, *ptr_resp_code, *ptr_error;
+ cJSON *json_body, *json_hash_algo, *json_hash_iterations, *json_totp;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) url;
+ (void) options;
+
+ remote = (struct t_relay_remote *)pointer;
+
+ ptr_resp_code = weechat_hashtable_get (output, "response_code");
+ if (ptr_resp_code && ptr_resp_code[0] && (strcmp (ptr_resp_code, "200") != 0))
+ {
+ weechat_printf (NULL,
+ _("%s%s: handshake failed with URL %s, response code: %s"),
+ weechat_prefix ("error"), RELAY_PLUGIN_NAME,
+ weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL]),
+ ptr_resp_code);
+ return WEECHAT_RC_OK;
+ }
+
+ ptr_error = weechat_hashtable_get (output, "error");
+ if (ptr_error && ptr_error[0])
+ {
+ weechat_printf (NULL,
+ _("%s%s: handshake failed with URL %s, error: %s"),
+ weechat_prefix ("error"), RELAY_PLUGIN_NAME,
+ weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL]),
+ ptr_error);
+ return WEECHAT_RC_OK;
+ }
+
+ ptr_output = weechat_hashtable_get (output, "output");
+ if (ptr_output && ptr_output[0])
+ {
+ json_body = cJSON_Parse (weechat_hashtable_get (output, "output"));
+ if (json_body)
+ {
+ /* hash algorithm */
+ json_hash_algo = cJSON_GetObjectItem (json_body, "password_hash_algo");
+ if (json_hash_algo && cJSON_IsString (json_hash_algo))
+ {
+ remote->password_hash_algo = relay_auth_password_hash_algo_search (
+ cJSON_GetStringValue (json_hash_algo));
+ }
+ /* hash iterations */
+ json_hash_iterations = cJSON_GetObjectItem (json_body, "password_hash_iterations");
+ if (json_hash_iterations && cJSON_IsNumber (json_hash_iterations))
+ remote->password_hash_iterations = json_hash_iterations->valueint;
+ /* TOTP */
+ json_totp = cJSON_GetObjectItem (json_body, "totp");
+ if (json_totp && cJSON_IsBool (json_totp))
+ remote->totp = (cJSON_IsTrue (json_totp)) ? 1 : 0;
+ }
+ }
+
+ if (remote->password_hash_algo < 0)
+ {
+ weechat_printf (NULL,
+ _("%s%s: handshake failed with URL %s, error: %s"),
+ weechat_prefix ("error"), RELAY_PLUGIN_NAME,
+ weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL]),
+ _("hash algorithm not found"));
+ return WEECHAT_RC_OK;
+ }
+
+ if (remote->password_hash_iterations < 0)
+ {
+ weechat_printf (NULL,
+ _("%s%s: handshake failed with URL %s, error: %s"),
+ weechat_prefix ("error"), RELAY_PLUGIN_NAME,
+ weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL]),
+ _("unknown number of hash iterations"));
+ return WEECHAT_RC_OK;
+ }
+
+ if (remote->totp < 0)
+ {
+ weechat_printf (NULL,
+ _("%s%s: handshake failed with URL %s, error: %s"),
+ weechat_prefix ("error"), RELAY_PLUGIN_NAME,
+ weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL]),
+ _("unknown TOTP status"));
+ return WEECHAT_RC_OK;
+ }
+
+ if (weechat_relay_plugin->debug >= 1)
+ {
+ weechat_printf (NULL,
+ _("%s: successful handshake with URL %s: "
+ "hash_algo=%s, iterations=%d, totp=%d"),
+ RELAY_PLUGIN_NAME,
+ weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL]),
+ relay_auth_password_hash_algo_name[remote->password_hash_algo],
+ remote->password_hash_iterations,
+ remote->totp);
+ }
+
+ remote->hook_connect = weechat_hook_connect (
+ NULL, /* proxy */
+ remote->address,
+ remote->port,
+ 1, /* ipv6 */
+ 0, /* retry */
+ NULL, /* gnutls_sess */
+ NULL, /* gnutls_cb */
+ 0, /* gnutls_dhkey_size */
+ NULL, /* gnutls_priorities */
+ NULL, /* local_hostname */
+ &relay_remote_network_connect_cb,
+ remote,
+ NULL);
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Builds a string with the API HTTP handshake request.
+ *
+ * Note: result must be free after use.
+ */
+
+char *
+relay_remote_network_get_handshake_request ()
+{
+ char **body;
+ int i;
+
+ body = weechat_string_dyn_alloc (256);
+ if (!body)
+ return NULL;
+
+ weechat_string_dyn_concat (body, "{\"password_hash_algo\": [", -1);
+ /* all password hash algorithms are supported */
+ for (i = 0; i < RELAY_NUM_PASSWORD_HASH_ALGOS; i++)
+ {
+ if (i > 0)
+ weechat_string_dyn_concat (body, ", ", -1);
+ weechat_string_dyn_concat (body, "\"", -1);
+ weechat_string_dyn_concat (body,
+ relay_auth_password_hash_algo_name[i], -1);
+ weechat_string_dyn_concat (body, "\"", -1);
+ }
+ weechat_string_dyn_concat (body, "]}", -1);
+ return weechat_string_dyn_free (body, 0);
+}
+
+/*
+ * Connects to a remote WeeChat relay/api.
+ *
+ * Returns:
+ * 1: OK
+ * 0: error
+ */
+
+int
+relay_remote_network_connect (struct t_relay_remote *remote)
+{
+ char *url, *body;
+ struct t_hashtable *options;
+
+ url = NULL;
+ body = NULL;
+ options = NULL;
+
+ if (!remote)
+ return 0;
+
+ if (remote->sock != -1)
+ {
+ weechat_printf (
+ NULL,
+ _("%s%s: already connected to remote \"%s\"!"),
+ weechat_prefix ("error"), RELAY_PLUGIN_NAME, remote->name);
+ return 0;
+ }
+
+ relay_remote_set_status (remote, RELAY_STATUS_CONNECTING);
+
+ weechat_printf (NULL,
+ _("remote[%s]: connecting to remote %s/%d%s..."),
+ remote->name,
+ remote->address,
+ remote->port,
+ (remote->tls) ? " (TLS)" : "");
+
+ url = relay_remote_network_get_url_resource (remote, "handshake");
+ if (!url)
+ goto error;
+
+ options = weechat_hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_STRING,
+ NULL, NULL);
+ if (!options)
+ goto error;
+
+ weechat_hashtable_set (options, "post", "1");
+ weechat_hashtable_set (options,
+ "httpheader",
+ "Accept: application/json\n"
+ "Content-Type: application/json; charset=utf-8");
+ body = relay_remote_network_get_handshake_request ();
+ if (!body)
+ goto error;
+
+ weechat_hashtable_set (options, "postfields", body);
+
+ remote->hook_url_handshake = weechat_hook_url (
+ url, options, 5 * 1000,
+ &relay_remote_network_url_handshake_cb, remote, NULL);
+
+ free (url);
+ free (body);
+ weechat_hashtable_free (options);
+
+ return 1;
+
+error:
+ weechat_printf (NULL,
+ _("remote[%s]: failed to connect, not enough memory"),
+ remote->name);
+ if (url)
+ free (url);
+ if (body)
+ free (body);
+ if (options)
+ weechat_hashtable_free (options);
+ return 0;
+}
diff --git a/src/plugins/relay/api/remote/relay-remote-network.h b/src/plugins/relay/api/remote/relay-remote-network.h
new file mode 100644
index 000000000..7635beb63
--- /dev/null
+++ b/src/plugins/relay/api/remote/relay-remote-network.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 Sébastien Helleu <flashcode@flashtux.org>
+ *
+ * This file is part of WeeChat, the extensible chat client.
+ *
+ * WeeChat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * WeeChat is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with WeeChat. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef WEECHAT_PLUGIN_RELAY_REMOTE_NETWORK_H
+#define WEECHAT_PLUGIN_RELAY_REMOTE_NETWORK_H
+
+extern int relay_remote_network_connect (struct t_relay_remote *remote);
+extern void relay_remote_network_disconnect (struct t_relay_remote *remote);
+
+#endif /* WEECHAT_PLUGIN_RELAY_REMOTE_NETWORK_H */
diff --git a/src/plugins/relay/irc/relay-irc.c b/src/plugins/relay/irc/relay-irc.c
index c0885511c..8389ffa8f 100644
--- a/src/plugins/relay/irc/relay-irc.c
+++ b/src/plugins/relay/irc/relay-irc.c
@@ -279,7 +279,7 @@ relay_irc_sendf (struct t_relay_client *client, const char *format, ...)
if (message)
{
snprintf (message, length, "%s\r\n", ptr_msg2);
- relay_client_send (client, RELAY_CLIENT_MSG_STANDARD,
+ relay_client_send (client, RELAY_MSG_STANDARD,
message, strlen (message), NULL);
free (message);
}
diff --git a/src/plugins/relay/relay-client.c b/src/plugins/relay/relay-client.c
index e1567722f..7b627d6fd 100644
--- a/src/plugins/relay/relay-client.c
+++ b/src/plugins/relay/relay-client.c
@@ -52,9 +52,6 @@
char *relay_client_data_type_string[] = /* strings for data types */
{ "text", "binary", "http" };
-char *relay_client_msg_type_string[] = /* prefix in raw buffer for message */
-{ "", "[PING]\n", "[PONG]\n", "[CLOSE]\n" };
-
struct t_relay_client *relay_clients = NULL;
struct t_relay_client *last_relay_client = NULL;
int relay_client_count = 0; /* number of clients */
@@ -313,9 +310,9 @@ relay_client_recv_text_single_line (struct t_relay_client *client)
pos - client->partial_message + 1);
if (raw_msg)
{
- relay_raw_print (client, RELAY_CLIENT_MSG_STANDARD,
- RELAY_RAW_FLAG_RECV,
- raw_msg, strlen (raw_msg) + 1);
+ relay_raw_print_client (client, RELAY_MSG_STANDARD,
+ RELAY_RAW_FLAG_RECV,
+ raw_msg, strlen (raw_msg) + 1);
free (raw_msg);
}
@@ -380,10 +377,10 @@ relay_client_recv_text_multi_line (struct t_relay_client *client)
return;
/* print message in raw buffer */
- relay_raw_print (client, RELAY_CLIENT_MSG_STANDARD,
- RELAY_RAW_FLAG_RECV,
- client->partial_message,
- strlen (client->partial_message) + 1);
+ relay_raw_print_client (client, RELAY_MSG_STANDARD,
+ RELAY_RAW_FLAG_RECV,
+ client->partial_message,
+ strlen (client->partial_message) + 1);
/*
* interpret text from client, according to the relay
@@ -457,7 +454,7 @@ relay_client_recv_text_buffer (struct t_relay_client *client,
index = 0;
while (index < length_buffer)
{
- msg_type = RELAY_CLIENT_MSG_STANDARD;
+ msg_type = RELAY_MSG_STANDARD;
/*
* in case of websocket, we can receive PING from client:
@@ -466,30 +463,30 @@ relay_client_recv_text_buffer (struct t_relay_client *client,
if (client->websocket == RELAY_CLIENT_WEBSOCKET_READY)
{
msg_type = (unsigned char)buffer[index];
- if (msg_type == RELAY_CLIENT_MSG_PING)
+ if (msg_type == RELAY_MSG_PING)
{
/* print message in raw buffer */
- relay_raw_print (client, RELAY_CLIENT_MSG_PING,
- RELAY_RAW_FLAG_RECV | RELAY_RAW_FLAG_BINARY,
- buffer + index + 1,
- strlen (buffer + index + 1));
+ relay_raw_print_client (client, RELAY_MSG_PING,
+ RELAY_RAW_FLAG_RECV | RELAY_RAW_FLAG_BINARY,
+ buffer + index + 1,
+ strlen (buffer + index + 1));
/* answer with a PONG */
relay_client_send (client,
- RELAY_CLIENT_MSG_PONG,
+ RELAY_MSG_PONG,
buffer + index + 1,
strlen (buffer + index + 1),
NULL);
}
- else if (msg_type == RELAY_CLIENT_MSG_CLOSE)
+ else if (msg_type == RELAY_MSG_CLOSE)
{
/* print message in raw buffer */
- relay_raw_print (client, RELAY_CLIENT_MSG_CLOSE,
- RELAY_RAW_FLAG_RECV | RELAY_RAW_FLAG_BINARY,
- buffer + index + 1,
- strlen (buffer + index + 1));
+ relay_raw_print_client (client, RELAY_MSG_CLOSE,
+ RELAY_RAW_FLAG_RECV | RELAY_RAW_FLAG_BINARY,
+ buffer + index + 1,
+ strlen (buffer + index + 1));
/* answer with a CLOSE */
relay_client_send (client,
- RELAY_CLIENT_MSG_CLOSE,
+ RELAY_MSG_CLOSE,
buffer + index + 1,
strlen (buffer + index + 1),
NULL);
@@ -501,7 +498,7 @@ relay_client_recv_text_buffer (struct t_relay_client *client,
index++;
}
- if (msg_type == RELAY_CLIENT_MSG_STANDARD)
+ if (msg_type == RELAY_MSG_STANDARD)
{
if ((client->websocket == RELAY_CLIENT_WEBSOCKET_INITIALIZING)
|| (client->recv_data_type == RELAY_CLIENT_DATA_HTTP))
@@ -549,28 +546,28 @@ relay_client_read_websocket_frames (struct t_relay_client *client,
}
switch (frames[i].opcode)
{
- case RELAY_CLIENT_MSG_PING:
+ case RELAY_MSG_PING:
/* print message in raw buffer */
- relay_raw_print (client, RELAY_CLIENT_MSG_PING,
- RELAY_RAW_FLAG_RECV | RELAY_RAW_FLAG_BINARY,
- frames[i].payload,
- frames[i].payload_size);
+ relay_raw_print_client (client, RELAY_MSG_PING,
+ RELAY_RAW_FLAG_RECV | RELAY_RAW_FLAG_BINARY,
+ frames[i].payload,
+ frames[i].payload_size);
/* answer with a PONG */
relay_client_send (client,
- RELAY_CLIENT_MSG_PONG,
+ RELAY_MSG_PONG,
frames[i].payload,
frames[i].payload_size,
NULL);
break;
- case RELAY_CLIENT_MSG_CLOSE:
+ case RELAY_MSG_CLOSE:
/* print message in raw buffer */
- relay_raw_print (client, RELAY_CLIENT_MSG_CLOSE,
- RELAY_RAW_FLAG_RECV | RELAY_RAW_FLAG_BINARY,
- frames[i].payload,
- frames[i].payload_size);
+ relay_raw_print_client (client, RELAY_MSG_CLOSE,
+ RELAY_RAW_FLAG_RECV | RELAY_RAW_FLAG_BINARY,
+ frames[i].payload,
+ frames[i].payload_size);
/* answer with a CLOSE */
relay_client_send (client,
- RELAY_CLIENT_MSG_CLOSE,
+ RELAY_MSG_CLOSE,
frames[i].payload,
frames[i].payload_size,
NULL);
@@ -653,11 +650,14 @@ relay_client_recv_buffer (struct t_relay_client *client,
frames = NULL;
num_frames = 0;
rc = relay_websocket_decode_frame (
- client,
(buffer2) ? (unsigned char *)buffer2 : (unsigned char *)buffer,
(buffer2) ? (unsigned long long)buffer2_size : (unsigned long long)buffer_size,
+ 1, /* expect_masked_frame */
+ client->ws_deflate,
&frames,
- &num_frames);
+ &num_frames,
+ &client->partial_ws_frame,
+ &client->partial_ws_frame_size);
if (buffer2)
free (buffer2);
if (!rc)
@@ -899,7 +899,7 @@ relay_client_send_outqueue (struct t_relay_client *client)
* (so that it is displayed only one time, even if
* message is sent in many chunks)
*/
- relay_raw_print (
+ relay_raw_print_client (
client,
client->outqueue->raw_msg_type[i],
client->outqueue->raw_flags[i],
@@ -1033,7 +1033,7 @@ relay_client_timer_send_cb (const void *pointer, void *data,
void
relay_client_outqueue_add (struct t_relay_client *client,
const char *data, int data_size,
- enum t_relay_client_msg_type raw_msg_type[2],
+ enum t_relay_msg_type raw_msg_type[2],
int raw_flags[2],
const char *raw_message[2],
int raw_size[2])
@@ -1059,7 +1059,7 @@ relay_client_outqueue_add (struct t_relay_client *client,
new_outqueue->data_size = data_size;
for (i = 0; i < 2; i++)
{
- new_outqueue->raw_msg_type[i] = RELAY_CLIENT_MSG_STANDARD;
+ new_outqueue->raw_msg_type[i] = RELAY_MSG_STANDARD;
new_outqueue->raw_flags[i] = 0;
new_outqueue->raw_message[i] = NULL;
new_outqueue->raw_size[i] = 0;
@@ -1104,12 +1104,12 @@ relay_client_outqueue_add (struct t_relay_client *client,
int
relay_client_send (struct t_relay_client *client,
- enum t_relay_client_msg_type msg_type,
+ enum t_relay_msg_type msg_type,
const char *data, int data_size,
const char *message_raw_buffer)
{
int num_sent, raw_size[2], raw_flags[2], opcode, i;
- enum t_relay_client_msg_type raw_msg_type[2];
+ enum t_relay_msg_type raw_msg_type[2];
char *websocket_frame;
unsigned long long length_frame;
const char *ptr_data, *raw_msg[2];
@@ -1154,9 +1154,9 @@ relay_client_send (struct t_relay_client *client,
{
raw_msg[0] = data;
raw_size[0] = data_size;
- if ((msg_type == RELAY_CLIENT_MSG_PING)
- || (msg_type == RELAY_CLIENT_MSG_PONG)
- || (msg_type == RELAY_CLIENT_MSG_CLOSE)
+ if ((msg_type == RELAY_MSG_PING)
+ || (msg_type == RELAY_MSG_PONG)
+ || (msg_type == RELAY_MSG_CLOSE)
|| ((client->websocket != RELAY_CLIENT_WEBSOCKET_INITIALIZING)
&& (client->send_data_type == RELAY_CLIENT_DATA_BINARY)))
{
@@ -1179,13 +1179,13 @@ relay_client_send (struct t_relay_client *client,
{
switch (msg_type)
{
- case RELAY_CLIENT_MSG_PING:
+ case RELAY_MSG_PING:
opcode = WEBSOCKET_FRAME_OPCODE_PING;
break;
- case RELAY_CLIENT_MSG_PONG:
+ case RELAY_MSG_PONG:
opcode = WEBSOCKET_FRAME_OPCODE_PONG;
break;
- case RELAY_CLIENT_MSG_CLOSE:
+ case RELAY_MSG_CLOSE:
opcode = WEBSOCKET_FRAME_OPCODE_CLOSE;
break;
default:
@@ -1195,7 +1195,7 @@ relay_client_send (struct t_relay_client *client,
break;
}
websocket_frame = relay_websocket_encode_frame (
- client, opcode, data, data_size, &length_frame);
+ client->ws_deflate, opcode, 0, data, data_size, &length_frame);
if (websocket_frame)
{
ptr_data = websocket_frame;
@@ -1223,8 +1223,9 @@ relay_client_send (struct t_relay_client *client,
{
if (raw_msg[i])
{
- relay_raw_print (client, raw_msg_type[i], raw_flags[i],
- raw_msg[i], raw_size[i]);
+ relay_raw_print_client (client, raw_msg_type[i],
+ raw_flags[i],
+ raw_msg[i], raw_size[i]);
}
}
if (num_sent > 0)
diff --git a/src/plugins/relay/relay-client.h b/src/plugins/relay/relay-client.h
index b2b5a7661..9231f2d3a 100644
--- a/src/plugins/relay/relay-client.h
+++ b/src/plugins/relay/relay-client.h
@@ -50,18 +50,6 @@ enum t_relay_client_websocket_status
RELAY_NUM_CLIENT_WEBSOCKET_STATUS,
};
-/* type of message exchanged with the client (used for websockets) */
-
-enum t_relay_client_msg_type
-{
- RELAY_CLIENT_MSG_STANDARD,
- RELAY_CLIENT_MSG_PING,
- RELAY_CLIENT_MSG_PONG,
- RELAY_CLIENT_MSG_CLOSE,
- /* number of message types */
- RELAY_NUM_CLIENT_MSG_TYPES,
-};
-
/* fake send function (for tests) */
typedef void (t_relay_fake_send_func)(void *client,
@@ -127,7 +115,6 @@ struct t_relay_client
struct t_relay_client *next_client;/* link to next client */
};
-extern char *relay_client_msg_type_string[];
extern struct t_relay_client *relay_clients;
extern struct t_relay_client *last_relay_client;
extern int relay_client_count;
@@ -141,7 +128,7 @@ extern void relay_client_recv_buffer (struct t_relay_client *client,
const char *buffer, int buffer_size);
extern int relay_client_recv_cb (const void *pointer, void *data, int fd);
extern int relay_client_send (struct t_relay_client *client,
- enum t_relay_client_msg_type msg_type,
+ enum t_relay_msg_type msg_type,
const char *data,
int data_size, const char *message_raw_buffer);
extern int relay_client_timer_cb (const void *pointer, void *data,
diff --git a/src/plugins/relay/relay-command.c b/src/plugins/relay/relay-command.c
index 7b6643b3d..2da4f53a3 100644
--- a/src/plugins/relay/relay-command.c
+++ b/src/plugins/relay/relay-command.c
@@ -501,7 +501,6 @@ relay_command_remote (const void *pointer, void *data,
if (weechat_strcmp (argv[1], "add") == 0)
{
WEECHAT_COMMAND_MIN_ARGS(4, "add");
-
ptr_remote = relay_remote_search (argv[2]);
if (ptr_remote)
{
@@ -511,7 +510,6 @@ relay_command_remote (const void *pointer, void *data,
weechat_prefix ("error"), RELAY_PLUGIN_NAME, ptr_remote->name);
return WEECHAT_RC_OK;
}
-
if (!relay_remote_name_valid (argv[2]))
{
weechat_printf (NULL,
@@ -521,8 +519,6 @@ relay_command_remote (const void *pointer, void *data,
argv[2]);
return WEECHAT_RC_OK;
}
-
-
if (!relay_remote_url_valid (argv[3]))
{
weechat_printf (NULL,
@@ -532,11 +528,9 @@ relay_command_remote (const void *pointer, void *data,
argv[3]);
return WEECHAT_RC_OK;
}
-
ptr_proxy = NULL;
ptr_password = NULL;
ptr_totp_secret = NULL;
-
for (i = 4; i < argc; i++)
{
if (strncmp (argv[i], "-proxy=", 7) == 0)
@@ -561,7 +555,6 @@ relay_command_remote (const void *pointer, void *data,
return WEECHAT_RC_OK;
}
}
-
ptr_remote = relay_remote_new (argv[2], argv[3], ptr_proxy,
ptr_password, ptr_totp_secret);
if (ptr_remote)
@@ -576,14 +569,12 @@ relay_command_remote (const void *pointer, void *data,
weechat_prefix ("error"), RELAY_PLUGIN_NAME,
argv[2]);
}
-
return WEECHAT_RC_OK;
}
if (weechat_strcmp (argv[1], "connect") == 0)
{
WEECHAT_COMMAND_MIN_ARGS(3, "connect");
-
ptr_remote = relay_remote_search (argv[2]);
if (!ptr_remote)
{
@@ -596,14 +587,13 @@ relay_command_remote (const void *pointer, void *data,
"remote connect");
return WEECHAT_RC_OK;
}
-
- WEECHAT_COMMAND_ERROR;
+ relay_remote_connect (ptr_remote);
+ return WEECHAT_RC_OK;
}
if (weechat_strcmp (argv[1], "rename") == 0)
{
WEECHAT_COMMAND_MIN_ARGS(4, "rename");
-
/* look for remote by name */
ptr_remote = relay_remote_search (argv[2]);
if (!ptr_remote)
@@ -617,7 +607,6 @@ relay_command_remote (const void *pointer, void *data,
"remote rename");
return WEECHAT_RC_OK;
}
-
/* check if target name already exists */
ptr_remote2 = relay_remote_search (argv[3]);
if (ptr_remote2)
@@ -631,7 +620,6 @@ relay_command_remote (const void *pointer, void *data,
"server rename");
return WEECHAT_RC_OK;
}
-
/* rename remote */
if (relay_remote_rename (ptr_remote, argv[3]))
{
@@ -643,14 +631,12 @@ relay_command_remote (const void *pointer, void *data,
argv[3]);
return WEECHAT_RC_OK;
}
-
WEECHAT_COMMAND_ERROR;
}
if (weechat_strcmp (argv[1], "disconnect") == 0)
{
WEECHAT_COMMAND_MIN_ARGS(3, "disconnect");
-
ptr_remote = relay_remote_search (argv[2]);
if (!ptr_remote)
{
@@ -663,14 +649,13 @@ relay_command_remote (const void *pointer, void *data,
"remote disconnect");
return WEECHAT_RC_OK;
}
-
- WEECHAT_COMMAND_ERROR;
+ relay_remote_disconnect (ptr_remote);
+ return WEECHAT_RC_OK;
}
if (weechat_strcmp (argv[1], "del") == 0)
{
WEECHAT_COMMAND_MIN_ARGS(3, "del");
-
/* look for remote by name */
ptr_remote = relay_remote_search (argv[2]);
if (!ptr_remote)
@@ -705,7 +690,6 @@ relay_command_remote (const void *pointer, void *data,
(remote_name) ? remote_name : "???");
if (remote_name)
free (remote_name);
-
return WEECHAT_RC_OK;
}
diff --git a/src/plugins/relay/relay-http.c b/src/plugins/relay/relay-http.c
index 8b64deb23..58879b4fa 100644
--- a/src/plugins/relay/relay-http.c
+++ b/src/plugins/relay/relay-http.c
@@ -833,7 +833,7 @@ relay_http_process_websocket (struct t_relay_client *client)
if (handshake)
{
relay_client_send (client,
- RELAY_CLIENT_MSG_STANDARD,
+ RELAY_MSG_STANDARD,
handshake,
strlen (handshake), NULL);
free (handshake);
@@ -877,10 +877,10 @@ relay_http_process_request (struct t_relay_client *client)
{
if (client->http_req->raw)
{
- relay_raw_print (client, RELAY_CLIENT_MSG_STANDARD,
- RELAY_RAW_FLAG_RECV,
- *(client->http_req->raw),
- strlen (*(client->http_req->raw)) + 1);
+ relay_raw_print_client (client, RELAY_MSG_STANDARD,
+ RELAY_RAW_FLAG_RECV,
+ *(client->http_req->raw),
+ strlen (*(client->http_req->raw)) + 1);
}
/* if websocket is initializing */
@@ -1199,7 +1199,7 @@ relay_http_send (struct t_relay_client *client,
if (!ptr_body || (*ptr_body_size <= 0))
{
- num_bytes = relay_client_send (client, RELAY_CLIENT_MSG_STANDARD,
+ num_bytes = relay_client_send (client, RELAY_MSG_STANDARD,
str_header, length_header, NULL);
}
else
@@ -1227,7 +1227,7 @@ relay_http_send (struct t_relay_client *client,
{
raw_message = NULL;
}
- num_bytes = relay_client_send (client, RELAY_CLIENT_MSG_STANDARD,
+ num_bytes = relay_client_send (client, RELAY_MSG_STANDARD,
http_message, length_msg,
raw_message);
if (raw_message)
diff --git a/src/plugins/relay/relay-raw.c b/src/plugins/relay/relay-raw.c
index f7e854e54..b18d7b796 100644
--- a/src/plugins/relay/relay-raw.c
+++ b/src/plugins/relay/relay-raw.c
@@ -31,6 +31,7 @@
#include "relay-buffer.h"
#include "relay-client.h"
#include "relay-config.h"
+#include "relay-remote.h"
struct t_gui_buffer *relay_raw_buffer = NULL;
@@ -235,76 +236,95 @@ relay_raw_message_add_to_list (time_t date, int date_usec,
}
/*
- * Adds a new raw message to list.
+ * Converts a binary message for raw display.
*/
-void
-relay_raw_message_add (struct t_relay_client *client,
- enum t_relay_client_msg_type msg_type,
- int flags,
- const char *data, int data_size)
+char *
+relay_raw_convert_binary_message (const char *data, int data_size)
+{
+ return weechat_string_hex_dump (data, data_size, 16, " > ", NULL);
+}
+
+/*
+ * Converts a text message for raw display.
+ */
+
+char *
+relay_raw_convert_text_message (const char *data)
{
- char *buf, *buf2, *buf3, prefix[256], prefix_arrow[16];
const unsigned char *ptr_buf;
const char *hexa = "0123456789ABCDEF";
- int pos_buf, pos_buf2, char_size, i, length;
- struct t_relay_raw_message *new_raw_message;
- struct timeval tv_now;
-
- buf = NULL;
- buf2 = NULL;
- buf3 = NULL;
+ char *buf, *buf2;
+ int i, pos_buf, pos_buf2, char_size;
- if (flags & RELAY_RAW_FLAG_BINARY)
- {
- /* binary message */
- buf = weechat_string_hex_dump (data, data_size, 16, " > ", NULL);
- snprintf (prefix, sizeof (prefix), " ");
- }
- else
+ buf = weechat_iconv_to_internal (NULL, data);
+ if (!buf)
+ return NULL;
+ buf2 = weechat_string_replace (buf, "\r", "");
+ free (buf);
+ if (!buf2)
+ return NULL;
+ buf = buf2;
+ buf2 = malloc ((strlen (buf) * 4) + 1);
+ if (buf2)
{
- /* text message */
- buf = weechat_iconv_to_internal (NULL, data);
- buf2 = weechat_string_replace (buf, "\r", "");
- if (buf2)
- {
- free (buf);
- buf = buf2;
- buf2 = NULL;
- }
- buf2 = malloc ((strlen (buf) * 4) + 1);
- if (buf2)
+ ptr_buf = (unsigned char *)buf;
+ pos_buf = 0;
+ pos_buf2 = 0;
+ while (ptr_buf[pos_buf])
{
- ptr_buf = (buf) ? (unsigned char *)buf : (unsigned char *)data;
- pos_buf = 0;
- pos_buf2 = 0;
- while (ptr_buf[pos_buf])
+ if ((ptr_buf[pos_buf] < 32) && (ptr_buf[pos_buf] != '\n'))
{
- if ((ptr_buf[pos_buf] < 32) && (ptr_buf[pos_buf] != '\n'))
- {
- buf2[pos_buf2++] = '\\';
- buf2[pos_buf2++] = 'x';
- buf2[pos_buf2++] = hexa[ptr_buf[pos_buf] / 16];
- buf2[pos_buf2++] = hexa[ptr_buf[pos_buf] % 16];
- pos_buf++;
- }
- else
+ buf2[pos_buf2++] = '\\';
+ buf2[pos_buf2++] = 'x';
+ buf2[pos_buf2++] = hexa[ptr_buf[pos_buf] / 16];
+ buf2[pos_buf2++] = hexa[ptr_buf[pos_buf] % 16];
+ pos_buf++;
+ }
+ else
+ {
+ char_size = weechat_utf8_char_size ((const char *)(ptr_buf + pos_buf));
+ for (i = 0; i < char_size; i++)
{
- char_size = weechat_utf8_char_size ((const char *)(ptr_buf + pos_buf));
- for (i = 0; i < char_size; i++)
- {
- buf2[pos_buf2++] = ptr_buf[pos_buf++];
- }
+ buf2[pos_buf2++] = ptr_buf[pos_buf++];
}
}
- buf2[pos_buf2] = '\0';
}
+ buf2[pos_buf2] = '\0';
}
+ free (buf);
+ return buf2;
+}
+
+/*
+ * Adds a new raw message to list.
+ */
+
+void
+relay_raw_message_add (enum t_relay_msg_type msg_type,
+ int flags,
+ const char *peer_id,
+ const char *data, int data_size)
+{
+ char *raw_data, *buf, prefix[1024], prefix_arrow[16];
+ int length;
+ struct t_relay_raw_message *new_raw_message;
+ struct timeval tv_now;
+
+ if (flags & RELAY_RAW_FLAG_BINARY)
+ raw_data = relay_raw_convert_binary_message (data, data_size);
+ else
+ raw_data = relay_raw_convert_text_message (data);
+
+ if (!raw_data)
+ return;
+
+ snprintf (prefix, sizeof (prefix), " ");
if (!(flags & RELAY_RAW_FLAG_BINARY)
- || (msg_type == RELAY_CLIENT_MSG_PING)
- || (msg_type == RELAY_CLIENT_MSG_PONG)
- || (msg_type == RELAY_CLIENT_MSG_CLOSE))
+ || (msg_type == RELAY_MSG_PING)
+ || (msg_type == RELAY_MSG_PONG)
+ || (msg_type == RELAY_MSG_CLOSE))
{
/* build prefix with arrow */
prefix_arrow[0] = '\0';
@@ -324,40 +344,22 @@ relay_raw_message_add (struct t_relay_client *client,
break;
}
- if (client)
- {
- snprintf (prefix, sizeof (prefix), "%s%s %s[%s%d%s] %s%s%s%s",
- (flags & RELAY_RAW_FLAG_SEND) ?
- weechat_color ("chat_prefix_quit") :
- weechat_color ("chat_prefix_join"),
- prefix_arrow,
- weechat_color ("chat_delimiters"),
- weechat_color ("chat"),
- client->id,
- weechat_color ("chat_delimiters"),
- weechat_color ("chat_server"),
- relay_protocol_string[client->protocol],
- (client->protocol_args) ? "." : "",
- (client->protocol_args) ? client->protocol_args : "");
- }
- else
- {
- snprintf (prefix, sizeof (prefix), "%s%s",
- (flags & RELAY_RAW_FLAG_SEND) ?
- weechat_color ("chat_prefix_quit") :
- weechat_color ("chat_prefix_join"),
- prefix_arrow);
- }
+ snprintf (prefix, sizeof (prefix), "%s%s%s%s",
+ (flags & RELAY_RAW_FLAG_SEND) ?
+ weechat_color ("chat_prefix_quit") :
+ weechat_color ("chat_prefix_join"),
+ prefix_arrow,
+ (peer_id && peer_id[0]) ? " " : "",
+ (peer_id && peer_id[0]) ? peer_id : "");
}
- length = strlen (relay_client_msg_type_string[msg_type]) +
- strlen ((buf2) ? buf2 : ((buf) ? buf : data)) + 1;
- buf3 = malloc (length);
- if (buf3)
+ length = strlen (relay_msg_type_string[msg_type]) + strlen (raw_data) + 1;
+ buf = malloc (length);
+ if (buf)
{
- snprintf (buf3, length, "%s%s",
- relay_client_msg_type_string[msg_type],
- (buf2) ? buf2 : ((buf) ? buf : data));
+ snprintf (buf, length, "%s%s",
+ relay_msg_type_string[msg_type],
+ raw_data);
}
gettimeofday (&tv_now, NULL);
@@ -365,7 +367,7 @@ relay_raw_message_add (struct t_relay_client *client,
tv_now.tv_sec,
tv_now.tv_usec,
prefix,
- (buf3) ? buf3 : ((buf2) ? buf2 : ((buf) ? buf : data)));
+ (buf) ? buf : raw_data);
if (new_raw_message)
{
@@ -377,26 +379,76 @@ relay_raw_message_add (struct t_relay_client *client,
if (buf)
free (buf);
- if (buf2)
- free (buf2);
- if (buf3)
- free (buf3);
+ free (raw_data);
+}
+
+/*
+ * Prints a message for a client on relay raw buffer.
+ */
+
+void
+relay_raw_print_client (struct t_relay_client *client,
+ enum t_relay_msg_type msg_type,
+ int flags,
+ const char *data, int data_size)
+{
+ char peer_id[256];
+
+ /* auto-open relay raw buffer if debug for irc plugin is >= 1 */
+ if (!relay_raw_buffer && (weechat_relay_plugin->debug >= 1))
+ relay_raw_open (0);
+
+ if (client)
+ {
+ snprintf (peer_id, sizeof (peer_id), "%s[%s%d%s] %s%s%s%s",
+ weechat_color ("chat_delimiters"),
+ weechat_color ("chat"),
+ client->id,
+ weechat_color ("chat_delimiters"),
+ weechat_color ("chat_server"),
+ relay_protocol_string[client->protocol],
+ (client->protocol_args) ? "." : "",
+ (client->protocol_args) ? client->protocol_args : "");
+ }
+ else
+ {
+ peer_id[0] = '\0';
+ }
+
+ relay_raw_message_add (msg_type, flags, peer_id, data, data_size);
}
/*
- * Prints a message on relay raw buffer.
+ * Prints a message for a remote on relay raw buffer.
*/
void
-relay_raw_print (struct t_relay_client *client,
- enum t_relay_client_msg_type msg_type, int flags,
- const char *data, int data_size)
+relay_raw_print_remote (struct t_relay_remote *remote,
+ enum t_relay_msg_type msg_type,
+ int flags,
+ const char *data, int data_size)
{
- /* auto-open Relay raw buffer if debug for irc plugin is >= 1 */
+ char peer_id[256];
+
+ /* auto-open relay raw buffer if debug for irc plugin is >= 1 */
if (!relay_raw_buffer && (weechat_relay_plugin->debug >= 1))
relay_raw_open (0);
- relay_raw_message_add (client, msg_type, flags, data, data_size);
+ if (remote)
+ {
+ snprintf (peer_id, sizeof (peer_id), "%s<%sR%s> %s%s",
+ weechat_color ("chat_delimiters"),
+ weechat_color ("chat"),
+ weechat_color ("chat_delimiters"),
+ weechat_color ("chat_server"),
+ remote->name);
+ }
+ else
+ {
+ peer_id[0] = '\0';
+ }
+
+ relay_raw_message_add (msg_type, flags, peer_id, data, data_size);
}
/*
diff --git a/src/plugins/relay/relay-raw.h b/src/plugins/relay/relay-raw.h
index e830d7f92..fd17e12c0 100644
--- a/src/plugins/relay/relay-raw.h
+++ b/src/plugins/relay/relay-raw.h
@@ -32,6 +32,8 @@
#define RELAY_RAW_FLAG_SEND (1 << 1)
#define RELAY_RAW_FLAG_BINARY (1 << 2)
+struct t_relay_remote;
+
struct t_relay_raw_message
{
time_t date; /* date/time of message */
@@ -53,9 +55,12 @@ extern struct t_relay_raw_message *relay_raw_message_add_to_list (time_t date,
int date_usec,
const char *prefix,
const char *message);
-extern void relay_raw_print (struct t_relay_client *client,
- enum t_relay_client_msg_type msg_type, int flags,
- const char *data, int data_size);
+extern void relay_raw_print_client (struct t_relay_client *client,
+ enum t_relay_msg_type msg_type, int flags,
+ const char *data, int data_size);
+extern void relay_raw_print_remote (struct t_relay_remote *remote,
+ enum t_relay_msg_type msg_type, int flags,
+ const char *data, int data_size);
extern void relay_raw_message_free_all ();
extern int relay_raw_add_to_infolist (struct t_infolist *infolist,
struct t_relay_raw_message *raw_message);
diff --git a/src/plugins/relay/relay-remote.c b/src/plugins/relay/relay-remote.c
index 6924b76ab..15dc42b50 100644
--- a/src/plugins/relay/relay-remote.c
+++ b/src/plugins/relay/relay-remote.c
@@ -35,6 +35,10 @@
#include "relay.h"
#include "relay-config.h"
#include "relay-remote.h"
+#include "relay-websocket.h"
+#ifdef HAVE_CJSON
+#include "api/remote/relay-remote-network.h"
+#endif
char *relay_remote_option_string[RELAY_REMOTE_NUM_OPTIONS] =
@@ -226,6 +230,59 @@ relay_remote_send_signal (struct t_relay_remote *remote)
}
/*
+ * Extracts address from URL.
+ *
+ * Note: result must be free after use.
+ */
+
+char *
+relay_remote_get_address (const char *url)
+{
+ const char *ptr_start;
+ char *pos;
+
+ if (!url)
+ return NULL;
+
+ if (strncmp (url, "http://", 7) == 0)
+ ptr_start = url + 7;
+ else if (strncmp (url, "https://", 8) == 0)
+ ptr_start = url + 8;
+ else
+ return NULL;
+
+ pos = strchr (ptr_start, ':');
+ return (pos) ?
+ weechat_strndup (ptr_start, pos - ptr_start) : strdup (ptr_start);
+}
+
+/*
+ * Extracts port from URL.
+ */
+
+int
+relay_remote_get_port (const char *url)
+{
+ char *pos, *error;
+ long port;
+
+ if (!url)
+ goto error;
+
+ pos = strrchr (url, ':');
+ if (!pos)
+ goto error;
+
+ error = NULL;
+ port = strtol (pos + 1, &error, 10);
+ if (error && !error[0])
+ return (int)port;
+
+error:
+ return RELAY_REMOTE_DEFAULT_PORT;
+}
+
+/*
* Allocates and initializes new remote structure.
*
* Returns pointer to new remote, NULL if error.
@@ -256,8 +313,16 @@ relay_remote_alloc (const char *name)
new_remote->port = 0;
new_remote->tls = 0;
new_remote->status = RELAY_STATUS_DISCONNECTED;
+ new_remote->password_hash_algo = -1;
+ new_remote->password_hash_iterations = -1;
+ new_remote->totp = -1;
+ new_remote->websocket_key = NULL;
new_remote->sock = -1;
+ new_remote->hook_url_handshake = NULL;
+ new_remote->hook_connect = NULL;
+ new_remote->hook_fd = NULL;
new_remote->gnutls_sess = NULL;
+ new_remote->ws_deflate = relay_websocket_deflate_alloc ();
new_remote->prev_remote = NULL;
new_remote->next_remote = NULL;
@@ -348,6 +413,11 @@ relay_remote_new_with_options (const char *name, struct t_config_option **option
new_remote->options[i] = options[i];
}
relay_remote_add (new_remote, &relay_remotes, &last_relay_remote);
+ new_remote->address = relay_remote_get_address (
+ weechat_config_string (new_remote->options[RELAY_REMOTE_OPTION_URL]));
+ new_remote->port = relay_remote_get_port (
+ weechat_config_string (new_remote->options[RELAY_REMOTE_OPTION_URL]));
+
relay_remotes_count++;
relay_remote_send_signal (new_remote);
@@ -409,6 +479,8 @@ struct t_relay_remote *
relay_remote_new_with_infolist (struct t_infolist *infolist)
{
struct t_relay_remote *new_remote;
+ Bytef *ptr_dict;
+ int dict_size;
new_remote = malloc (sizeof (*new_remote));
if (!new_remote)
@@ -419,8 +491,57 @@ relay_remote_new_with_infolist (struct t_infolist *infolist)
new_remote->port = weechat_infolist_integer (infolist, "port");
new_remote->tls = weechat_infolist_integer (infolist, "tls");
new_remote->status = weechat_infolist_integer (infolist, "status");
+ new_remote->password_hash_algo = weechat_infolist_integer (
+ infolist, "password_hash_algo");
+ new_remote->password_hash_iterations = weechat_infolist_integer (
+ infolist, "password_hash_iterations");
+ new_remote->totp = weechat_infolist_integer (infolist, "totp");
+ new_remote->websocket_key = strdup (weechat_infolist_string (infolist, "websocket_key"));
new_remote->sock = weechat_infolist_integer (infolist, "sock");
+ new_remote->hook_url_handshake = NULL;
+ new_remote->hook_connect = NULL;
+ new_remote->hook_fd = NULL;
new_remote->gnutls_sess = NULL;
+ new_remote->ws_deflate = relay_websocket_deflate_alloc ();
+ new_remote->ws_deflate->enabled = weechat_infolist_integer (infolist, "ws_deflate_enabled");
+ new_remote->ws_deflate->server_context_takeover = weechat_infolist_integer (infolist, "ws_deflate_server_context_takeover");
+ new_remote->ws_deflate->client_context_takeover = weechat_infolist_integer (infolist, "ws_deflate_client_context_takeover");
+ new_remote->ws_deflate->window_bits_deflate = weechat_infolist_integer (infolist, "ws_deflate_window_bits_deflate");
+ new_remote->ws_deflate->window_bits_inflate = weechat_infolist_integer (infolist, "ws_deflate_window_bits_inflate");
+ new_remote->ws_deflate->strm_deflate = NULL;
+ new_remote->ws_deflate->strm_inflate = NULL;
+ if (weechat_infolist_search_var (infolist, "ws_deflate_strm_deflate_dict"))
+ {
+ ptr_dict = weechat_infolist_buffer (infolist, "ws_deflate_strm_deflate_dict", &dict_size);
+ if (ptr_dict)
+ {
+ new_remote->ws_deflate->strm_deflate = calloc (1, sizeof (*new_remote->ws_deflate->strm_deflate));
+ if (new_remote->ws_deflate->strm_deflate)
+ {
+ if (relay_websocket_deflate_init_stream_deflate (new_remote->ws_deflate))
+ {
+ deflateSetDictionary (new_remote->ws_deflate->strm_deflate,
+ ptr_dict, dict_size);
+ }
+ }
+ }
+ }
+ if (weechat_infolist_search_var (infolist, "ws_deflate_strm_inflate_dict"))
+ {
+ ptr_dict = weechat_infolist_buffer (infolist, "ws_deflate_strm_inflate_dict", &dict_size);
+ if (ptr_dict)
+ {
+ new_remote->ws_deflate->strm_inflate = calloc (1, sizeof (*new_remote->ws_deflate->strm_inflate));
+ if (new_remote->ws_deflate->strm_inflate)
+ {
+ if (relay_websocket_deflate_init_stream_inflate (new_remote->ws_deflate))
+ {
+ inflateSetDictionary (new_remote->ws_deflate->strm_inflate,
+ ptr_dict, dict_size);
+ }
+ }
+ }
+ }
new_remote->prev_remote = NULL;
new_remote->next_remote = relay_remotes;
if (relay_remotes)
@@ -449,12 +570,40 @@ relay_remote_set_status (struct t_relay_remote *remote,
* a disconnected state for remote in infolist (used on /upgrade -save)
*/
+ if (remote->status == status)
+ return;
+
remote->status = status;
relay_remote_send_signal (remote);
}
/*
+ * Connects to a remote WeeChat relay/api.
+ *
+ * Returns:
+ * 1: OK
+ * 0: error
+ */
+
+int
+relay_remote_connect (struct t_relay_remote *remote)
+{
+#ifndef HAVE_CJSON
+ weechat_printf (NULL,
+ _("%s%s: error: unable to connect to remote relay via API "
+ "(cJSON support is not enabled)"),
+ weechat_prefix ("error"), RELAY_PLUGIN_NAME);
+ return 0;
+#endif /* HAVE_CJSON */
+
+ if (!remote)
+ return 0;
+
+ return relay_remote_network_connect (remote);
+}
+
+/*
* Renames a remote.
*
* Returns:
@@ -543,6 +692,7 @@ relay_remote_free (struct t_relay_remote *remote)
}
if (remote->address)
free (remote->address);
+ relay_websocket_deflate_free (remote->ws_deflate);
free (remote);
@@ -570,9 +720,7 @@ void
relay_remote_disconnect (struct t_relay_remote *remote)
{
if (remote->sock >= 0)
- {
- relay_remote_set_status (remote, RELAY_STATUS_DISCONNECTED);
- }
+ relay_remote_network_disconnect (remote);
}
/*
@@ -609,6 +757,8 @@ relay_remote_add_to_infolist (struct t_infolist *infolist,
int force_disconnected_state)
{
struct t_infolist_item *ptr_item;
+ Bytef *dict;
+ uInt dict_size;
if (!infolist || !remote)
return 0;
@@ -625,6 +775,14 @@ relay_remote_add_to_infolist (struct t_infolist *infolist,
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "tls", remote->tls))
return 0;
+ if (!weechat_infolist_new_var_integer (ptr_item, "password_hash_algo", remote->password_hash_algo))
+ return 0;
+ if (!weechat_infolist_new_var_integer (ptr_item, "password_hash_iterations", remote->password_hash_iterations))
+ return 0;
+ if (!weechat_infolist_new_var_integer (ptr_item, "totp", remote->totp))
+ return 0;
+ if (!weechat_infolist_new_var_string (ptr_item, "websocket_key", remote->websocket_key))
+ return 0;
if (!RELAY_STATUS_HAS_ENDED(remote->status) && force_disconnected_state)
{
if (!weechat_infolist_new_var_integer (ptr_item, "status", RELAY_STATUS_DISCONNECTED))
@@ -639,6 +797,33 @@ relay_remote_add_to_infolist (struct t_infolist *infolist,
if (!weechat_infolist_new_var_integer (ptr_item, "sock", remote->sock))
return 0;
}
+ if (remote->ws_deflate->strm_deflate || remote->ws_deflate->strm_inflate)
+ {
+ /* save the deflate/inflate dictionary, as it's required after /upgrade */
+ dict = malloc (32768);
+ if (dict)
+ {
+ if (remote->ws_deflate->strm_deflate)
+ {
+ if (deflateGetDictionary (remote->ws_deflate->strm_deflate, dict, &dict_size) == Z_OK)
+ {
+ weechat_infolist_new_var_buffer (ptr_item,
+ "ws_deflate_strm_deflate_dict",
+ dict, dict_size);
+ }
+ }
+ if (remote->ws_deflate->strm_inflate)
+ {
+ if (inflateGetDictionary (remote->ws_deflate->strm_inflate, dict, &dict_size) == Z_OK)
+ {
+ weechat_infolist_new_var_buffer (ptr_item,
+ "ws_deflate_strm_inflate_dict",
+ dict, dict_size);
+ }
+ }
+ free (dict);
+ }
+ }
return 1;
}
@@ -657,24 +842,32 @@ relay_remote_print_log ()
{
weechat_log_printf ("");
weechat_log_printf ("[relay remote (addr:0x%lx)]", ptr_remote);
- weechat_log_printf (" name. . . . . . . . . : '%s'", ptr_remote->name);
- weechat_log_printf (" url . . . . . . . . . : '%s'",
+ weechat_log_printf (" name. . . . . . . . . . : '%s'", ptr_remote->name);
+ weechat_log_printf (" url . . . . . . . . . . : '%s'",
weechat_config_string (ptr_remote->options[RELAY_REMOTE_OPTION_URL]));
- weechat_log_printf (" proxy . . . . . . . . : '%s'",
+ weechat_log_printf (" proxy . . . . . . . . . : '%s'",
weechat_config_string (ptr_remote->options[RELAY_REMOTE_OPTION_PROXY]));
- weechat_log_printf (" password. . . . . . . : '%s'",
+ weechat_log_printf (" password. . . . . . . . : '%s'",
weechat_config_string (ptr_remote->options[RELAY_REMOTE_OPTION_PASSWORD]));
- weechat_log_printf (" totp_secret . . . . . : '%s'",
+ weechat_log_printf (" totp_secret . . . . . . : '%s'",
weechat_config_string (ptr_remote->options[RELAY_REMOTE_OPTION_TOTP_SECRET]));
- weechat_log_printf (" address . . . . . . . : '%s'", ptr_remote->address);
- weechat_log_printf (" port. . . . . . . . . : %d", ptr_remote->port);
- weechat_log_printf (" tls . . . . . . . . . : %d", ptr_remote->tls);
- weechat_log_printf (" status. . . . . . . . : %d (%s)",
+ weechat_log_printf (" address . . . . . . . . : '%s'", ptr_remote->address);
+ weechat_log_printf (" port. . . . . . . . . . : %d", ptr_remote->port);
+ weechat_log_printf (" tls . . . . . . . . . . : %d", ptr_remote->tls);
+ weechat_log_printf (" status. . . . . . . . . : %d (%s)",
ptr_remote->status,
relay_status_string[ptr_remote->status]);
- weechat_log_printf (" sock. . . . . . . . . : %d", ptr_remote->sock);
- weechat_log_printf (" gnutls_sess . . . . . : 0x%lx", ptr_remote->gnutls_sess);
- weechat_log_printf (" prev_remote . . . . . : 0x%lx", ptr_remote->prev_remote);
- weechat_log_printf (" next_remote . . . . . : 0x%lx", ptr_remote->next_remote);
+ weechat_log_printf (" password_hash_algo. . . : %d", ptr_remote->password_hash_algo);
+ weechat_log_printf (" password_hash_iterations: %d", ptr_remote->password_hash_iterations);
+ weechat_log_printf (" totp. . . . . . . . . . : %d", ptr_remote->totp);
+ weechat_log_printf (" websocket_key . . . . . : 0x%ls", ptr_remote->websocket_key);
+ weechat_log_printf (" sock. . . . . . . . . . : %d", ptr_remote->sock);
+ weechat_log_printf (" hook_url_handshake. . . : 0x%lx", ptr_remote->hook_url_handshake);
+ weechat_log_printf (" hook_connect. . . . . . : 0x%lx", ptr_remote->hook_connect);
+ weechat_log_printf (" hook_fd . . . . . . . . : 0x%lx", ptr_remote->hook_fd);
+ weechat_log_printf (" gnutls_sess . . . . . . : 0x%lx", ptr_remote->gnutls_sess);
+ relay_websocket_deflate_print_log (ptr_remote->ws_deflate, "");
+ weechat_log_printf (" prev_remote . . . . . . : 0x%lx", ptr_remote->prev_remote);
+ weechat_log_printf (" next_remote . . . . . . : 0x%lx", ptr_remote->next_remote);
}
}
diff --git a/src/plugins/relay/relay-remote.h b/src/plugins/relay/relay-remote.h
index 2e6b938dc..b2f0b2cdb 100644
--- a/src/plugins/relay/relay-remote.h
+++ b/src/plugins/relay/relay-remote.h
@@ -22,6 +22,8 @@
#include <gnutls/gnutls.h>
+#define RELAY_REMOTE_DEFAULT_PORT 9000
+
enum t_relay_remote_option
{
RELAY_REMOTE_OPTION_URL = 0, /* remote URL */
@@ -42,8 +44,17 @@ struct t_relay_remote
int port; /* port number */
int tls; /* 1 if TLS is enabled */
enum t_relay_status status; /* status (connecting, active,..) */
+ int password_hash_algo; /* hash algo (from handshake) */
+ int password_hash_iterations; /* hash iterations (from handshake) */
+ int totp; /* TOTP enabled (from handshake) */
+ char *websocket_key; /* random key sent to the remote */
+ /* in the websocket handshake */
int sock; /* connected socket */
+ struct t_hook *hook_url_handshake; /* URL hook for the handshake */
+ struct t_hook *hook_connect; /* connection hook */
+ struct t_hook *hook_fd; /* hook for socket */
gnutls_session_t gnutls_sess; /* gnutls session (only if TLS used) */
+ struct t_relay_websocket_deflate *ws_deflate; /* websocket deflate data */
struct t_relay_remote *prev_remote;/* link to previous remote */
struct t_relay_remote *next_remote;/* link to next remote */
};
@@ -76,6 +87,7 @@ extern struct t_relay_remote *relay_remote_new (const char *name,
extern struct t_relay_remote *relay_remote_new_with_infolist (struct t_infolist *infolist);
extern void relay_remote_set_status (struct t_relay_remote *remote,
enum t_relay_status status);
+extern int relay_remote_connect (struct t_relay_remote *remote);
extern int relay_remote_rename (struct t_relay_remote *remote, const char *name);
extern void relay_remote_free (struct t_relay_remote *remote);
extern void relay_remote_free_all ();
diff --git a/src/plugins/relay/relay-websocket.c b/src/plugins/relay/relay-websocket.c
index 3ba4d408e..5259f471d 100644
--- a/src/plugins/relay/relay-websocket.c
+++ b/src/plugins/relay/relay-websocket.c
@@ -23,6 +23,7 @@
#include <unistd.h>
#include <stdio.h>
#include <string.h>
+#include <gcrypt.h>
#include <zlib.h>
#include "../weechat-plugin.h"
@@ -34,13 +35,6 @@
/*
- * globally unique identifier that is concatenated to HTTP header
- * "Sec-WebSocket-Key"
- */
-#define WEBSOCKET_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
-
-
-/*
* Allocates a t_relay_websocket_deflate structure.
*/
@@ -547,17 +541,23 @@ error:
* frame is first decompressed if "permessage-deflate" websocket extension
* is used).
*
+ * If argument "expect_masked_frame" is 1 and a frame is not masked,
+ * the function returns an error.
+ *
* Returns:
* 1: frame(s) decoded successfully
* 0: error decoding frame (connection must be closed if it happens)
*/
int
-relay_websocket_decode_frame (struct t_relay_client *client,
- const unsigned char *buffer,
+relay_websocket_decode_frame (const unsigned char *buffer,
unsigned long long buffer_length,
+ int expect_masked_frame,
+ struct t_relay_websocket_deflate *ws_deflate,
struct t_relay_websocket_frame **frames,
- int *num_frames)
+ int *num_frames,
+ char **partial_ws_frame,
+ int *partial_ws_frame_size)
{
unsigned long long i, index_buffer, index_buffer_start_frame;
unsigned long long length_frame_size, length_frame;
@@ -565,7 +565,7 @@ relay_websocket_decode_frame (struct t_relay_client *client,
size_t size_decompressed;
char *payload_decompressed;
struct t_relay_websocket_frame *frames2, *ptr_frame;
- int size;
+ int size, masked_frame, mask[4];
if (!buffer || !frames || !num_frames)
return 0;
@@ -586,11 +586,15 @@ relay_websocket_decode_frame (struct t_relay_client *client,
opcode = buffer[index_buffer] & 15;
+ /* check if frame is masked */
+ masked_frame = (buffer[index_buffer + 1] & 128) ? 1 : 0;
+
/*
- * check if frame is masked: client MUST send a masked frame; if frame is
- * not masked, we MUST reject it and close the connection (see RFC 6455)
+ * error if the frame is not masked and we expect it to be masked,
+ * in this case we must reject it and close the connection
+ * (see RFC 6455)
*/
- if (!(buffer[index_buffer + 1] & 128))
+ if (!masked_frame && expect_masked_frame)
return 0;
/* decode frame length */
@@ -611,15 +615,17 @@ relay_websocket_decode_frame (struct t_relay_client *client,
index_buffer += length_frame_size;
}
- /* read masks (4 bytes) */
- if (index_buffer + 4 > buffer_length)
- goto missing_data;
- int masks[4];
- for (i = 0; i < 4; i++)
+ if (masked_frame)
{
- masks[i] = (int)((unsigned char)buffer[index_buffer + i]);
+ /* read mask (4 bytes) */
+ if (index_buffer + 4 > buffer_length)
+ goto missing_data;
+ for (i = 0; i < 4; i++)
+ {
+ mask[i] = (int)((unsigned char)buffer[index_buffer + i]);
+ }
+ index_buffer += 4;
}
- index_buffer += 4;
/* check if we have enough data */
if ((length_frame > buffer_length)
@@ -646,13 +652,13 @@ relay_websocket_decode_frame (struct t_relay_client *client,
switch (opcode)
{
case WEBSOCKET_FRAME_OPCODE_PING:
- ptr_frame->opcode = RELAY_CLIENT_MSG_PING;
+ ptr_frame->opcode = RELAY_MSG_PING;
break;
case WEBSOCKET_FRAME_OPCODE_CLOSE:
- ptr_frame->opcode = RELAY_CLIENT_MSG_CLOSE;
+ ptr_frame->opcode = RELAY_MSG_CLOSE;
break;
default:
- ptr_frame->opcode = RELAY_CLIENT_MSG_STANDARD;
+ ptr_frame->opcode = RELAY_MSG_STANDARD;
break;
}
@@ -663,9 +669,16 @@ relay_websocket_decode_frame (struct t_relay_client *client,
ptr_frame->payload_size = length_frame;
/* fill payload */
- for (i = 0; i < length_frame; i++)
+ if (masked_frame)
+ {
+ for (i = 0; i < length_frame; i++)
+ {
+ ptr_frame->payload[i] = (int)((unsigned char)buffer[index_buffer + i]) ^ mask[i % 4];
+ }
+ }
+ else
{
- ptr_frame->payload[i] = (int)((unsigned char)buffer[index_buffer + i]) ^ masks[i % 4];
+ memcpy (ptr_frame->payload, buffer + index_buffer, length_frame);
}
ptr_frame->payload[length_frame] = '\0';
@@ -673,58 +686,58 @@ relay_websocket_decode_frame (struct t_relay_client *client,
* decompress data if frame is not empty and if "permessage-deflate"
* is enabled
*/
- if ((length_frame > 0) && client->ws_deflate->enabled)
+ if ((length_frame > 0) && ws_deflate && ws_deflate->enabled)
{
- if (!client->ws_deflate->strm_inflate)
+ if (!ws_deflate->strm_inflate)
{
- client->ws_deflate->strm_inflate = calloc (
- 1, sizeof (*client->ws_deflate->strm_inflate));
- if (!client->ws_deflate->strm_inflate)
+ ws_deflate->strm_inflate = calloc (
+ 1, sizeof (*ws_deflate->strm_inflate));
+ if (!ws_deflate->strm_inflate)
return 0;
- if (!relay_websocket_deflate_init_stream_inflate (client->ws_deflate))
+ if (!relay_websocket_deflate_init_stream_inflate (ws_deflate))
return 0;
}
payload_decompressed = relay_websocket_inflate (
ptr_frame->payload,
ptr_frame->payload_size,
- client->ws_deflate->strm_inflate,
+ ws_deflate->strm_inflate,
&size_decompressed);
if (!payload_decompressed)
return 0;
free (ptr_frame->payload);
ptr_frame->payload = payload_decompressed;
ptr_frame->payload_size = size_decompressed;
- if (!client->ws_deflate->client_context_takeover)
- relay_websocket_deflate_free_stream_inflate (client->ws_deflate);
+ if (!ws_deflate->client_context_takeover)
+ relay_websocket_deflate_free_stream_inflate (ws_deflate);
}
index_buffer += length_frame;
}
- if (client->partial_ws_frame)
+ if (*partial_ws_frame)
{
- free (client->partial_ws_frame);
- client->partial_ws_frame = NULL;
- client->partial_ws_frame_size = 0;
+ free (*partial_ws_frame);
+ *partial_ws_frame = NULL;
+ *partial_ws_frame_size = 0;
}
return 1;
missing_data:
- if (client->partial_ws_frame)
+ if (*partial_ws_frame)
{
- free (client->partial_ws_frame);
- client->partial_ws_frame = NULL;
- client->partial_ws_frame_size = 0;
+ free (*partial_ws_frame);
+ *partial_ws_frame = NULL;
+ *partial_ws_frame_size = 0;
}
size = buffer_length - index_buffer_start_frame;
if (size >= 0)
{
- client->partial_ws_frame = malloc (size);
- if (!client->partial_ws_frame)
+ *partial_ws_frame = malloc (size);
+ if (!*partial_ws_frame)
return 0;
- memcpy (client->partial_ws_frame, buffer + index_buffer_start_frame, size);
- client->partial_ws_frame_size = size;
+ memcpy (*partial_ws_frame, buffer + index_buffer_start_frame, size);
+ *partial_ws_frame_size = size;
}
return 1;
}
@@ -779,12 +792,16 @@ relay_websocket_deflate (const void *data, size_t size, z_stream *strm,
* Returns websocket frame, NULL if error.
* Argument "length_frame" is set with the length of frame built.
*
+ * Argument "mask_frame" must be 1 when sending to server (remote) and 0 when
+ * sending to a client.
+ *
* Note: result must be freed after use.
*/
char *
-relay_websocket_encode_frame (struct t_relay_client *client,
+relay_websocket_encode_frame (struct t_relay_websocket_deflate *ws_deflate,
int opcode,
+ int mask_frame,
const char *payload,
unsigned long long payload_size,
unsigned long long *length_frame)
@@ -792,8 +809,8 @@ relay_websocket_encode_frame (struct t_relay_client *client,
const char *ptr_data;
char *payload_compressed;
size_t size_compressed;
- unsigned char *frame;
- unsigned long long index, data_size;
+ unsigned char *frame, *ptr_mask;
+ unsigned long long i, index, data_size;
*length_frame = 0;
@@ -810,21 +827,22 @@ relay_websocket_encode_frame (struct t_relay_client *client,
if (((opcode == WEBSOCKET_FRAME_OPCODE_TEXT)
|| (opcode == WEBSOCKET_FRAME_OPCODE_BINARY))
&& (payload_size > 0)
- && client->ws_deflate->enabled)
+ && ws_deflate
+ && ws_deflate->enabled)
{
- if (!client->ws_deflate->strm_deflate)
+ if (!ws_deflate->strm_deflate)
{
- client->ws_deflate->strm_deflate = calloc (
- 1, sizeof (*client->ws_deflate->strm_deflate));
- if (!client->ws_deflate->strm_deflate)
+ ws_deflate->strm_deflate = calloc (
+ 1, sizeof (*ws_deflate->strm_deflate));
+ if (!ws_deflate->strm_deflate)
return NULL;
- if (!relay_websocket_deflate_init_stream_deflate (client->ws_deflate))
+ if (!relay_websocket_deflate_init_stream_deflate (ws_deflate))
return NULL;
}
payload_compressed = relay_websocket_deflate (
payload,
payload_size,
- client->ws_deflate->strm_deflate,
+ ws_deflate->strm_deflate,
&size_compressed);
if (!payload_compressed)
return NULL;
@@ -838,13 +856,13 @@ relay_websocket_encode_frame (struct t_relay_client *client,
{
data_size -= 4;
}
- if (!client->ws_deflate->server_context_takeover)
- relay_websocket_deflate_free_stream_deflate (client->ws_deflate);
+ if (!ws_deflate->server_context_takeover)
+ relay_websocket_deflate_free_stream_deflate (ws_deflate);
/* set bit RSV1: indicate permessage-deflate compressed data */
opcode |= 0x40;
}
- frame = malloc (data_size + 10);
+ frame = malloc (data_size + 14);
if (!frame)
{
if (payload_compressed)
@@ -884,9 +902,26 @@ relay_websocket_encode_frame (struct t_relay_client *client,
index = 10;
}
+ if (mask_frame)
+ {
+ frame[1] |= 128;
+ ptr_mask = frame + index;
+ gcry_create_nonce (ptr_mask, 4);
+ index += 4;
+ }
+
/* copy buffer after data_size */
memcpy (frame + index, ptr_data, data_size);
+ /* mask frame */
+ if (mask_frame)
+ {
+ for (i = 0; i < data_size; i++)
+ {
+ frame[index + i] = frame[index + i] ^ ptr_mask[i % 4];
+ }
+ }
+
*length_frame = index + data_size;
if (payload_compressed)
diff --git a/src/plugins/relay/relay-websocket.h b/src/plugins/relay/relay-websocket.h
index 6e48569fd..e9d09f3e6 100644
--- a/src/plugins/relay/relay-websocket.h
+++ b/src/plugins/relay/relay-websocket.h
@@ -24,6 +24,12 @@
#include <zlib.h>
+/*
+ * globally unique identifier that is concatenated to HTTP header
+ * "Sec-WebSocket-Key"
+ */
+#define WEBSOCKET_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
#define WEBSOCKET_FRAME_OPCODE_CONTINUATION 0x00
#define WEBSOCKET_FRAME_OPCODE_TEXT 0x01
#define WEBSOCKET_FRAME_OPCODE_BINARY 0x02
@@ -64,13 +70,17 @@ extern int relay_websocket_client_handshake_valid (struct t_relay_http_request *
extern void relay_websocket_parse_extensions (const char *extensions,
struct t_relay_websocket_deflate *ws_deflate);
extern char *relay_websocket_build_handshake (struct t_relay_http_request *request);
-extern int relay_websocket_decode_frame (struct t_relay_client *client,
- const unsigned char *buffer,
+extern int relay_websocket_decode_frame (const unsigned char *buffer,
unsigned long long length,
+ int expect_masked_frame,
+ struct t_relay_websocket_deflate *ws_deflate,
struct t_relay_websocket_frame **frames,
- int *num_frames);
-extern char *relay_websocket_encode_frame (struct t_relay_client *client,
+ int *num_frames,
+ char **partial_ws_frame,
+ int *partial_ws_frame_size);
+extern char *relay_websocket_encode_frame (struct t_relay_websocket_deflate *ws_deflate,
int opcode,
+ int mask_frame,
const char *payload,
unsigned long long payload_size,
unsigned long long *length_frame);
diff --git a/src/plugins/relay/relay.c b/src/plugins/relay/relay.c
index bf3188937..f643ec860 100644
--- a/src/plugins/relay/relay.c
+++ b/src/plugins/relay/relay.c
@@ -58,6 +58,8 @@ char *relay_status_name[] = /* name of status (for signal/info) */
{ "connecting", "waiting_auth",
"connected", "auth_failed", "disconnected"
};
+char *relay_msg_type_string[] = /* prefix in raw buffer for msg */
+{ "", "[PING]\n", "[PONG]\n", "[CLOSE]\n" };
struct t_hdata *relay_hdata_buffer = NULL;
struct t_hdata *relay_hdata_lines = NULL;
diff --git a/src/plugins/relay/relay.h b/src/plugins/relay/relay.h
index f933021b7..c78efea74 100644
--- a/src/plugins/relay/relay.h
+++ b/src/plugins/relay/relay.h
@@ -62,6 +62,18 @@ enum t_relay_status
RELAY_NUM_STATUS,
};
+/* type of message exchanged with the peer (client/remote) */
+
+enum t_relay_msg_type
+{
+ RELAY_MSG_STANDARD,
+ RELAY_MSG_PING,
+ RELAY_MSG_PONG,
+ RELAY_MSG_CLOSE,
+ /* number of message types */
+ RELAY_NUM_MSG_TYPES,
+};
+
#define RELAY_STATUS_HAS_ENDED(status) \
((status == RELAY_STATUS_AUTH_FAILED) || \
(status == RELAY_STATUS_DISCONNECTED))
@@ -74,6 +86,7 @@ enum t_relay_status
extern char *relay_protocol_string[];
extern char *relay_status_string[];
extern char *relay_status_name[];
+extern char *relay_msg_type_string[];
extern int relay_protocol_search (const char *name);
extern int relay_status_search (const char *name);
diff --git a/src/plugins/relay/weechat/relay-weechat-msg.c b/src/plugins/relay/weechat/relay-weechat-msg.c
index a1e373bdb..2985eb6e1 100644
--- a/src/plugins/relay/weechat/relay-weechat-msg.c
+++ b/src/plugins/relay/weechat/relay-weechat-msg.c
@@ -1116,7 +1116,7 @@ relay_weechat_msg_compress_zlib (struct t_relay_client *client,
msg->id);
/* send compressed data */
- relay_client_send (client, RELAY_CLIENT_MSG_STANDARD,
+ relay_client_send (client, RELAY_MSG_STANDARD,
(const char *)dest, dest_size + 5,
raw_message);
@@ -1189,7 +1189,7 @@ relay_weechat_msg_compress_zstd (struct t_relay_client *client,
msg->id);
/* send compressed data */
- relay_client_send (client, RELAY_CLIENT_MSG_STANDARD,
+ relay_client_send (client, RELAY_MSG_STANDARD,
(const char *)dest, comp_size + 5,
raw_message);
@@ -1250,7 +1250,7 @@ relay_weechat_msg_send (struct t_relay_client *client,
/* send uncompressed data */
snprintf (raw_message, sizeof (raw_message),
"obj: %d bytes, id: %s", msg->data_size, msg->id);
- relay_client_send (client, RELAY_CLIENT_MSG_STANDARD,
+ relay_client_send (client, RELAY_MSG_STANDARD,
msg->data, msg->data_size, raw_message);
}