diff options
author | Daniel Kahn Gillmor <dkg@fifthhorseman.net> | 2016-05-17 11:00:29 -0400 |
---|---|---|
committer | Daniel Kahn Gillmor <dkg@fifthhorseman.net> | 2016-05-17 16:55:20 -0400 |
commit | dd4d201ab1c9456e1bffc39f9dec00a4a4619084 (patch) | |
tree | ca157d8ed22b9197380640af08b0785fdfacdcdf /src/basic | |
parent | 1a7906ae914fd233305fff0acf4b9138ff944209 (diff) |
util-lib: add ether_addr_from_string()
This isn't quite symmetrical to in_addr_from_string() because it also returns
an offset indicating how much of the string was consumed by the matched
pattern. This offset reporting is needed for either of the following use
cases:
* verifying the lack of trailing garbage after such an address
* parsing subsequent data from the same string
Diffstat (limited to 'src/basic')
-rw-r--r-- | src/basic/ether-addr-util.c | 69 | ||||
-rw-r--r-- | src/basic/ether-addr-util.h | 2 |
2 files changed, 71 insertions, 0 deletions
diff --git a/src/basic/ether-addr-util.c b/src/basic/ether-addr-util.c index a4d8d656da..5697e8d132 100644 --- a/src/basic/ether-addr-util.c +++ b/src/basic/ether-addr-util.c @@ -23,6 +23,7 @@ #include "ether-addr-util.h" #include "macro.h" +#include "string-util.h" char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) { assert(addr); @@ -54,3 +55,71 @@ bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) { a->ether_addr_octet[4] == b->ether_addr_octet[4] && a->ether_addr_octet[5] == b->ether_addr_octet[5]; } + +int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset) { + size_t pos = 0, n, field; + char sep = '\0'; + const char *hex = HEXDIGITS, *hexoff; + size_t x; + bool touched; + +#define parse_fields(v) \ + for (field = 0; field < ELEMENTSOF(v); field++) { \ + touched = false; \ + for (n = 0; n < (2 * sizeof(v[0])); n++) { \ + if (s[pos] == '\0') \ + break; \ + hexoff = strchr(hex, s[pos]); \ + if (hexoff == NULL) \ + break; \ + assert(hexoff >= hex); \ + x = hexoff - hex; \ + if (x >= 16) \ + x -= 6; /* A-F */ \ + assert(x < 16); \ + touched = true; \ + v[field] <<= 4; \ + v[field] += x; \ + pos++; \ + } \ + if (!touched) \ + return -EINVAL; \ + if (field < (ELEMENTSOF(v)-1)) { \ + if (s[pos] != sep) \ + return -EINVAL; \ + else \ + pos++; \ + } \ + } + + assert(s); + assert(ret); + + sep = s[strspn(s, hex)]; + if (sep == '\n') + return -EINVAL; + if (strchr(":.-", sep) == NULL) + return -EINVAL; + + if (sep == '.') { + uint16_t shorts[3] = { 0 }; + + parse_fields(shorts); + + for (n = 0; n < ELEMENTSOF(shorts); n++) { + ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8); + ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff); + } + } else { + struct ether_addr out = { .ether_addr_octet = { 0 } }; + + parse_fields(out.ether_addr_octet); + + for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++) + ret->ether_addr_octet[n] = out.ether_addr_octet[n]; + } + + if (offset) + *offset = pos; + return 0; +} diff --git a/src/basic/ether-addr-util.h b/src/basic/ether-addr-util.h index 074363793e..74e125a95f 100644 --- a/src/basic/ether-addr-util.h +++ b/src/basic/ether-addr-util.h @@ -35,3 +35,5 @@ bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b); static inline bool ether_addr_is_null(const struct ether_addr *addr) { return ether_addr_equal(addr, ÐER_ADDR_NULL); } + +int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset); |