[Accel-ppp-users] [PATCH 2/3] utils: add string parsing helpers
Status: Beta
Brought to you by:
xebd
From: Guillaume N. <g....@al...> - 2018-11-30 16:36:35
|
Define parsers for IPv6 addresses and CIDR notations, unsigned integers, separators (variable number of space characters) and end of strings (variable number of spaces followed by '\0'). All of these functions work on constant string and return the number bytes parsed. If the input string doesn't have the expected format, these functions return 0 (no forward progress). Also implement a convenient wrapper around inet_ntop() that can be used easily in printf-like functions. Signed-off-by: Guillaume Nault <g....@al...> --- accel-pppd/utils.c | 181 +++++++++++++++++++++++++++++++++++++++++++++ accel-pppd/utils.h | 14 ++++ 2 files changed, 195 insertions(+) diff --git a/accel-pppd/utils.c b/accel-pppd/utils.c index 3b87ee16..544c59e7 100644 --- a/accel-pppd/utils.c +++ b/accel-pppd/utils.c @@ -1,7 +1,11 @@ +#include <arpa/inet.h> +#include <ctype.h> #include <errno.h> #include <netdb.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> #include <netinet/in.h> @@ -12,6 +16,21 @@ extern int urandom_fd; +/* Convenient wrapper around inet_ntop() to print IPv6 addresses. + * It stores a string representation of addr into buf, which must be at + * least INET6_ADDRSTRLEN bytes long. + * + * Returns buf, which is guaranteed to contain a valid string even if an error + * occured. + */ +char __export *u_ip6str(const struct in6_addr *addr, char *buf) +{ + if (!inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN)) + snprintf(buf, INET6_ADDRSTRLEN, "< ERROR! >"); + + return buf; +} + void __export u_inet_ntoa(in_addr_t addr, char *str) { addr = ntohl(addr); @@ -37,6 +56,117 @@ int __export u_readlong(long int *dst, const char *src, } } +/* Parse spaces. + * Returns the number of leading space characters in str. + * This is a convenient function around strspn() which preserves the look and + * feel of other u_parse_*() functions. + */ +size_t __export u_parse_spaces(const char *str) +{ + return strspn(str, " "); +} + +/* Parse end of string. + * Reads a sequence of space characters, followed by the end-of-string + * mark ('\0'). + * Returns the number of characters parsed on success (that is, the number of + * space characters plus one for '\0'). Beware that 'str + u_parse_eos(str)' + * points to the next byte after the end of the string in this case. + * Returns 0 if parsing fails (that is, if unexpected characers are found + * before the end of the string). + */ +size_t __export u_parse_endstr(const char *str) +{ + const char *end; + + end = str + strspn(str, " "); + if (*end != '\0') + return 0; + + ++end; + + return end - str; +} + +/* Parse an 8 bits unsigned integer in base 10. + * Returns the number of bytes parsed on success. + * Returns 0 if str doesn't start with a valid number or if this number doesn't + * fit in 8 bits. + */ +size_t __export u_parse_u8(const char *str, uint8_t *val) +{ + char *endptr; + unsigned long ul; + + /* strtoul() handles leading signs (+/-) and white spaces. Make sure we + * parse raw numbers. + */ + if (!isdigit(*str)) + return 0; + + ul = strtoul(str, &endptr, 10); + if (ul > UINT8_MAX) + return 0; + + *val = ul; + + return endptr - str; +} + +/* Parse a 16 bits unsigned integer in base 10. + * Returns the number of bytes parsed on success. + * Returns 0 if str doesn't start with a valid number or if this number doesn't + * fit in 16 bits. + */ +size_t __export u_parse_u16(const char *str, uint16_t *val) +{ + char *endptr; + unsigned long ul; + + /* strtoul() handles leading signs (+/-) and white spaces. Make sure we + * parse raw numbers. + */ + if (!isdigit(*str)) + return 0; + + ul = strtoul(str, &endptr, 10); + if (ul > UINT16_MAX) + return 0; + + *val = ul; + + return endptr - str; +} + +/* Parse a 32 bits unsigned integer in base 10. + * Returns the number of bytes parsed on success. + * Returns 0 if str doesn't start with a valid number or if this number doesn't + * fit in 32 bits. + */ +size_t __export u_parse_u32(const char *str, uint32_t *val) +{ + char *endptr; + unsigned long ul; + + /* strtoul() handles leading signs (+/-) and white spaces. Make sure we + * parse raw numbers. + */ + if (!isdigit(*str)) + return 0; + + errno = 0; + ul = strtoul(str, &endptr, 10); + /* On platforms where unsigned longs are 32 bits wide, overflows would + * return a valid UINT32_MAX value. So we need to check for ERANGE too. + */ + if (errno == ERANGE || ul > UINT32_MAX) + return 0; + + *val = ul; + + return endptr - str; +} + int __export u_parse_ip4addr(const char *src, struct in_addr *addr, const char **err_msg) { @@ -62,6 +192,57 @@ int __export u_parse_ip4addr(const char *src, struct in_addr *addr, return 0; } +/* Parse an IPv6 address (for example "2001:db8::1"). + * Returns the number of bytes parsed, or 0 if str doesn't start with an IPv6 + * address. + */ +size_t __export u_parse_ip6addr(const char *str, struct in6_addr *addr) +{ + char buf[INET6_ADDRSTRLEN]; + size_t len; + + len = strspn(str, ":0123456789abcdef"); + if (!len || len >= sizeof(buf)) + return 0; + + memcpy(buf, str, len); + buf[len] = '\0'; + + if (inet_pton(AF_INET6, buf, addr) != 1) + return 0; + + return len; +} + +/* Parse an IPv6 network prefix in CIDR notation (for example "2001:db8::/32"). + * Returns the number of bytes parsed, or 0 if str doesn't start with an IPv6 + * network prefix. + */ +size_t __export u_parse_ip6cidr(const char *str, struct in6_addr *netp, uint8_t *plen) +{ + const char *ptr = str; + size_t len; + + len = u_parse_ip6addr(ptr, netp); + if (!len) + return 0; + + ptr += len; + if (*ptr != '/') + return 0; + + len = u_parse_u8(++ptr, plen); + if (!len) + return 0; + + if (*plen > 128) + return 0; + + ptr += len; + + return ptr - str; +} + int __export u_randbuf(void *buf, size_t buf_len, int *err) { uint8_t *u8buf = buf; diff --git a/accel-pppd/utils.h b/accel-pppd/utils.h index 87582648..d3e06083 100644 --- a/accel-pppd/utils.h +++ b/accel-pppd/utils.h @@ -2,11 +2,25 @@ #define __UTILS_H #include <netinet/in.h> +#include <stdint.h> + +char *u_ip6str(const struct in6_addr *addr, char *buf); void u_inet_ntoa(in_addr_t, char *str); int u_readlong(long int *dst, const char *src, long int min, long int max); + +size_t u_parse_spaces(const char *str); +size_t u_parse_endstr(const char *str); + +size_t u_parse_u8(const char *str, uint8_t *val); +size_t u_parse_u16(const char *str, uint16_t *val); +size_t u_parse_u32(const char *str, uint32_t *val); + int u_parse_ip4addr(const char *src, struct in_addr *addr, const char **err_msg); +size_t u_parse_ip6addr(const char *str, struct in6_addr *addr); +size_t u_parse_ip6cidr(const char *str, struct in6_addr *netp, uint8_t *plen); + int u_randbuf(void *buf, size_t buf_len, int *err); #endif -- 2.20.0.rc1 |