static gboolean on_handle_stream_socket (CockpitWebServer *server, const gchar *path, GIOStream *io_stream, GHashTable *headers, GByteArray *input, guint in_length, gpointer user_data) { CockpitTransport *transport; const gchar *query = NULL; CockpitCreds *creds; CockpitPipe *pipe; gchar *value; gchar **env; if (!g_str_has_prefix (path, "/cockpit/socket")) return FALSE; if (path[15] == '?') query = path + 16; else if (path[15] != '\0') return FALSE; if (service) { g_object_ref (service); } else { value = g_strdup_printf ("%d", server_port); env = g_environ_setenv (g_get_environ (), "COCKPIT_TEST_SERVER_PORT", value, TRUE); creds = cockpit_creds_new (g_get_user_name (), "test", COCKPIT_CRED_CSRF_TOKEN, "myspecialtoken", NULL); pipe = cockpit_pipe_spawn ((const gchar **)bridge_argv, (const gchar **)env, NULL, FALSE); transport = cockpit_pipe_transport_new (pipe); service = cockpit_web_service_new (creds, transport); cockpit_creds_unref (creds); g_object_unref (transport); g_object_unref (pipe); g_free (value); g_strfreev (env); /* Clear the pointer automatically when service is done */ g_object_add_weak_pointer (G_OBJECT (service), (gpointer *)&service); } if (query) cockpit_channel_socket_open (service, "/cockpit/socket", query, io_stream, headers, input); else cockpit_web_service_socket (service, "/cockpit/socket", io_stream, headers, input); /* Keeps ref on itself until it closes */ g_object_unref (service); return TRUE; }
gboolean cockpit_handler_external (CockpitWebServer *server, const gchar *original_path, const gchar *path, GIOStream *io_stream, GHashTable *headers, GByteArray *input, CockpitHandlerData *ws) { CockpitWebResponse *response = NULL; CockpitWebService *service = NULL; const gchar *segment = NULL; JsonObject *open = NULL; const gchar *query = NULL; CockpitCreds *creds; const gchar *expected; const gchar *upgrade; guchar *decoded; GBytes *bytes; gsize length; gsize seglen; /* The path must start with /cockpit+xxx/channel/csrftoken? or similar */ if (path && path[0]) segment = strchr (path + 1, '/'); if (!segment) return FALSE; if (!g_str_has_prefix (segment, "/channel/")) return FALSE; segment += 9; /* Make sure we are authenticated, otherwise 404 */ service = cockpit_auth_check_cookie (ws->auth, path, headers); if (!service) return FALSE; creds = cockpit_web_service_get_creds (service); g_return_val_if_fail (creds != NULL, FALSE); expected = cockpit_creds_get_csrf_token (creds); g_return_val_if_fail (expected != NULL, FALSE); /* The end of the token */ query = strchr (segment, '?'); if (query) { seglen = query - segment; query += 1; } else { seglen = strlen (segment); query = ""; } /* No such path is valid */ if (strlen (expected) != seglen || memcmp (expected, segment, seglen) != 0) { g_message ("invalid csrf token"); return FALSE; } decoded = g_base64_decode (query, &length); if (decoded) { bytes = g_bytes_new_take (decoded, length); if (!cockpit_transport_parse_command (bytes, NULL, NULL, &open)) { open = NULL; g_message ("invalid external channel query"); } g_bytes_unref (bytes); } if (!open) { response = cockpit_web_response_new (io_stream, original_path, path, NULL, headers); cockpit_web_response_error (response, 400, NULL, NULL); g_object_unref (response); } else { upgrade = g_hash_table_lookup (headers, "Upgrade"); if (upgrade && g_ascii_strcasecmp (upgrade, "websocket") == 0) { cockpit_channel_socket_open (service, open, original_path, path, io_stream, headers, input); } else { response = cockpit_web_response_new (io_stream, original_path, path, NULL, headers); cockpit_channel_response_open (service, headers, response, open); g_object_unref (response); } json_object_unref (open); } g_object_unref (service); return TRUE; }
static gboolean on_handle_stream_external (CockpitWebServer *server, const gchar *path, GIOStream *io_stream, GHashTable *headers, GByteArray *input, gpointer user_data) { CockpitWebResponse *response; gboolean handled = FALSE; const gchar *upgrade; CockpitCreds *creds; const gchar *expected; const gchar *query; const gchar *segment; JsonObject *open = NULL; GBytes *bytes; guchar *decoded; gsize length; gsize seglen; if (g_str_has_prefix (path, "/cockpit/echosocket")) { const gchar *protocols[] = { "cockpit1", NULL }; const gchar *origins[2] = { NULL, NULL }; WebSocketConnection *ws = NULL; gchar *url; url = g_strdup_printf ("ws://localhost:%u%s", server_port, path); origins[0] = g_strdup_printf ("http://localhost:%u", server_port); ws = web_socket_server_new_for_stream (url, (const gchar **)origins, protocols, io_stream, headers, input); g_signal_connect (ws, "message", G_CALLBACK (on_echo_socket_message), NULL); g_signal_connect (ws, "close", G_CALLBACK (on_echo_socket_close), NULL); return TRUE; } if (!g_str_has_prefix (path, "/cockpit/channel/")) return FALSE; /* Remove /cockpit/channel/ part */ segment = path + 17; if (service) { creds = cockpit_web_service_get_creds (service); g_return_val_if_fail (creds != NULL, FALSE); expected = cockpit_creds_get_csrf_token (creds); g_return_val_if_fail (expected != NULL, FALSE); /* The end of the token */ query = strchr (segment, '?'); if (!query) query = segment + strlen (segment); /* No such path is valid */ seglen = query - segment; if (strlen(expected) == seglen && memcmp (expected, segment, seglen) == 0) { decoded = g_base64_decode (query, &length); if (decoded) { bytes = g_bytes_new_take (decoded, length); if (!cockpit_transport_parse_command (bytes, NULL, NULL, &open)) { open = NULL; g_message ("invalid external channel query"); } g_bytes_unref (bytes); } } if (open) { upgrade = g_hash_table_lookup (headers, "Upgrade"); if (upgrade && g_ascii_strcasecmp (upgrade, "websocket") == 0) { cockpit_channel_socket_open (service, open, path, io_stream, headers, input); handled = TRUE; } else { response = cockpit_web_response_new (io_stream, path, NULL, headers); cockpit_channel_response_open (service, headers, response, open); g_object_unref (response); handled = TRUE; } json_object_unref (open); } } return handled; }