diff --git a/src/lib/core/util.c b/src/lib/core/util.c
index 0576574b0d684543f52ed99369b4135d1f8220e6..5970fdf81ea518be4dfa39ed670f9f43ab08e001 100644
--- a/src/lib/core/util.c
+++ b/src/lib/core/util.c
@@ -510,3 +510,22 @@ strtolowerdup(const char *s)
 	lowercase[len] = '\0';
 	return lowercase;
 }
+
+char *
+strtoupper(char *s)
+{
+	for (size_t i = 0; s[i] != '\0'; ++i)
+		s[i] = (char)toupper(s[i]);
+	return s;
+}
+
+char *
+strtoupperdup(const char *s)
+{
+	size_t len = strlen(s);
+	char *uppercase = xmalloc(len + 1);
+	for (size_t i = 0; i < len; ++i)
+		uppercase[i] = (char)toupper(s[i]);
+	uppercase[len] = '\0';
+	return uppercase;
+}
diff --git a/src/trivia/util.h b/src/trivia/util.h
index 70477c8cc1bfdc1512e89e9f20736068bc69e69e..ccfc9cd40df98bc0e64603e62f5be24be0d9dbb5 100644
--- a/src/trivia/util.h
+++ b/src/trivia/util.h
@@ -748,6 +748,17 @@ strtolower(char *s);
 char *
 strtolowerdup(const char *s);
 
+/** Returns the null-terminated string converted to upper case in-place. */
+char *
+strtoupper(char *s);
+
+/**
+ * Returns a copy of the null-terminated string converted to upper case. The
+ * result is dynamically allocated using `xmalloc`.
+ */
+char *
+strtoupperdup(const char *s);
+
 #if !defined(__cplusplus) && !defined(static_assert)
 # define static_assert _Static_assert
 #endif
diff --git a/test/unit/string.c b/test/unit/string.c
index 467e72809675d0b722f937757d7b86c778f79b23..312143501ca19f4fdea5b0fa7e0cc7a558aa3ae9 100644
--- a/test/unit/string.c
+++ b/test/unit/string.c
@@ -5,7 +5,8 @@
 #include "unit.h"
 
 static const char *const test_lower_case_conv_expected = "str";
-static const char *const test_lower_case_conv_input[] = {
+static const char *const test_upper_case_conv_expected = "STR";
+static const char *const test_case_conv_input[] = {
 	"str", "Str", "sTr", "stR", "STr", "sTR", "StR", "STR"
 };
 
@@ -50,15 +51,15 @@ static void
 test_strtolowerdup(void)
 {
 	header();
-	plan(lengthof(test_lower_case_conv_input) * 2);
+	plan(lengthof(test_case_conv_input) * 2);
 
-	for (size_t i = 0; i < lengthof(test_lower_case_conv_input); ++i) {
-		char *test = strtolowerdup(test_lower_case_conv_input[i]);
-		isnt(test, test_lower_case_conv_input[i],
-		     "a copy of %s is returned", test_lower_case_conv_input[i]);
+	for (size_t i = 0; i < lengthof(test_case_conv_input); ++i) {
+		char *test = strtolowerdup(test_case_conv_input[i]);
+		isnt(test, test_case_conv_input[i],
+		     "a copy of %s is returned", test_case_conv_input[i]);
 		is(strcmp(test_lower_case_conv_expected, test), 0,
 		   "%s is converted to lower case correctly",
-		   test_lower_case_conv_input[i]);
+		   test_case_conv_input[i]);
 		free(test);
 	}
 
@@ -70,15 +71,55 @@ static void
 test_strtolower(void)
 {
 	header();
-	plan(lengthof(test_lower_case_conv_input) * 2);
+	plan(lengthof(test_case_conv_input) * 2);
 
-	for (size_t i = 0; i < lengthof(test_lower_case_conv_input); ++i) {
-		char *cp = xstrdup(test_lower_case_conv_input[i]);
+	for (size_t i = 0; i < lengthof(test_case_conv_input); ++i) {
+		char *cp = xstrdup(test_case_conv_input[i]);
 		char *test = strtolower(cp);
 		is(test, cp, "%s is converted in-place", cp);
 		is(strcmp(test_lower_case_conv_expected, test), 0,
 		   "%s is converted to lower case correctly",
-		   test_lower_case_conv_input[i]);
+		   test_case_conv_input[i]);
+		free(cp);
+	}
+
+	footer();
+	check_plan();
+}
+
+static void
+test_strtoupperdup(void)
+{
+	header();
+	plan(lengthof(test_case_conv_input) * 2);
+
+	for (size_t i = 0; i < lengthof(test_case_conv_input); ++i) {
+		char *test = strtoupperdup(test_case_conv_input[i]);
+		isnt(test, test_case_conv_input[i],
+		     "a copy of %s is returned", test_case_conv_input[i]);
+		is(strcmp(test_upper_case_conv_expected, test), 0,
+		   "%s is converted to upper case correctly",
+		   test_case_conv_input[i]);
+		free(test);
+	}
+
+	footer();
+	check_plan();
+}
+
+static void
+test_strtoupper(void)
+{
+	header();
+	plan(lengthof(test_case_conv_input) * 2);
+
+	for (size_t i = 0; i < lengthof(test_case_conv_input); ++i) {
+		char *cp = xstrdup(test_case_conv_input[i]);
+		char *test = strtoupper(cp);
+		is(test, cp, "%s is converted in-place", cp);
+		is(strcmp(test_upper_case_conv_expected, test), 0,
+		   "%s is converted to upper case correctly",
+		   test_case_conv_input[i]);
 		free(cp);
 	}
 
@@ -89,12 +130,14 @@ test_strtolower(void)
 int
 main(void)
 {
-	plan(3);
+	plan(5);
 	header();
 
 	test_strlcat();
 	test_strtolowerdup();
 	test_strtolower();
+	test_strtoupperdup();
+	test_strtoupper();
 
 	footer();
 	return check_plan();