* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) The Exim Maintainers 2020 - 2022 */
+/* Copyright (c) The Exim Maintainers 2020 - 2023 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Miscellaneous string-handling functions. Some are not required for
utilities and tests, and are cut out by the COMPILE_UTILITY macro. */
The legacy string_is_ip_address() function follows below.
*/
-int
-string_is_ip_addressX(const uschar *ip_addr, int *maskptr, const uschar **errp) {
- struct addrinfo hints;
- struct addrinfo *res;
-
- uschar *slash, *percent;
- uschar *endp = 0;
- long int mask = 0;
- const uschar *addr = 0;
-
- /* If there is a slash, but we didn't request a (optional) netmask,
- we return failure, as we do if the mask isn't a pure numerical value,
- or if it is negative. The actual length is checked later, once we know
- the address family. */
- if (slash = Ustrchr(ip_addr, '/'))
+int
+string_is_ip_addressX(const uschar * ip_addr, int * maskptr, const uschar ** errp)
+{
+uschar * slash, * percent, * endp = NULL;
+long int mask = 0;
+const uschar * addr = NULL;
+int af;
+union { /* we do not need this, but inet_pton() needs a place for storage */
+ struct in_addr sa4;
+ struct in6_addr sa6;
+} sa;
+
+/* If there is a slash, but we didn't request a (optional) netmask,
+we return failure, as we do if the mask isn't a pure numerical value,
+or if it is negative. The actual length is checked later, once we know
+the address family. */
+if (slash = Ustrchr(ip_addr, '/'))
{
- if (!maskptr)
+ uschar * rest;
+
+ if (!maskptr)
{
- if (errp) *errp = "netmask found, but not requested";
- return 0;
+ if (errp) *errp = US"netmask found, but not requested";
+ return 0;
}
- uschar *rest;
- mask = Ustrtol(slash+1, &rest, 10);
- if (*rest || mask < 0)
+ mask = Ustrtol(slash+1, &rest, 10);
+ if (*rest || mask < 0)
{
- if (errp) *errp = "netmask not numeric or <0";
- return 0;
+ if (errp) *errp = US"netmask not numeric or <0";
+ return 0;
}
- *maskptr = slash - ip_addr; /* offset of the slash */
- endp = slash;
- } else if (maskptr) *maskptr = 0; /* no slash found */
+ *maskptr = slash - ip_addr; /* offset of the slash */
+ endp = slash;
+ }
+else if (maskptr) *maskptr = 0; /* no slash found */
+
+/* The interface-ID suffix (%<id>) is optional (for IPv6). If it
+exists, we check it syntactically. Later, if we know the address
+family is IPv4, we might reject it.
+The interface-ID is mutually exclusive with the netmask, to the
+best of my knowledge. */
- /* The interface-ID suffix (%<id>) is optional (for IPv6). If it
- exists, we check it syntactically. Later, if we know the address
- family is IPv4, we might reject it.
- The interface-ID is mutually exclusive with the netmask, to the
- best of my knowledge. */
- if (percent = Ustrchr(ip_addr, '%'))
+if (percent = Ustrchr(ip_addr, '%'))
{
- if (slash)
+ if (slash)
{
- if (errp) *errp = "interface-ID and netmask are mutually exclusive";
- return 0;
+ if (errp) *errp = US"interface-ID and netmask are mutually exclusive";
+ return 0;
}
- for (uschar *p = percent+1; *p; p++)
- if (!isalnum(*p) && !ispunct(*p))
- {
- if (errp) *errp = "interface-ID must match [[:alnum:][:punct:]]";
- return 0;
- }
- endp = percent;
+ for (uschar *p = percent+1; *p; p++)
+ if (!isalnum(*p) && !ispunct(*p))
+ {
+ if (errp) *errp = US"interface-ID must match [[:alnum:][:punct:]]";
+ return 0;
+ }
+ endp = percent;
}
- /* inet_pton() can't parse netmasks and interface IDs, so work on a shortened copy
- allocated on the current stack */
- if (endp) {
- ptrdiff_t l = endp - ip_addr;
- if (l > 255)
+/* inet_pton() can't parse netmasks and interface IDs, so work on a shortened copy
+allocated on the current stack */
+
+if (endp)
+ {
+ ptrdiff_t l = endp - ip_addr;
+ if (l > 255)
{
- if (errp) *errp = "rudiculous long ip address string";
- return 0;
+ if (errp) *errp = US"rudiculous long ip address string";
+ return 0;
}
- addr = alloca(l+1); /* *BSD does not have strndupa() */
- Ustrncpy((uschar *)addr, ip_addr, l);
- ((uschar*)addr)[l] = '\0';
- } else addr = ip_addr;
-
- int af;
- union { /* we do not need this, but inet_pton() needs a place for storage */
- struct in_addr sa4;
- struct in6_addr sa6;
- } sa;
-
- af = Ustrchr(addr, ':') ? AF_INET6 : AF_INET;
- if (!inet_pton(af, addr, &sa))
+ addr = string_copyn(ip_addr, l);
+ }
+else
+ addr = ip_addr;
+
+af = Ustrchr(addr, ':') ? AF_INET6 : AF_INET;
+if (!inet_pton(af, CCS addr, &sa))
{
- if (errp) *errp = af == AF_INET6 ? "IP address string not parsable as IPv6"
- : "IP address string not parsable IPv4";
- return 0;
+ if (errp) *errp = af == AF_INET6 ? US"IP address string not parsable as IPv6"
+ : US"IP address string not parsable IPv4";
+ return 0;
}
- /* we do not check the values of the mask here, as
- this is done on the callers side (but I don't understand why), so
- actually I'd like to do it here, but it breaks at least 0002 */
- switch (af)
+
+/* we do not check the values of the mask here, as
+this is done on the callers side (but I don't understand why), so
+actually I'd like to do it here, but it breaks at least testcase 0002 */
+
+switch (af)
{
- case AF_INET6:
- if (errp && mask > 128)
- {
- *errp = "IPv6 netmask value must not be >128";
- return 0;
- }
- return 6;
- case AF_INET:
- if (percent)
- {
- if (errp) *errp = "IPv4 address string must not have an interface-ID";
- return 0;
- }
- if (errp && mask > 32) {
- *errp = "IPv4 netmask value must not be >32";
- return 0;
- }
- return 4;
- default:
- if (errp) *errp = "unknown address family (should not happen)";
- return 0;
- }
+ case AF_INET6:
+ if (errp && mask > 128)
+ {
+ *errp = US"IPv6 netmask value must not be >128";
+ return 0;
+ }
+ return 6;
+ case AF_INET:
+ if (percent)
+ {
+ if (errp) *errp = US"IPv4 address string must not have an interface-ID";
+ return 0;
+ }
+ if (errp && mask > 32)
+ {
+ *errp = US"IPv4 netmask value must not be >32";
+ return 0;
+ }
+ return 4;
+ default:
+ if (errp) *errp = US"unknown address family (should not happen)";
+ return 0;
+ }
}
+
int
-string_is_ip_address(const uschar *ip_addr, int *maskptr) {
- return string_is_ip_addressX(ip_addr, maskptr, 0);
+string_is_ip_address(const uschar * ip_addr, int * maskptr)
+{
+return string_is_ip_addressX(ip_addr, maskptr, 0);
}
#endif /* COMPILE_UTILITY */
*************************************************/
/* Convert a long integer into an ASCII base 62 string. For Cygwin the value of
-BASE_62 is actually 36. Always return exactly 6 characters plus zero, in a
-static area.
+BASE_62 is actually 36. Always return exactly 6 characters plus a NUL, in a
+static area. This is enough for a 32b input, for 62 (for 64b we would want 11+nul);
+but with 36 we lose half the input range of a 32b input.
Argument: a long integer
Returns: pointer to base 62 string
*/
uschar *
-string_base62(unsigned long int value)
+string_base62_32(unsigned long int value)
{
static uschar yield[7];
-uschar *p = yield + sizeof(yield) - 1;
+uschar * p = yield + sizeof(yield) - 1;
*p = 0;
while (p > yield)
{
- *(--p) = base62_chars[value % BASE_62];
+ *--p = base62_chars[value % BASE_62];
value /= BASE_62;
}
return yield;
}
+
+uschar *
+string_base62_64(unsigned long int value)
+{
+static uschar yield[12];
+uschar * p = yield + sizeof(yield) - 1;
+*p = '\0';
+while (p > yield)
+ if (value)
+ {
+ *--p = base62_chars[value % BASE_62];
+ value /= BASE_62;
+ }
+ else
+ *--p = '0';
+return yield;
+}
#endif /* COMPILE_UTILITY */
will not be grown, and is usable in the original place after return.
The return value can be NULL to signify overflow.
+Field width: decimal digits, or *
+Precision: dot, followed by decimal digits or *
+Length modifiers: h L l ll z
+Conversion specifiers: n d o u x X p f e E g G % c s S T Y D M
+
Returns the possibly-new (if copy for growth or taint-handling was needed)
string, not nul-terminated.
*/
slen = string_datestamp_length;
goto INSERT_STRING;
+ case 'Y': /* gstring pointer */
+ {
+ gstring * zg = va_arg(ap, gstring *);
+ if (zg) { s = CS zg->s; slen = zg->ptr; }
+ else { s = null; slen = Ustrlen(s); }
+ goto INSERT_GSTRING;
+ }
+
case 's':
case 'S': /* Forces *lower* case */
case 'T': /* Forces *upper* case */
if (!s) s = null;
slen = Ustrlen(s);
+ INSERT_GSTRING: /* Coome to from %Y above */
+
if (!(flags & SVFMT_TAINT_NOCHK) && is_incompatible(g->s, s))
if (flags & SVFMT_REBUFFER)
{
int llflag = 0;
int n = 0;
int count;
- int countset = 0;
+ BOOL countset = FASE;
uschar format[256];
uschar outbuf[256];
uschar *s;
else if (Ustrcmp(ss, "*") == 0)
{
args[n++] = (void *)(&count);
- countset = 1;
+ countset = TRUE;
}
else