From a4bca275d651ca750224149945bc8fca5c80de77 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Sun, 17 Mar 2002 14:00:30 +0000 Subject: [PATCH] Added remote version control support to server. --- CHANGES | 16 ++++++ TODO | 18 ++++-- apps/silcd/protocol.c | 8 --- apps/silcd/server.c | 115 +++++++++++++------------------------- apps/silcd/server_util.c | 101 +++++++++++++++++++++++++++++++++ apps/silcd/server_util.h | 10 ++++ apps/silcd/serverconfig.c | 59 +++++++++++++------ apps/silcd/serverconfig.h | 3 + doc/example_silcd.conf.in | 32 +++++++++++ lib/silcske/silcske.c | 17 ++++++ lib/silcske/silcske.h | 28 ++++++++++ lib/silcutil/silclog.c | 5 +- lib/silcutil/silcutil.c | 92 ++++++++++++++++++++++++++++++ lib/silcutil/silcutil.h | 7 +++ 14 files changed, 403 insertions(+), 108 deletions(-) diff --git a/CHANGES b/CHANGES index d5e56ae7..aaa98c62 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,19 @@ +Sun Mar 17 15:44:56 EET 2002 Pekka Riikonen + + * Added functions silc_parse_version_string, silc_version_to_num, + and silc_ske_parse_version to parse SILC protocol style version + strings. Affected files lib/silcutil/silcutil.[ch] and + lib/silcske/silcske.[ch]. + + * Added new configuration params: version_protocol, version_software + and version_software_vendor to specify what version the remote + host must at least be to be able to connect to server. The vendor + string can be regex matched too. Added new function + silc_server_connection_allowed to check maximum number of allowed + connections, and allowed versions for incoming connections. + Affected files are silcd/server.c, server_util.[ch] and + serverconfig.[ch]. + Sun Mar 17 10:24:50 EET 2002 Pekka Riikonen * Added preliminary support for signals in scheduler. The diff --git a/TODO b/TODO index 57226a95..7ec1bc17 100644 --- a/TODO +++ b/TODO @@ -37,9 +37,6 @@ TODO/bugs In SILC Server o Configuration file additions: - o Add version handling, to allow, disallow certain versions to - connect. - o Add incoming connection frequency, incoming connection frequency for single IP address, key exchange frequency, key exchange frequency for single IP. Add also frequency base. @@ -94,8 +91,19 @@ TODO in Toolkit Documentation Stuff that needs to be done in order to complete the Tooolkit Reference Manual. - o Lots of ROBOdoc header formatting is undone in lib/silcutil, and - lib/silccrypt. + o ROBOdoc documenting missing from lib/silcutil/silcbuffer.h. + + o ROBOdoc documenting missing from lib/silcutil/silcdlist.h. + + o ROBOdoc documenting missing from lib/silcutil/silcfileutil.h. + + o ROBOdoc documenting missing from lib/silcutil/silcutil.h. + + o ROBOdoc documenting missing from lib/silccrypt/silchash.h. + + o ROBOdoc documenting missing from lib/silccrypt/silccipher.h. + + o ROBOdoc documenting missing from lib/silccrypt/silcpkcs.h. o Write "Programming with Toolkit" document, describing how to build Toolkit, how the build system works, where is everything, how diff --git a/apps/silcd/protocol.c b/apps/silcd/protocol.c index 2200bfb8..7cb72ada 100644 --- a/apps/silcd/protocol.c +++ b/apps/silcd/protocol.c @@ -371,14 +371,6 @@ SilcSKEStatus silc_ske_check_version(SilcSKE ske, unsigned char *version, if (maj != maj2) status = SILC_SKE_STATUS_BAD_VERSION; -#if 0 - if (min > min2) - status = SILC_SKE_STATUS_BAD_VERSION; -#endif - - /* XXX < 0.6 is not allowed */ - if (maj == 0 && min < 5) - status = SILC_SKE_STATUS_BAD_VERSION; if (status == SILC_SKE_STATUS_BAD_VERSION) SILC_LOG_ERROR(("%s (%s) %s is not allowed/supported version", diff --git a/apps/silcd/server.c b/apps/silcd/server.c index dec04e26..4dc47c92 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -1284,7 +1284,6 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) SilcUnknownEntry entry = (SilcUnknownEntry)sock->user_data; void *id_entry; SilcUInt32 hearbeat_timeout = server->config->param.keepalive_secs; - SilcUInt32 num_sockets; SILC_LOG_DEBUG(("Start")); @@ -1309,38 +1308,16 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) entry->data.last_receive = time(NULL); - num_sockets = silc_server_num_sockets_by_ip(server, sock->ip, - ctx->conn_type); - switch (ctx->conn_type) { case SILC_SOCKET_TYPE_CLIENT: { SilcClientEntry client; SilcServerConfigClient *conn = ctx->cconfig; - SilcUInt32 max_per_host = server->config->param.connections_max_per_host; - /* Check for maximum connections limit */ - if (conn->param) { - if (conn->param->connections_max && - server->stat.my_clients >= conn->param->connections_max) { - SILC_LOG_INFO(("Server is full, closing %s (%s) connection", - sock->hostname, sock->ip)); - silc_server_disconnect_remote(server, sock, - "Server closed connection: " - "Server is full, try again later"); - server->stat.auth_failures++; - goto out; - } - - max_per_host = conn->param->connections_max_per_host; - } - - if (num_sockets >= max_per_host) { - SILC_LOG_INFO(("Too many connections from %s (%s), closing connection", - sock->hostname, sock->ip)); - silc_server_disconnect_remote(server, sock, - "Server closed connection: " - "Too many connections from your host"); + /* Verify whether this connection is after all allowed to connect */ + if (!silc_server_connection_allowed(server, sock, ctx->conn_type, + &server->config->param, + conn->param, ctx->ske)) { server->stat.auth_failures++; goto out; } @@ -1390,69 +1367,55 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) SilcUInt16 backup_replace_port = 0; SilcServerConfigServer *sconn = ctx->sconfig; SilcServerConfigRouter *rconn = ctx->rconfig; - SilcUInt32 max_per_host = server->config->param.connections_max_per_host; - - if (ctx->conn_type == SILC_SOCKET_TYPE_ROUTER && rconn) { - if (rconn->param) { - /* Check for maximum connections limit */ - if (rconn->param->connections_max && - server->stat.my_routers >= rconn->param->connections_max) { - silc_server_disconnect_remote(server, sock, - "Server closed connection: " - "Server is full, try again later"); - server->stat.auth_failures++; - goto out; - } - max_per_host = rconn->param->connections_max_per_host; - if (rconn->param->keepalive_secs) - hearbeat_timeout = rconn->param->keepalive_secs; + if (ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) { + /* Verify whether this connection is after all allowed to connect */ + if (!silc_server_connection_allowed(server, sock, ctx->conn_type, + &server->config->param, + rconn ? rconn->param : NULL, + ctx->ske)) { + server->stat.auth_failures++; + goto out; } - initiator = rconn->initiator; - backup_local = rconn->backup_local; - backup_router = rconn->backup_router; - backup_replace_ip = rconn->backup_replace_ip; - backup_replace_port = rconn->backup_replace_port; - } - - if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER && sconn) { - if (sconn->param) { - /* Check for maximum connections limit */ - if (sconn->param->connections_max && - server->stat.my_servers >= sconn->param->connections_max) { - SILC_LOG_INFO(("Server is full, closing %s (%s) connection", - sock->hostname, sock->ip)); - silc_server_disconnect_remote(server, sock, - "Server closed connection: " - "Server is full, try again later"); - server->stat.auth_failures++; - goto out; + if (rconn) { + if (rconn->param) { + if (rconn->param->keepalive_secs) + hearbeat_timeout = rconn->param->keepalive_secs; } - max_per_host = sconn->param->connections_max_per_host; - if (sconn->param->keepalive_secs) - hearbeat_timeout = sconn->param->keepalive_secs; + initiator = rconn->initiator; + backup_local = rconn->backup_local; + backup_router = rconn->backup_router; + backup_replace_ip = rconn->backup_replace_ip; + backup_replace_port = rconn->backup_replace_port; } - - backup_router = sconn->backup_router; } - if (num_sockets >= max_per_host) { - SILC_LOG_INFO(("Too many connections from %s (%s), closing connection", - sock->hostname, sock->ip)); - silc_server_disconnect_remote(server, sock, - "Server closed connection: " - "Too many connections from your host"); - server->stat.auth_failures++; - goto out; + if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER) { + /* Verify whether this connection is after all allowed to connect */ + if (!silc_server_connection_allowed(server, sock, ctx->conn_type, + &server->config->param, + sconn ? sconn->param : NULL, + ctx->ske)) { + server->stat.auth_failures++; + goto out; + } + if (sconn) { + if (sconn->param) { + if (sconn->param->keepalive_secs) + hearbeat_timeout = sconn->param->keepalive_secs; + } + + backup_router = sconn->backup_router; + } } SILC_LOG_DEBUG(("Remote host is %s", ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? "server" : (backup_router ? "backup router" : "router"))); - SILC_LOG_INFO(("Connection s (%s) is %s", sock->hostname, + SILC_LOG_INFO(("Connection %s (%s) is %s", sock->hostname, sock->ip, ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? "server" : (backup_router ? "backup router" : "router"))); diff --git a/apps/silcd/server_util.c b/apps/silcd/server_util.c index dc76c481..529b75de 100644 --- a/apps/silcd/server_util.c +++ b/apps/silcd/server_util.c @@ -856,3 +856,104 @@ SilcPublicKey silc_server_get_public_key(SilcServer server, return cached_key; } + +/* Check whether the connection `sock' is allowed to connect to us. This + checks for example whether there is too much connections for this host, + and required version for the host etc. */ + +bool silc_server_connection_allowed(SilcServer server, + SilcSocketConnection sock, + SilcSocketType type, + SilcServerConfigConnParams *global, + SilcServerConfigConnParams *params, + SilcSKE ske) +{ + SilcUInt32 conn_number = (type == SILC_SOCKET_TYPE_CLIENT ? + server->stat.my_clients : + type == SILC_SOCKET_TYPE_SERVER ? + server->stat.my_servers : + server->stat.my_routers); + SilcUInt32 num_sockets, max_hosts, max_per_host; + SilcUInt32 r_protocol_version, l_protocol_version; + SilcUInt32 r_software_version, l_software_version; + char *r_vendor_version = NULL, *l_vendor_version; + + /* Check version */ + + l_protocol_version = + silc_version_to_num(params && params->version_protocol ? + params->version_protocol : + global->version_protocol); + l_software_version = + silc_version_to_num(params && params->version_software ? + params->version_software : + global->version_software); + l_vendor_version = (params && params->version_software_vendor ? + params->version_software_vendor : + global->version_software_vendor); + + if (ske && silc_ske_parse_version(ske, &r_protocol_version, NULL, + &r_software_version, NULL, + &r_vendor_version)) { + /* Match protocol version */ + if (l_protocol_version && r_protocol_version && + r_protocol_version < l_protocol_version) { + SILC_LOG_INFO(("Connection %s (%s) is too old version", + sock->hostname, sock->ip)); + silc_server_disconnect_remote(server, sock, + "Server closed connection: " + "You support too old protocol version"); + return FALSE; + } + + /* Math software version */ + if (l_software_version && r_software_version && + r_software_version < l_software_version) { + SILC_LOG_INFO(("Connection %s (%s) is too old version", + sock->hostname, sock->ip)); + silc_server_disconnect_remote(server, sock, + "Server closed connection: " + "You support too old software version"); + return FALSE; + } + + /* Regex match vendor version */ + if (l_vendor_version && r_vendor_version && + !silc_string_match(l_vendor_version, r_vendor_version)) { + SILC_LOG_INFO(("Connection %s (%s) is unsupported version", + sock->hostname, sock->ip)); + silc_server_disconnect_remote(server, sock, + "Server closed connection: " + "Your software is not supported"); + return FALSE; + } + } + silc_free(r_vendor_version); + + /* Check for maximum connections limit */ + + num_sockets = silc_server_num_sockets_by_ip(server, sock->ip, type); + max_hosts = (params ? params->connections_max : global->connections_max); + max_per_host = (params ? params->connections_max_per_host : + global->connections_max_per_host); + + if (max_hosts && conn_number >= max_hosts) { + SILC_LOG_INFO(("Server is full, closing %s (%s) connection", + sock->hostname, sock->ip)); + silc_server_disconnect_remote(server, sock, + "Server closed connection: " + "Server is full, try again later"); + return FALSE; + } + + if (num_sockets >= max_per_host) { + SILC_LOG_INFO(("Too many connections from %s (%s), closing connection", + sock->hostname, sock->ip)); + silc_server_disconnect_remote(server, sock, + "Server closed connection: " + "Too many connections from your host"); + return FALSE; + } + + return TRUE; +} diff --git a/apps/silcd/server_util.h b/apps/silcd/server_util.h index ca54b1b8..4feb4ee5 100644 --- a/apps/silcd/server_util.h +++ b/apps/silcd/server_util.h @@ -101,4 +101,14 @@ SilcPublicKey silc_server_find_public_key(SilcServer server, SilcPublicKey silc_server_get_public_key(SilcServer server, SilcHashTable local_public_keys); +/* Check whether the connection `sock' is allowed to connect to us. This + checks for example whether there is too much connections for this host, + and required version for the host etc. */ +bool silc_server_connection_allowed(SilcServer server, + SilcSocketConnection sock, + SilcSocketType type, + SilcServerConfigConnParams *global, + SilcServerConfigConnParams *params, + SilcSKE ske); + #endif /* SERVER_UTIL_H */ diff --git a/apps/silcd/serverconfig.c b/apps/silcd/serverconfig.c index 2e4773c7..2153870e 100644 --- a/apps/silcd/serverconfig.c +++ b/apps/silcd/serverconfig.c @@ -74,24 +74,17 @@ static void my_set_param_defaults(SilcServerConfigConnParams *params, SilcServerConfigConnParams *defaults) { -#define SET_PARAM_DEFAULT(p, d) \ +#define SET_PARAM_DEFAULT(p, d) params->p = \ (params->p ? params->p : (defaults && defaults->p ? defaults->p : d)) - params->connections_max = - SET_PARAM_DEFAULT(connections_max, SILC_SERVER_MAX_CONNECTIONS); - params->connections_max_per_host = - SET_PARAM_DEFAULT(connections_max_per_host, - SILC_SERVER_MAX_CONNECTIONS_SINGLE); - params->keepalive_secs = - SET_PARAM_DEFAULT(keepalive_secs, SILC_SERVER_KEEPALIVE); - params->reconnect_count = - SET_PARAM_DEFAULT(reconnect_count, SILC_SERVER_RETRY_COUNT); - params->reconnect_interval = - SET_PARAM_DEFAULT(reconnect_interval, SILC_SERVER_RETRY_INTERVAL_MIN); - params->reconnect_interval_max = - SET_PARAM_DEFAULT(reconnect_interval_max, SILC_SERVER_RETRY_INTERVAL_MAX); - params->key_exchange_rekey = - SET_PARAM_DEFAULT(key_exchange_rekey, SILC_SERVER_REKEY); + SET_PARAM_DEFAULT(connections_max, SILC_SERVER_MAX_CONNECTIONS); + SET_PARAM_DEFAULT(connections_max_per_host, + SILC_SERVER_MAX_CONNECTIONS_SINGLE); + SET_PARAM_DEFAULT(keepalive_secs, SILC_SERVER_KEEPALIVE); + SET_PARAM_DEFAULT(reconnect_count, SILC_SERVER_RETRY_COUNT); + SET_PARAM_DEFAULT(reconnect_interval, SILC_SERVER_RETRY_INTERVAL_MIN); + SET_PARAM_DEFAULT(reconnect_interval_max, SILC_SERVER_RETRY_INTERVAL_MAX); + SET_PARAM_DEFAULT(key_exchange_rekey, SILC_SERVER_REKEY); } /* Find connection parameters by the parameter block name. */ @@ -203,6 +196,21 @@ SILC_CONFIG_CALLBACK(fetch_generic) else if (!strcmp(name, "conn_auth_timeout")) { config->conn_auth_timeout = (SilcUInt32) *(int *)val; } + else if (!strcmp(name, "version_protocol")) { + CONFIG_IS_DOUBLE(config->param.version_protocol); + config->param.version_protocol = + (*(char *)val ? strdup((char *) val) : NULL); + } + else if (!strcmp(name, "version_software")) { + CONFIG_IS_DOUBLE(config->param.version_software); + config->param.version_software = + (*(char *)val ? strdup((char *) val) : NULL); + } + else if (!strcmp(name, "version_software_vendor")) { + CONFIG_IS_DOUBLE(config->param.version_software_vendor);; + config->param.version_software_vendor = + (*(char *)val ? strdup((char *) val) : NULL); + } else return SILC_CONFIG_EINTERNAL; @@ -626,6 +634,19 @@ SILC_CONFIG_CALLBACK(fetch_connparam) else if (!strcmp(name, "key_exchange_pfs")) { tmp->key_exchange_pfs = *(bool *)val; } + else if (!strcmp(name, "version_protocol")) { + CONFIG_IS_DOUBLE(tmp->version_protocol); + tmp->version_protocol = (*(char *)val ? strdup((char *) val) : NULL); + } + else if (!strcmp(name, "version_software")) { + CONFIG_IS_DOUBLE(tmp->version_software); + tmp->version_software = (*(char *)val ? strdup((char *) val) : NULL); + } + else if (!strcmp(name, "version_software_vendor")) { + CONFIG_IS_DOUBLE(tmp->version_software_vendor);; + tmp->version_software_vendor = + (*(char *)val ? strdup((char *) val) : NULL); + } else return SILC_CONFIG_EINTERNAL; @@ -1002,6 +1023,9 @@ static const SilcConfigTable table_general[] = { { "channel_rekey_secs", SILC_CONFIG_ARG_INT, fetch_generic, NULL }, { "key_exchange_timeout", SILC_CONFIG_ARG_INT, fetch_generic, NULL }, { "conn_auth_timeout", SILC_CONFIG_ARG_INT, fetch_generic, NULL }, + { "version_protocol", SILC_CONFIG_ARG_STR, fetch_generic, NULL }, + { "version_software", SILC_CONFIG_ARG_STR, fetch_generic, NULL }, + { "version_software_vendor", SILC_CONFIG_ARG_STR, fetch_generic, NULL }, { 0, 0, 0, 0 } }; @@ -1079,6 +1103,9 @@ static const SilcConfigTable table_connparam[] = { { "reconnect_keep_trying", SILC_CONFIG_ARG_TOGGLE, fetch_connparam, NULL }, { "key_exchange_rekey", SILC_CONFIG_ARG_INT, fetch_connparam, NULL }, { "key_exchange_pfs", SILC_CONFIG_ARG_TOGGLE, fetch_connparam, NULL }, + { "version_protocol", SILC_CONFIG_ARG_STR, fetch_connparam, NULL }, + { "version_software", SILC_CONFIG_ARG_STR, fetch_connparam, NULL }, + { "version_software_vendor", SILC_CONFIG_ARG_STR, fetch_connparam, NULL }, { 0, 0, 0, 0 } }; diff --git a/apps/silcd/serverconfig.h b/apps/silcd/serverconfig.h index 2af85bbd..b27c7b65 100644 --- a/apps/silcd/serverconfig.h +++ b/apps/silcd/serverconfig.h @@ -82,6 +82,9 @@ typedef struct SilcServerConfigConnParams { bool reconnect_keep_trying; SilcUInt32 key_exchange_rekey; bool key_exchange_pfs; + char *version_protocol; + char *version_software; + char *version_software_vendor; struct SilcServerConfigConnParams *next; } SilcServerConfigConnParams; diff --git a/doc/example_silcd.conf.in b/doc/example_silcd.conf.in index d32f0612..bf0a81b4 100644 --- a/doc/example_silcd.conf.in +++ b/doc/example_silcd.conf.in @@ -59,6 +59,22 @@ General { # refused. This can be overridden with ConnectionParams. #connections_max_per_host = 10; + # Required version of the remote. If these are specified then the + # remote must be of at least this version, or newer. If older then + # the connection will not be allowed. + # + # version_protocol - SILC protocol version ("majog.minor") + # version_software - software version ("major.minor") + # version_software_vendor - vendor specific version extension + # + # The version_software_vendor may be for example a string or a build + # number of the software. The string can be a regex string to match + # more widely. Usually the vendor version checking is not necessary + # and can be omitted. These can be overridden with ConnectionParams. + #version_protocol = "1.0"; + #version_software = "1.3"; + #version_software_vendor = "SomeVendor"; + # Default keepalive frequency (seconds). This can be overridden # with ConnectionParams. keepalive_secs = 300; @@ -256,6 +272,22 @@ ConnectionParams { # connections it is recommended that this value is set to one (1). connections_max_per_host = 10; + # Required version of the remote. If these are specified then the + # remote must be of at least this version, or newer. If older then + # the connection will not be allowed. + # + # version_protocol - SILC protocol version + # version_software - software version + # version_software_vendor - vendor specific version extension + # + # The version_software_vendor may be for example a string or a build + # number of the software. The string can be a regex string to match + # more widely. Usually the vendor version checking is not necessary + # and can be omitted. + #version_protocol = "1.0"; + #version_software = "1.3"; + #version_software_vendor = "SomeVendor"; + # Keepalive frequency (seconds). keepalive_secs = 300; diff --git a/lib/silcske/silcske.c b/lib/silcske/silcske.c index 836a4fb8..56b38eb4 100644 --- a/lib/silcske/silcske.c +++ b/lib/silcske/silcske.c @@ -1993,3 +1993,20 @@ const char *silc_ske_map_status(SilcSKEStatus status) return ""; } + +/* Parses remote host's version string. */ + +bool silc_ske_parse_version(SilcSKE ske, + SilcUInt32 *protocol_version, + char **protocol_version_string, + SilcUInt32 *software_version, + char **software_version_string, + char **vendor_version) +{ + return silc_parse_version_string(ske->start_payload->version, + protocol_version, + protocol_version_string, + software_version, + software_version_string, + vendor_version); +} diff --git a/lib/silcske/silcske.h b/lib/silcske/silcske.h index ffb25b0a..7d0aab9e 100644 --- a/lib/silcske/silcske.h +++ b/lib/silcske/silcske.h @@ -892,4 +892,32 @@ silc_ske_process_key_material_data(unsigned char *data, ***/ void silc_ske_free_key_material(SilcSKEKeyMaterial *key); +/****f* silcske/SilcSKEAPI/silc_ske_parse_version + * + * SYNOPSIS + * + * bool silc_ske_parse_version(SilcSKE ske, + * SilcUInt32 *protocol_version, + * char **protocol_version_string, + * SilcUInt32 *software_version, + * char **software_version_string, + * char **vendor_version); + * + * DESCRIPTION + * + * This utility function can be used to parse the remote host's version + * string. This returns the protocol version, and software version into + * the `protocol_version', `software_version' and `vendor_version' pointers + * if they are provided. The string versions of the versions are saved + * in *_string pointers if they are provided. Returns TRUE if the version + * string was successfully parsed. + * + ***/ +bool silc_ske_parse_version(SilcSKE ske, + SilcUInt32 *protocol_version, + char **protocol_version_string, + SilcUInt32 *software_version, + char **software_version_string, + char **vendor_version); + #endif /* !SILCSKE_H */ diff --git a/lib/silcutil/silclog.c b/lib/silcutil/silclog.c index 06e5771f..3d371749 100644 --- a/lib/silcutil/silclog.c +++ b/lib/silcutil/silclog.c @@ -484,9 +484,8 @@ void silc_log_reset_debug_callbacks() void silc_log_set_debug_string(const char *debug_string) { silc_free(silc_log_debug_string); - if ((strchr(debug_string, '(') && - strchr(debug_string, ')')) || - strchr(debug_string, '$')) + if ((strchr(debug_string, '(') && strchr(debug_string, ')')) || + strchr(debug_string, '$')) silc_log_debug_string = strdup(debug_string); else silc_log_debug_string = silc_string_regexify(debug_string); diff --git a/lib/silcutil/silcutil.c b/lib/silcutil/silcutil.c index 6ef7c576..82068283 100644 --- a/lib/silcutil/silcutil.c +++ b/lib/silcutil/silcutil.c @@ -859,3 +859,95 @@ bool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len) return TRUE; } + +/* Parses SILC protocol style version string. */ + +bool silc_parse_version_string(const char *version, + SilcUInt32 *protocol_version, + char **protocol_version_string, + SilcUInt32 *software_version, + char **software_version_string, + char **vendor_version) +{ + char *cp, buf[32]; + int maj = 0, min = 0; + + if (!strstr(version, "SILC-")) + return FALSE; + + cp = (char *)version + 5; + if (!cp) + return FALSE; + + /* Take protocol version */ + + maj = atoi(cp); + cp = strchr(cp, '.'); + if (cp) { + min = atoi(cp + 1); + cp++; + } + + memset(buf, 0, sizeof(buf)); + snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min); + if (protocol_version) + *protocol_version = atoi(buf); + memset(buf, 0, sizeof(buf)); + snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min); + if (protocol_version_string) + *protocol_version_string = strdup(buf); + + /* Take software version */ + + maj = 0; + min = 0; + cp = strchr(cp, '-'); + if (!cp) + return FALSE; + + maj = atoi(cp + 1); + cp = strchr(cp, '.'); + if (cp) + min = atoi(cp + 1); + + memset(buf, 0, sizeof(buf)); + snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min); + if (software_version) + *software_version = atoi(buf); + memset(buf, 0, sizeof(buf)); + snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min); + if (software_version_string) + *software_version_string = strdup(buf); + + /* Take vendor string */ + + cp++; + if (cp) { + cp = strchr(cp, '.'); + if (cp && cp + 1 && vendor_version) + *vendor_version = strdup(cp + 1); + } + + return TRUE; +} + +/* Converts version string x.x into number representation. */ + +SilcUInt32 silc_version_to_num(const char *version) +{ + int maj = 0, min = 0; + char *cp, buf[32]; + + if (!version) + return 0; + + cp = (char *)version; + maj = atoi(cp); + cp = strchr(cp, '.'); + if (cp) + min = atoi(cp + 1); + + memset(buf, 0, sizeof(buf)); + snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min); + return (SilcUInt32)atoi(buf); +} diff --git a/lib/silcutil/silcutil.h b/lib/silcutil/silcutil.h index 2d6fa7f3..aa4112bc 100644 --- a/lib/silcutil/silcutil.h +++ b/lib/silcutil/silcutil.h @@ -62,5 +62,12 @@ char *silc_client_chumode_char(SilcUInt32 mode); int silc_gettimeofday(struct timeval *p); char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len); bool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len); +bool silc_parse_version_string(const char *version, + SilcUInt32 *protocol_version, + char **protocol_version_string, + SilcUInt32 *software_version, + char **software_version_string, + char **vendor_version); +SilcUInt32 silc_version_to_num(const char *version); #endif -- 2.24.0