diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fdfee9ec..e2e774ea5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ SPDX-License-Identifier: GPL-3.0-or-later ### Fixed +- api: fix parsing of date/times with timezone offset in function util_parse_time - relay/api: fix crash when an invalid HTTP request is received from a client ## Version 4.7.1 (2025-08-16) diff --git a/src/core/core-util.c b/src/core/core-util.c index 574b67dcc..b3487fa99 100644 --- a/src/core/core-util.c +++ b/src/core/core-util.c @@ -349,8 +349,8 @@ util_strftimeval (char *string, int max, const char *format, struct timeval *tv) int util_parse_time (const char *datetime, struct timeval *tv) { - char *string, *pos, *pos2, *pos_colon, *pos_hyphen, str_usec[16], *error; - char str_date[128], str_date2[256]; + char *string, *pos, *pos2, *pos_colon, *pos_hyphen, *pos_dot; + char str_usec[16], *error, str_date[128], str_date2[256]; struct tm tm_date, tm_date_gm, tm_date_local, *local_time; time_t time_now, time_gm, time_local; long long value; @@ -370,6 +370,7 @@ util_parse_time (const char *datetime, struct timeval *tv) pos_colon = strchr (datetime, ':'); pos_hyphen = strchr (datetime, '-'); + pos_dot = strchr (datetime, '.'); if (pos_colon && !pos_hyphen) { /* add current date: "21:01:02" -> "2024-01-04T21:01:02" */ @@ -381,7 +382,7 @@ util_parse_time (const char *datetime, struct timeval *tv) strftime (str_date, sizeof (str_date), "%Y-%m-%dT", local_time); snprintf (string, sizeof (str_date2), "%s%s", str_date, datetime); } - else if (!pos_colon && pos_hyphen) + else if (!pos_colon && pos_hyphen && (!pos_dot || (pos_hyphen < pos_dot))) { /* add time (midnight): "2024-01-04" -> "2024-01-04T00:00:00" */ string = malloc (strlen (datetime) + 16 + 1); @@ -436,7 +437,7 @@ util_parse_time (const char *datetime, struct timeval *tv) memmove (pos, pos2, strlen (pos2) + 1); } - /* extract timezone and remove it from string2 */ + /* extract timezone and remove it from string */ pos = strrchr (string, 'Z'); if (pos) { @@ -451,7 +452,7 @@ util_parse_time (const char *datetime, struct timeval *tv) pos = strchr (string, ' '); if (pos) { - pos2 = strchr (pos, '+'); + pos2 = strrchr (pos, '+'); if (pos2) { pos2[0] = '\0'; @@ -460,7 +461,7 @@ util_parse_time (const char *datetime, struct timeval *tv) } else { - pos2 = strchr (pos, '-'); + pos2 = strrchr (pos, '-'); if (pos2) { pos2[0] = '\0'; @@ -491,109 +492,65 @@ util_parse_time (const char *datetime, struct timeval *tv) } } - if (strchr (string, '-')) + if (strchr (string, '-') && strchr (string, ':')) { - if (strchr (string, ':')) + if (strchr (string, 'T')) { - if (strchr (string, 'T')) + /* ISO 8601 format like: "2024-01-04T21:01:02" */ + /* initialize structure, because strptime does not do it */ + memset (&tm_date, 0, sizeof (struct tm)); + pos = strptime (string, "%Y-%m-%dT%H:%M:%S", &tm_date); + if (pos && (tm_date.tm_year > 0)) { - /* ISO 8601 format like: "2024-01-04T21:01:02.123Z" */ - /* initialize structure, because strptime does not do it */ - memset (&tm_date, 0, sizeof (struct tm)); - pos = strptime (string, "%Y-%m-%dT%H:%M:%S", &tm_date); - if (pos && (tm_date.tm_year > 0)) + if (use_local_time) { - if (use_local_time) - { - tv->tv_sec = mktime (&tm_date); - } - else - { - /* convert to UTC and add timezone_offset */ - time_now = mktime (&tm_date); - gmtime_r (&time_now, &tm_date_gm); - localtime_r (&time_now, &tm_date_local); - time_gm = mktime (&tm_date_gm); - time_local = mktime (&tm_date_local); - tv->tv_sec = mktime (&tm_date_local) - + (time_local - time_gm) - + timezone_offset; - } - rc = 1; + tm_date.tm_isdst = -1; + tv->tv_sec = mktime (&tm_date); } - } - else - { - /* like ISO 8601 but with space like: "2024-01-04 21:01:02.123Z" */ - /* initialize structure, because strptime does not do it */ - memset (&tm_date, 0, sizeof (struct tm)); - pos = strptime (string, "%Y-%m-%d %H:%M:%S", &tm_date); - if (pos && (tm_date.tm_year > 0)) + else { - if (use_local_time) - { - tv->tv_sec = mktime (&tm_date); - } - else - { - /* convert to UTC and add timezone_offset */ - time_now = mktime (&tm_date); - gmtime_r (&time_now, &tm_date_gm); - localtime_r (&time_now, &tm_date_local); - time_gm = mktime (&tm_date_gm); - time_local = mktime (&tm_date_local); - tv->tv_sec = mktime (&tm_date_local) - + (time_local - time_gm) - + timezone_offset; - } - rc = 1; + /* convert to UTC and add timezone_offset */ + time_now = mktime (&tm_date); + gmtime_r (&time_now, &tm_date_gm); + localtime_r (&time_now, &tm_date_local); + time_gm = mktime (&tm_date_gm); + time_local = mktime (&tm_date_local); + tv->tv_sec = mktime (&tm_date_local) + + (time_local - time_gm) + - timezone_offset; } + rc = 1; } } else { - /* ISO 8601 format like: "2024-01-04" */ + /* like ISO 8601 but with space like: "2024-01-04 21:01:02" */ /* initialize structure, because strptime does not do it */ memset (&tm_date, 0, sizeof (struct tm)); - pos = strptime (string, "%Y-%m-%d", &tm_date); + pos = strptime (string, "%Y-%m-%d %H:%M:%S", &tm_date); if (pos && (tm_date.tm_year > 0)) { - tv->tv_sec = mktime (&tm_date); + if (use_local_time) + { + tm_date.tm_isdst = -1; + tv->tv_sec = mktime (&tm_date); + } + else + { + /* convert to UTC and add timezone_offset */ + time_now = mktime (&tm_date); + gmtime_r (&time_now, &tm_date_gm); + localtime_r (&time_now, &tm_date_local); + time_gm = mktime (&tm_date_gm); + time_local = mktime (&tm_date_local); + tv->tv_sec = mktime (&tm_date_local) + + (time_local - time_gm) + - timezone_offset; + } rc = 1; } } } - else if (strchr (string, ':')) - { - /* hour format like: "21:01:02" */ - time_now = time (NULL); - local_time = localtime (&time_now); - strftime (str_date, sizeof (str_date), "%Y-%m-%dT", local_time); - snprintf (str_date2, sizeof (str_date2), "%s%s", str_date, string); - /* initialize structure, because strptime does not do it */ - memset (&tm_date, 0, sizeof (struct tm)); - pos = strptime (str_date2, "%Y-%m-%dT%H:%M:%S", &tm_date); - if (pos) - { - if (use_local_time) - { - tv->tv_sec = mktime (&tm_date); - } - else - { - /* convert to UTC and add timezone_offset */ - time_now = mktime (&tm_date); - gmtime_r (&time_now, &tm_date_gm); - localtime_r (&time_now, &tm_date_local); - time_gm = mktime (&tm_date_gm); - time_local = mktime (&tm_date_local); - tv->tv_sec = mktime (&tm_date_local) - + (time_local - time_gm) - + timezone_offset; - } - rc = 1; - } - } else { /* timestamp format: "1704402062" */ diff --git a/tests/unit/core/test-core-util.cpp b/tests/unit/core/test-core-util.cpp index f7c808bdc..f175c2bf7 100644 --- a/tests/unit/core/test-core-util.cpp +++ b/tests/unit/core/test-core-util.cpp @@ -291,8 +291,9 @@ TEST(CoreUtil, ParseTime) time_t date; char str_time[128]; - /* test with local timezone: UTC+1 */ - setenv ("TZ", "UTC+1", 1); + /* test with UTC timezone */ + setenv ("TZ", "UTC", 1); + tzset (); LONGS_EQUAL(0, util_parse_time (NULL, NULL)); LONGS_EQUAL(0, util_parse_time (NULL, &tv)); @@ -305,9 +306,8 @@ TEST(CoreUtil, ParseTime) /* * expected: 2023-12-25T00:00:00Z == 1703462400 - * (local timezone UTC+1: 1703466000) */ - WEE_PARSE_DATE(1, 1703466000, 0, "2023-12-25"); + WEE_PARSE_DATE(1, 1703462400, 0, "2023-12-25"); /* expected: current date with specified local time */ date = time (NULL); @@ -329,42 +329,42 @@ TEST(CoreUtil, ParseTime) local_time = localtime (&date); strftime (str_time, sizeof (str_time), "%H:%M:%S+0100", local_time); LONGS_EQUAL(1, util_parse_time (str_time, &tv)); - CHECK((tv.tv_sec >= date) && (tv.tv_sec <= date + 10)); + CHECK((tv.tv_sec >= date - 3600) && (tv.tv_sec <= date - 3600 + 10)); LONGS_EQUAL(0, tv.tv_usec); date = time (NULL); local_time = localtime (&date); strftime (str_time, sizeof (str_time), "%H:%M:%S +01:00", local_time); LONGS_EQUAL(1, util_parse_time (str_time, &tv)); - CHECK((tv.tv_sec >= date) && (tv.tv_sec <= date + 10)); + CHECK((tv.tv_sec >= date - 3600) && (tv.tv_sec <= date - 3600 + 10)); LONGS_EQUAL(0, tv.tv_usec); date = time (NULL); local_time = localtime (&date); strftime (str_time, sizeof (str_time), "%H:%M:%S +02:00", local_time); LONGS_EQUAL(1, util_parse_time (str_time, &tv)); - CHECK((tv.tv_sec >= date + 3600) && (tv.tv_sec <= date + 3600 + 10)); + CHECK((tv.tv_sec >= date - 7200) && (tv.tv_sec <= date - 7200 + 10)); LONGS_EQUAL(0, tv.tv_usec); date = time (NULL); local_time = localtime (&date); strftime (str_time, sizeof (str_time), "%H:%M:%S.456789+0100", local_time); LONGS_EQUAL(1, util_parse_time (str_time, &tv)); - CHECK((tv.tv_sec >= date) && (tv.tv_sec <= date + 10)); + CHECK((tv.tv_sec >= date - 3600) && (tv.tv_sec <= date - 3600 + 10)); LONGS_EQUAL(456789, tv.tv_usec); date = time (NULL); local_time = localtime (&date); strftime (str_time, sizeof (str_time), "%H:%M:%S.456789 +01:00", local_time); LONGS_EQUAL(1, util_parse_time (str_time, &tv)); - CHECK((tv.tv_sec >= date) && (tv.tv_sec <= date + 10)); + CHECK((tv.tv_sec >= date - 3600) && (tv.tv_sec <= date - 3600 + 10)); LONGS_EQUAL(456789, tv.tv_usec); date = time (NULL); local_time = localtime (&date); strftime (str_time, sizeof (str_time), "%H:%M:%S.456789 +02:00", local_time); LONGS_EQUAL(1, util_parse_time (str_time, &tv)); - CHECK((tv.tv_sec >= date + 3600) && (tv.tv_sec <= date + 3600 + 10)); + CHECK((tv.tv_sec >= date - 7200) && (tv.tv_sec <= date - 7200 + 10)); LONGS_EQUAL(456789, tv.tv_usec); /* expected: current date with specified UTC time */ @@ -372,31 +372,29 @@ TEST(CoreUtil, ParseTime) local_time = localtime (&date); strftime (str_time, sizeof (str_time), "%H:%M:%SZ", local_time); LONGS_EQUAL(1, util_parse_time (str_time, &tv)); - CHECK((tv.tv_sec >= date - 3600) && (tv.tv_sec <= date - 3600 + 10)); + CHECK((tv.tv_sec >= date) && (tv.tv_sec <= date + 10)); LONGS_EQUAL(0, tv.tv_usec); date = time (NULL); local_time = localtime (&date); strftime (str_time, sizeof (str_time), "%H:%M:%S.456789Z", local_time); LONGS_EQUAL(1, util_parse_time (str_time, &tv)); - CHECK((tv.tv_sec >= date - 3600) && (tv.tv_sec <= date - 3600 + 10)); + CHECK((tv.tv_sec >= date) && (tv.tv_sec <= date + 10)); LONGS_EQUAL(456789, tv.tv_usec); /* * expected: 2023-12-25T10:29:09.456789Z == 1703500149.456789 - * (local timezone UTC+1: 1703503749.456789) */ - WEE_PARSE_DATE(1, 1703500149 + 3600, 0, "2023-12-25T10:29:09"); + WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25T10:29:09"); WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25T10:29:09Z"); WEE_PARSE_DATE(1, 1703500149, 456000, "2023-12-25T10:29:09.456Z"); WEE_PARSE_DATE(1, 1703500149, 456789, "2023-12-25T10:29:09.456789Z"); /* * expected: 2023-12-25T10:29:09.456789Z == 1703500149.456789 - * (local timezone UTC+1: 1703503749.456789) * with space instead of "T" */ - WEE_PARSE_DATE(1, 1703500149 + 3600, 0, "2023-12-25 10:29:09"); + WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09"); WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09Z"); WEE_PARSE_DATE(1, 1703500149, 456000, "2023-12-25 10:29:09.456Z"); WEE_PARSE_DATE(1, 1703500149, 456789, "2023-12-25 10:29:09.456789Z"); @@ -413,20 +411,20 @@ TEST(CoreUtil, ParseTime) WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25T10:29:09-00:00"); WEE_PARSE_DATE(1, 1703500149, 456789, "2023-12-25T10:29:09.456789-00:00"); - WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25T10:29:09+02"); - WEE_PARSE_DATE(1, 1703500149 + 60, 0, "2023-12-25T10:29:09+0001"); - WEE_PARSE_DATE(1, 1703500149 + 60, 456789, "2023-12-25T10:29:09.456789+0001"); + WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25T12:29:09+02"); + WEE_PARSE_DATE(1, 1703500149 - 60, 0, "2023-12-25T10:29:09+0001"); + WEE_PARSE_DATE(1, 1703500149 - 60, 456789, "2023-12-25T10:29:09.456789+0001"); - WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25T10:29:09+0200"); - WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25T10:29:09+02:00"); - WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25T10:29:09+0130"); - WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25T10:29:09+01:30"); - WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 456789, "2023-12-25T10:29:09.456789+01:30"); + WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25T10:29:09+0200"); + WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25T10:29:09+02:00"); + WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25T10:29:09+0130"); + WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25T10:29:09+01:30"); + WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 456789, "2023-12-25T10:29:09.456789+01:30"); - WEE_PARSE_DATE(1, 1703500149 - 60, 0, "2023-12-25T10:29:09-0001"); - WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25T10:29:09-0130"); - WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25T10:29:09-01:30"); - WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 456789, "2023-12-25T10:29:09.456789-01:30"); + WEE_PARSE_DATE(1, 1703500149 + 60, 0, "2023-12-25T10:29:09-0001"); + WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25T10:29:09-0130"); + WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25T10:29:09-01:30"); + WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 456789, "2023-12-25T10:29:09.456789-01:30"); /* * expected: 2023-12-25T10:29:09.456789Z == 1703500149.456789 @@ -440,20 +438,20 @@ TEST(CoreUtil, ParseTime) WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09-00:00"); WEE_PARSE_DATE(1, 1703500149, 456789, "2023-12-25 10:29:09.456789-00:00"); - WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25 10:29:09+02"); - WEE_PARSE_DATE(1, 1703500149 + 60, 0, "2023-12-25 10:29:09+0001"); - WEE_PARSE_DATE(1, 1703500149 + 60, 456789, "2023-12-25 10:29:09.456789+0001"); + WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25 10:29:09+02"); + WEE_PARSE_DATE(1, 1703500149 - 60, 0, "2023-12-25 10:29:09+0001"); + WEE_PARSE_DATE(1, 1703500149 - 60, 456789, "2023-12-25 10:29:09.456789+0001"); - WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25 10:29:09+0200"); - WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25 10:29:09+02:00"); - WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09+0130"); - WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09+01:30"); - WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 456789, "2023-12-25 10:29:09.456789+01:30"); + WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25 10:29:09+0200"); + WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25 10:29:09+02:00"); + WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09+0130"); + WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09+01:30"); + WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 456789, "2023-12-25 10:29:09.456789+01:30"); - WEE_PARSE_DATE(1, 1703500149 - 60, 0, "2023-12-25 10:29:09-0001"); - WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09-0130"); - WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09-01:30"); - WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 456789, "2023-12-25 10:29:09.456789-01:30"); + WEE_PARSE_DATE(1, 1703500149 + 60, 0, "2023-12-25 10:29:09-0001"); + WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09-0130"); + WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09-01:30"); + WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 456789, "2023-12-25 10:29:09.456789-01:30"); /* * expected: 2023-12-25T10:29:09.456789Z == 1703500149.456789 @@ -467,20 +465,20 @@ TEST(CoreUtil, ParseTime) WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09 -00:00"); WEE_PARSE_DATE(1, 1703500149, 456789, "2023-12-25 10:29:09.456789 -00:00"); - WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25 10:29:09 +02"); - WEE_PARSE_DATE(1, 1703500149 + 60, 0, "2023-12-25 10:29:09 +0001"); - WEE_PARSE_DATE(1, 1703500149 + 60, 456789, "2023-12-25 10:29:09.456789 +0001"); + WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25 10:29:09 +02"); + WEE_PARSE_DATE(1, 1703500149 - 60, 0, "2023-12-25 10:29:09 +0001"); + WEE_PARSE_DATE(1, 1703500149 - 60, 456789, "2023-12-25 10:29:09.456789 +0001"); - WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25 10:29:09 +0200"); - WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25 10:29:09 +02:00"); - WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09 +0130"); - WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09 +01:30"); - WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 456789, "2023-12-25 10:29:09.456789 +01:30"); + WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25 10:29:09 +0200"); + WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25 10:29:09 +02:00"); + WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09 +0130"); + WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09 +01:30"); + WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 456789, "2023-12-25 10:29:09.456789 +01:30"); - WEE_PARSE_DATE(1, 1703500149 - 60, 0, "2023-12-25 10:29:09 -0001"); - WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09 -0130"); - WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09 -01:30"); - WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 456789, "2023-12-25 10:29:09.456789 -01:30"); + WEE_PARSE_DATE(1, 1703500149 + 60, 0, "2023-12-25 10:29:09 -0001"); + WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09 -0130"); + WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09 -01:30"); + WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 456789, "2023-12-25 10:29:09.456789 -01:30"); /* expected: 2023-12-25T10:29:09.456789Z == 1703500149.456789 */ WEE_PARSE_DATE(1, 1703500149, 0, "1703500149"); @@ -495,6 +493,7 @@ TEST(CoreUtil, ParseTime) WEE_PARSE_DATE(1, 1703500149, 456789, "1703500149.456789123456789123456789123456789"); setenv ("TZ", "", 1); + tzset (); } /*