bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
e9e5e84ffb2ce2e606a24ce6d930580367562ff0Timo Sirainen#include "lib.h"
633a3da9d3e9a5befd3405f6651043a6bdd327cbTimo Sirainen#include "array.h"
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include "llist.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "istream.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "ostream.h"
7a60e1dc9e93ef3f7c7fe1af6385a0bfa1e31bc3Timo Sirainen#include "strescape.h"
1b33e848e84e6f74aa0e3339c32fa96bc15102a2Timo Sirainen#include "settings-parser.h"
24ff367825286b52be4edb92df2fff0dd54cdf10Timo Sirainen#include "master-service.h"
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen#include "master-service-settings.h"
e9e5e84ffb2ce2e606a24ce6d930580367562ff0Timo Sirainen#include "config-request.h"
ec1a4f4306496380e9d96ee08a3718a669d0875aTimo Sirainen#include "config-parser.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "config-connection.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include <unistd.h>
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#define MAX_INBUF_SIZE 1024
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen#define CONFIG_CLIENT_PROTOCOL_MAJOR_VERSION 2
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#define CONFIG_CLIENT_PROTOCOL_MINOR_VERSION 0
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstruct config_connection {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen struct config_connection *prev, *next;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen int fd;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct istream *input;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct ostream *output;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct io *io;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool version_received:1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool handshaked:1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen};
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
c2d398246d2a5bb42beb083370695484142295f6Phil Carmodystatic struct config_connection *config_connections = NULL;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstatic const char *const *
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenconfig_connection_next_line(struct config_connection *conn)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const char *line;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen line = i_stream_next_line(conn->input);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (line == NULL)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return NULL;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
7a60e1dc9e93ef3f7c7fe1af6385a0bfa1e31bc3Timo Sirainen return t_strsplit_tabescaped(line);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
e9e5e84ffb2ce2e606a24ce6d930580367562ff0Timo Sirainenstatic void
e9e5e84ffb2ce2e606a24ce6d930580367562ff0Timo Sirainenconfig_request_output(const char *key, const char *value,
d84c270231a617298088f597474a73f4a14921aeTimo Sirainen enum config_key_type type ATTR_UNUSED, void *context)
e9e5e84ffb2ce2e606a24ce6d930580367562ff0Timo Sirainen{
e9e5e84ffb2ce2e606a24ce6d930580367562ff0Timo Sirainen struct ostream *output = context;
1b33e848e84e6f74aa0e3339c32fa96bc15102a2Timo Sirainen const char *p;
e9e5e84ffb2ce2e606a24ce6d930580367562ff0Timo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(output, key);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(output, "=");
1b33e848e84e6f74aa0e3339c32fa96bc15102a2Timo Sirainen while ((p = strchr(value, '\n')) != NULL) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(output, value, p-value);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(output, SETTING_STREAM_LF_CHAR, 1);
1b33e848e84e6f74aa0e3339c32fa96bc15102a2Timo Sirainen value = p+1;
1b33e848e84e6f74aa0e3339c32fa96bc15102a2Timo Sirainen }
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(output, value);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(output, "\n");
e9e5e84ffb2ce2e606a24ce6d930580367562ff0Timo Sirainen}
e9e5e84ffb2ce2e606a24ce6d930580367562ff0Timo Sirainen
ec1a4f4306496380e9d96ee08a3718a669d0875aTimo Sirainenstatic int config_connection_request(struct config_connection *conn,
ec1a4f4306496380e9d96ee08a3718a669d0875aTimo Sirainen const char *const *args)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen struct config_export_context *ctx;
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen struct master_service_settings_output output;
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen struct config_filter filter;
3b3a70990ab21c97140fcc2d1e3eb84c17d90388Timo Sirainen const char *path, *error, *module, *const *wanted_modules;
633a3da9d3e9a5befd3405f6651043a6bdd327cbTimo Sirainen ARRAY(const char *) modules;
633a3da9d3e9a5befd3405f6651043a6bdd327cbTimo Sirainen bool is_master = FALSE;
e9e5e84ffb2ce2e606a24ce6d930580367562ff0Timo Sirainen
c3c0a727bdc9b883af7ae098785643464d13e332Timo Sirainen /* [<args>] */
633a3da9d3e9a5befd3405f6651043a6bdd327cbTimo Sirainen t_array_init(&modules, 4);
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(&filter);
c3c0a727bdc9b883af7ae098785643464d13e332Timo Sirainen for (; *args != NULL; args++) {
c3c0a727bdc9b883af7ae098785643464d13e332Timo Sirainen if (strncmp(*args, "service=", 8) == 0)
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen filter.service = *args + 8;
633a3da9d3e9a5befd3405f6651043a6bdd327cbTimo Sirainen else if (strncmp(*args, "module=", 7) == 0) {
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen module = *args + 7;
633a3da9d3e9a5befd3405f6651043a6bdd327cbTimo Sirainen if (strcmp(module, "master") == 0)
633a3da9d3e9a5befd3405f6651043a6bdd327cbTimo Sirainen is_master = TRUE;
633a3da9d3e9a5befd3405f6651043a6bdd327cbTimo Sirainen array_append(&modules, &module, 1);
633a3da9d3e9a5befd3405f6651043a6bdd327cbTimo Sirainen } else if (strncmp(*args, "lname=", 6) == 0)
41942258112e4131de96b6a4399c1a8ac83a23cbTimo Sirainen filter.local_name = *args + 6;
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen else if (strncmp(*args, "lip=", 4) == 0) {
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen if (net_addr2ip(*args + 4, &filter.local_net) == 0) {
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen filter.local_bits =
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen IPADDR_IS_V4(&filter.local_net) ?
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen 32 : 128;
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen }
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen } else if (strncmp(*args, "rip=", 4) == 0) {
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen if (net_addr2ip(*args + 4, &filter.remote_net) == 0) {
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen filter.remote_bits =
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen IPADDR_IS_V4(&filter.remote_net) ?
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen 32 : 128;
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen }
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen }
c3c0a727bdc9b883af7ae098785643464d13e332Timo Sirainen }
633a3da9d3e9a5befd3405f6651043a6bdd327cbTimo Sirainen array_append_zero(&modules);
3b3a70990ab21c97140fcc2d1e3eb84c17d90388Timo Sirainen wanted_modules = array_count(&modules) == 1 ? NULL :
3b3a70990ab21c97140fcc2d1e3eb84c17d90388Timo Sirainen array_idx(&modules, 0);
3c00540a27bc1e7636ab2b52c3baae9ed9ec9aa6Timo Sirainen
633a3da9d3e9a5befd3405f6651043a6bdd327cbTimo Sirainen if (is_master) {
ec1a4f4306496380e9d96ee08a3718a669d0875aTimo Sirainen /* master reads configuration only when reloading settings */
ec1a4f4306496380e9d96ee08a3718a669d0875aTimo Sirainen path = master_service_get_config_path(master_service);
633a3da9d3e9a5befd3405f6651043a6bdd327cbTimo Sirainen if (config_parse_file(path, TRUE, NULL, &error) <= 0) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(conn->output,
384034c3a9acc3d557ecac00d52f5751b7a704c6Timo Sirainen t_strconcat("\nERROR ", error, "\n", NULL));
ec1a4f4306496380e9d96ee08a3718a669d0875aTimo Sirainen config_connection_destroy(conn);
ec1a4f4306496380e9d96ee08a3718a669d0875aTimo Sirainen return -1;
ec1a4f4306496380e9d96ee08a3718a669d0875aTimo Sirainen }
ec1a4f4306496380e9d96ee08a3718a669d0875aTimo Sirainen }
ec1a4f4306496380e9d96ee08a3718a669d0875aTimo Sirainen
3c00540a27bc1e7636ab2b52c3baae9ed9ec9aa6Timo Sirainen o_stream_cork(conn->output);
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen
3b3a70990ab21c97140fcc2d1e3eb84c17d90388Timo Sirainen ctx = config_export_init(wanted_modules, CONFIG_DUMP_SCOPE_SET, 0,
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen config_request_output, conn->output);
c0787d6ab19f4a17ec08699d0bbc77f13a9b02a9Timo Sirainen config_export_by_filter(ctx, &filter);
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen config_export_get_output(ctx, &output);
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen
194755bdfb97c07ca8b9df071099f68947b971e3Timo Sirainen if (output.specific_services != NULL) {
194755bdfb97c07ca8b9df071099f68947b971e3Timo Sirainen const char *const *s;
194755bdfb97c07ca8b9df071099f68947b971e3Timo Sirainen
194755bdfb97c07ca8b9df071099f68947b971e3Timo Sirainen for (s = output.specific_services; *s != NULL; s++) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(conn->output,
194755bdfb97c07ca8b9df071099f68947b971e3Timo Sirainen t_strdup_printf("service=%s\t", *s));
194755bdfb97c07ca8b9df071099f68947b971e3Timo Sirainen }
194755bdfb97c07ca8b9df071099f68947b971e3Timo Sirainen }
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen if (output.service_uses_local)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(conn->output, "service-uses-local\t");
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen if (output.service_uses_remote)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(conn->output, "service-uses-remote\t");
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen if (output.used_local)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(conn->output, "used-local\t");
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen if (output.used_remote)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(conn->output, "used-remote\t");
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(conn->output, "\n");
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen if (config_export_finish(&ctx) < 0) {
637ec4c33b4715737a41f7e58c9b6d1f693c27e2Timo Sirainen config_connection_destroy(conn);
637ec4c33b4715737a41f7e58c9b6d1f693c27e2Timo Sirainen return -1;
637ec4c33b4715737a41f7e58c9b6d1f693c27e2Timo Sirainen }
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(conn->output, "\n");
3c00540a27bc1e7636ab2b52c3baae9ed9ec9aa6Timo Sirainen o_stream_uncork(conn->output);
ec1a4f4306496380e9d96ee08a3718a669d0875aTimo Sirainen return 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
27a44fcfd8d19bffe0f267f20a2b5d3fe7600fddTimo Sirainenstatic void config_connection_input(struct config_connection *conn)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const char *const *args, *line;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen switch (i_stream_read(conn->input)) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen case -2:
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_error("BUG: Config client connection sent too much data");
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen config_connection_destroy(conn);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen case -1:
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen config_connection_destroy(conn);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (!conn->version_received) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen line = i_stream_next_line(conn->input);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (line == NULL)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
f158d9a303bb15a6848ca276c9391c7ca52e452bTimo Sirainen if (!version_string_verify(line, "config",
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen CONFIG_CLIENT_PROTOCOL_MAJOR_VERSION)) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_error("Config client not compatible with this server "
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen "(mixed old and new binaries?)");
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen config_connection_destroy(conn);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen conn->version_received = TRUE;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen while ((args = config_connection_next_line(conn)) != NULL) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (args[0] == NULL)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen continue;
ec1a4f4306496380e9d96ee08a3718a669d0875aTimo Sirainen if (strcmp(args[0], "REQ") == 0) {
ec1a4f4306496380e9d96ee08a3718a669d0875aTimo Sirainen if (config_connection_request(conn, args + 1) < 0)
ec1a4f4306496380e9d96ee08a3718a669d0875aTimo Sirainen break;
ec1a4f4306496380e9d96ee08a3718a669d0875aTimo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstruct config_connection *config_connection_create(int fd)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct config_connection *conn;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen conn = i_new(struct config_connection, 1);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen conn->fd = fd;
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE);
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi conn->output = o_stream_create_fd(fd, (size_t)-1);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_set_no_error_handling(conn->output, TRUE);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen conn->io = io_add(fd, IO_READ, config_connection_input, conn);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen DLLIST_PREPEND(&config_connections, conn);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return conn;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenvoid config_connection_destroy(struct config_connection *conn)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen DLLIST_REMOVE(&config_connections, conn);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen io_remove(&conn->io);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_stream_destroy(&conn->input);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen o_stream_destroy(&conn->output);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (close(conn->fd) < 0)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_error("close(config conn) failed: %m");
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_free(conn);
24ff367825286b52be4edb92df2fff0dd54cdf10Timo Sirainen
24ff367825286b52be4edb92df2fff0dd54cdf10Timo Sirainen master_service_client_connection_destroyed(master_service);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenvoid config_connections_destroy_all(void)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen while (config_connections != NULL)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen config_connection_destroy(config_connections);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen}