diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index a7661670d7323c73dd5362a36d1691cf24690b2c..9d9208edddab443c92e796b92490cd92b4f7372d 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -95,8 +95,10 @@ include_directories(${EXTRA_BOX_INCLUDE_DIRS}) add_library(box_error STATIC error.cc errcode.c mp_error.cc) target_link_libraries(box_error core stat mpstream vclock) +add_library(node_name STATIC node_name.c) + add_library(xrow STATIC xrow.c iproto_constants.c iproto_features.c) -target_link_libraries(xrow server core small vclock misc box_error +target_link_libraries(xrow server core small vclock misc box_error node_name ${MSGPUCK_LIBRARIES}) set(tuple_sources @@ -324,6 +326,6 @@ add_custom_command(OUTPUT ${SQL_BIN_DIR}/opcodes.c ${SQL_BIN_DIR}/opcodes.h) target_link_libraries(box box_error tuple stat xrow xlog vclock crc32 raft - ${common_libraries}) + node_name ${common_libraries}) add_dependencies(box build_bundled_libs generate_sql_files) diff --git a/src/box/node_name.c b/src/box/node_name.c new file mode 100644 index 0000000000000000000000000000000000000000..4443fbd2be5b4dc8fc57b4fa3c16d95a727ba089 --- /dev/null +++ b/src/box/node_name.c @@ -0,0 +1,24 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright 2010-2023, Tarantool AUTHORS, please see AUTHORS file. + */ +#include "node_name.h" + +#include <ctype.h> + +bool +node_name_is_valid_n(const char *name, size_t len) +{ + if (len == 0 || len > NODE_NAME_LEN_MAX || !isalpha(*name)) + return false; + const char *end = name + len; + while (name < end) { + char c = *(name++); + if (!isalnum(c) && c != '-') + return false; + if (tolower(c) != c) + return false; + } + return true; +} diff --git a/src/box/node_name.h b/src/box/node_name.h new file mode 100644 index 0000000000000000000000000000000000000000..c11fdee1818855eefc8f98e848e95db030bdb115 --- /dev/null +++ b/src/box/node_name.h @@ -0,0 +1,55 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright 2010-2023, Tarantool AUTHORS, please see AUTHORS file. + */ +#pragma once + +#include <assert.h> +#include <stdbool.h> +#include <stddef.h> +#include <string.h> + +/** + * Name suitable for a node visible in the network. Its format matches the + * sub-domain label in RFC 1035, section 2.3.1 + * (https://www.rfc-editor.org/rfc/rfc1035#section-2.3.1). + * + * It allows to use the node name as a sub-domain and a host name. + * + * The limitations are: max 63 symbols (not including term 0); only lowercase + * letters, digits, and hyphen. Can start only with a letter. Note that the + * sub-domain name rules say that uppercase is allowed but the names are + * case-insensitive. In Tarantool the lowercase is enforced. + */ + +enum { + NODE_NAME_LEN_MAX = 63, + NODE_NAME_SIZE_MAX = NODE_NAME_LEN_MAX + 1, +}; + +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + +/** Check if the node name of the given length is valid. */ +bool +node_name_is_valid_n(const char *name, size_t len); + +static inline bool +node_name_is_valid(const char *name) +{ + return node_name_is_valid_n(name, strnlen(name, NODE_NAME_SIZE_MAX)); +} + +static inline const char * +node_name_str(const char *name) +{ + if (name == NULL || *name == 0) + return "<no-name>"; + return name; +} + +#if defined(__cplusplus) +} +#endif /* defined(__cplusplus) */ diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 24f45f201d2d71f15e4798ebe14dd71a4d588284..4649c6e897da2fc1b87a6c7a3bfe49d046e9b925 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -600,3 +600,8 @@ create_unit_test(PREFIX lua_tweaks ${LIBYAML_LIBRARIES} ${READLINE_LIBRARIES} ) + +create_unit_test(PREFIX node_name + SOURCES node_name.c core_test_utils.c + LIBRARIES core unit node_name +) diff --git a/test/unit/node_name.c b/test/unit/node_name.c new file mode 100644 index 0000000000000000000000000000000000000000..d17d4d3ffee7c67379962bb8b3f1ae5028ce775a --- /dev/null +++ b/test/unit/node_name.c @@ -0,0 +1,79 @@ +#include "node_name.h" + +#include "trivia/util.h" +#define UNIT_TAP_COMPATIBLE 1 +#include "unit.h" + +static void +test_node_name_is_valid(void) +{ + header(); + plan(27); + + const char *bad_names[] = { + "", + "1", + "1abc", + "*", + "a_b", + "aBcD", + "a~b", + "{ab}", + }; + for (int i = 0; i < (int)lengthof(bad_names); ++i) { + const char *str = bad_names[i]; + ok(!node_name_is_valid(str), "bad name %d", i); + ok(!node_name_is_valid_n(str, strlen(str)), + "bad name n %d", i); + } + const char *good_names[] = { + "a", + "a-b-c", + "abc", + "a1b2c3-d4-e5-", + }; + for (int i = 0; i < (int)lengthof(good_names); ++i) { + const char *str = good_names[i]; + ok(node_name_is_valid(str), "bad name %d", i); + ok(node_name_is_valid_n(str, strlen(str)), + "bad name n %d", i); + } + char name[NODE_NAME_SIZE_MAX + 1]; + memset(name, 'a', sizeof(name)); + ok(!node_name_is_valid_n(name, NODE_NAME_SIZE_MAX), "max + 1"); + ok(node_name_is_valid_n(name, NODE_NAME_LEN_MAX), "max n"); + name[NODE_NAME_SIZE_MAX - 1] = 0; + ok(node_name_is_valid(name), "max"); + + check_plan(); + footer(); +} + +static void +test_node_name_str(void) +{ + header(); + plan(3); + + const char *stub = "<no-name>"; + is(strcmp(node_name_str("abc"), "abc"), 0, "name"); + is(strcmp(node_name_str(""), stub), 0, "empty"); + is(strcmp(node_name_str(NULL), stub), 0, "null"); + + check_plan(); + footer(); +} + +int +main(void) +{ + header(); + plan(2); + + test_node_name_is_valid(); + test_node_name_str(); + + int rc = check_plan(); + footer(); + return rc; +}