Skip to content

Commit a723fb8

Browse files
committed
util: introduce in_addr_port_ifindex_name_from_string_auto() and in_addr_port_ifindex_name_to_string()
1 parent cbe194b commit a723fb8

File tree

5 files changed

+206
-43
lines changed

5 files changed

+206
-43
lines changed

src/basic/in-addr-util.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "macro.h"
1515
#include "parse-util.h"
1616
#include "random-util.h"
17+
#include "string-util.h"
1718
#include "strxcpyx.h"
1819
#include "util.h"
1920

@@ -445,6 +446,61 @@ int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifin
445446
return in_addr_to_string(family, u, ret);
446447
}
447448

449+
int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret) {
450+
_cleanup_free_ char *ip_str = NULL, *x = NULL;
451+
int r;
452+
453+
assert(IN_SET(family, AF_INET, AF_INET6));
454+
assert(u);
455+
assert(ret);
456+
457+
/* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly
458+
* handle IPv6 link-local addresses. */
459+
460+
r = in_addr_to_string(family, u, &ip_str);
461+
if (r < 0)
462+
return r;
463+
464+
if (family == AF_INET6) {
465+
r = in_addr_is_link_local(family, u);
466+
if (r < 0)
467+
return r;
468+
if (r == 0)
469+
ifindex = 0;
470+
} else
471+
ifindex = 0; /* For IPv4 address, ifindex is always ignored. */
472+
473+
if (port == 0 && ifindex == 0 && isempty(server_name)) {
474+
*ret = TAKE_PTR(ip_str);
475+
return 0;
476+
}
477+
478+
const char *separator = isempty(server_name) ? "" : "#";
479+
server_name = strempty(server_name);
480+
481+
if (port > 0) {
482+
if (family == AF_INET6) {
483+
if (ifindex > 0)
484+
r = asprintf(&x, "[%s]:%"PRIu16"%%%i%s%s", ip_str, port, ifindex, separator, server_name);
485+
else
486+
r = asprintf(&x, "[%s]:%"PRIu16"%s%s", ip_str, port, separator, server_name);
487+
} else
488+
r = asprintf(&x, "%s:%"PRIu16"%s%s", ip_str, port, separator, server_name);
489+
} else {
490+
if (ifindex > 0)
491+
r = asprintf(&x, "%s%%%i%s%s", ip_str, ifindex, separator, server_name);
492+
else {
493+
x = strjoin(ip_str, separator, server_name);
494+
r = x ? 0 : -ENOMEM;
495+
}
496+
}
497+
if (r < 0)
498+
return -ENOMEM;
499+
500+
*ret = TAKE_PTR(x);
501+
return 0;
502+
}
503+
448504
int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
449505
union in_addr_union buffer;
450506
assert(s);

src/basic/in-addr-util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen
4141
int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
4242
int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret);
4343
int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret);
44+
int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret);
4445
int in_addr_from_string(int family, const char *s, union in_addr_union *ret);
4546
int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret);
4647

src/shared/socket-netlink.c

Lines changed: 103 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -327,68 +327,130 @@ int make_socket_fd(int log_level, const char* address, int type, int flags) {
327327
return fd;
328328
}
329329

330-
int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret_addr, int *ret_ifindex) {
331-
_cleanup_free_ char *buf = NULL;
332-
const char *suffix;
333-
int r, ifindex = 0;
330+
int in_addr_port_ifindex_name_from_string_auto(
331+
const char *s,
332+
int *ret_family,
333+
union in_addr_union *ret_address,
334+
uint16_t *ret_port,
335+
int *ret_ifindex,
336+
char **ret_server_name) {
337+
338+
_cleanup_free_ char *buf1 = NULL, *buf2 = NULL, *name = NULL;
339+
int family, ifindex = 0, r;
340+
union in_addr_union a;
341+
uint16_t port = 0;
342+
const char *m;
334343

335344
assert(s);
336-
assert(family);
337-
assert(ret_addr);
338345

339-
/* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id")
340-
* if one is found. */
346+
/* This accepts the following:
347+
* 192.168.0.1:53#example.com
348+
* [2001:4860:4860::8888]:53%eth0#example.com */
349+
350+
/* if ret_port is NULL, then strings with port cannot be specified.
351+
* Also, if ret_server_name is NULL, then server_name cannot be specified. */
352+
353+
m = strchr(s, '#');
354+
if (m) {
355+
if (!ret_server_name)
356+
return -EINVAL;
357+
358+
if (isempty(m + 1))
359+
return -EINVAL;
360+
361+
name = strdup(m + 1);
362+
if (!name)
363+
return -ENOMEM;
364+
365+
s = buf1 = strndup(s, m - s);
366+
if (!buf1)
367+
return -ENOMEM;
368+
}
369+
370+
m = strchr(s, '%');
371+
if (m) {
372+
if (isempty(m + 1))
373+
return -EINVAL;
341374

342-
suffix = strchr(s, '%');
343-
if (suffix) {
344375
if (ret_ifindex) {
345376
/* If we shall return the interface index, try to parse it */
346-
ifindex = resolve_interface(NULL, suffix + 1);
377+
ifindex = resolve_interface(NULL, m + 1);
347378
if (ifindex < 0)
348379
return ifindex;
349380
}
350381

351-
s = buf = strndup(s, suffix - s);
352-
if (!buf)
382+
s = buf2 = strndup(s, m - s);
383+
if (!buf2)
353384
return -ENOMEM;
354385
}
355386

356-
r = in_addr_from_string_auto(s, family, ret_addr);
357-
if (r < 0)
358-
return r;
359-
360-
if (ret_ifindex)
361-
*ret_ifindex = ifindex;
387+
m = strrchr(s, ':');
388+
if (m) {
389+
if (*s == '[') {
390+
_cleanup_free_ char *ip_str = NULL;
362391

363-
return r;
364-
}
392+
if (!ret_port)
393+
return -EINVAL;
365394

366-
int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) {
367-
_cleanup_free_ char *buf = NULL, *name = NULL;
368-
const char *m;
369-
int r;
395+
if (*(m - 1) != ']')
396+
return -EINVAL;
370397

371-
assert(s);
398+
family = AF_INET6;
372399

373-
m = strchr(s, '#');
374-
if (m) {
375-
name = strdup(m+1);
376-
if (!name)
377-
return -ENOMEM;
400+
r = parse_ip_port(m + 1, &port);
401+
if (r < 0)
402+
return r;
378403

379-
buf = strndup(s, m - s);
380-
if (!buf)
381-
return -ENOMEM;
404+
ip_str = strndup(s + 1, m - s - 2);
405+
if (!ip_str)
406+
return -ENOMEM;
382407

383-
s = buf;
408+
r = in_addr_from_string(family, ip_str, &a);
409+
if (r < 0)
410+
return r;
411+
} else {
412+
/* First try to parse the string as IPv6 address without port number */
413+
r = in_addr_from_string(AF_INET6, s, &a);
414+
if (r < 0) {
415+
/* Then the input should be IPv4 address with port number */
416+
_cleanup_free_ char *ip_str = NULL;
417+
418+
if (!ret_port)
419+
return -EINVAL;
420+
421+
family = AF_INET;
422+
423+
ip_str = strndup(s, m - s);
424+
if (!ip_str)
425+
return -ENOMEM;
426+
427+
r = in_addr_from_string(family, ip_str, &a);
428+
if (r < 0)
429+
return r;
430+
431+
r = parse_ip_port(m + 1, &port);
432+
if (r < 0)
433+
return r;
434+
} else
435+
family = AF_INET6;
436+
}
437+
} else {
438+
family = AF_INET;
439+
r = in_addr_from_string(family, s, &a);
440+
if (r < 0)
441+
return r;
384442
}
385443

386-
r = in_addr_ifindex_from_string_auto(s, family, ret, ifindex);
387-
if (r < 0)
388-
return r;
389-
390-
if (server_name)
391-
*server_name = TAKE_PTR(name);
444+
if (ret_family)
445+
*ret_family = family;
446+
if (ret_address)
447+
*ret_address = a;
448+
if (ret_port)
449+
*ret_port = port;
450+
if (ret_ifindex)
451+
*ret_ifindex = ifindex;
452+
if (ret_server_name)
453+
*ret_server_name = TAKE_PTR(name);
392454

393455
return r;
394456
}

src/shared/socket-netlink.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,16 @@ int socket_address_parse_netlink(SocketAddress *a, const char *s);
2020
bool socket_address_is(const SocketAddress *a, const char *s, int type);
2121
bool socket_address_is_netlink(const SocketAddress *a, const char *s);
2222

23-
int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex);
24-
int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name);
23+
int in_addr_port_ifindex_name_from_string_auto(
24+
const char *s,
25+
int *ret_family,
26+
union in_addr_union *ret_address,
27+
uint16_t *ret_port,
28+
int *ret_ifindex,
29+
char **ret_server_name);
30+
static inline int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) {
31+
return in_addr_port_ifindex_name_from_string_auto(s, family, ret, NULL, ifindex, server_name);
32+
}
33+
static inline int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) {
34+
return in_addr_ifindex_name_from_string_auto(s, family, ret, ifindex, NULL);
35+
}

src/test/test-socket-util.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,38 @@ static void test_in_addr_ifindex_name_from_string_auto(void) {
302302
test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19#another.test.com", "another.test.com");
303303
}
304304

305+
static void test_in_addr_port_ifindex_name_from_string_auto_one(const char *str, int family, uint16_t port, int ifindex, const char *server_name) {
306+
_cleanup_free_ char *name = NULL, *x = NULL;
307+
union in_addr_union a;
308+
uint16_t p;
309+
int f, i;
310+
311+
assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, &p, &i, &name) >= 0);
312+
assert_se(family == f);
313+
assert_se(port == p);
314+
assert_se(ifindex == i);
315+
assert_se(streq_ptr(server_name, name));
316+
assert_se(in_addr_port_ifindex_name_to_string(f, &a, p, i, name, &x) >= 0);
317+
assert_se(streq(str, x));
318+
}
319+
320+
static void test_in_addr_port_ifindex_name_from_string_auto(void) {
321+
log_info("/* %s */", __func__);
322+
323+
test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1", AF_INET, 0, 0, NULL);
324+
test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1#test.com", AF_INET, 0, 0, "test.com");
325+
test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1:53", AF_INET, 53, 0, NULL);
326+
test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1:53#example.com", AF_INET, 53, 0, "example.com");
327+
test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18", AF_INET6, 0, 0, NULL);
328+
test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18#hoge.com", AF_INET6, 0, 0, "hoge.com");
329+
test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%19", AF_INET6, 0, 19, NULL);
330+
test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53", AF_INET6, 53, 0, NULL);
331+
test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%19#hoge.com", AF_INET6, 0, 19, "hoge.com");
332+
test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53#hoge.com", AF_INET6, 53, 0, "hoge.com");
333+
test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19", AF_INET6, 53, 19, NULL);
334+
test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19#hoge.com", AF_INET6, 53, 19, "hoge.com");
335+
}
336+
305337
static void test_sockaddr_equal(void) {
306338
union sockaddr_union a = {
307339
.in.sin_family = AF_INET,
@@ -735,6 +767,7 @@ int main(int argc, char *argv[]) {
735767
test_in_addr_ifindex_to_string();
736768
test_in_addr_ifindex_from_string_auto();
737769
test_in_addr_ifindex_name_from_string_auto();
770+
test_in_addr_port_ifindex_name_from_string_auto();
738771

739772
test_sockaddr_equal();
740773

0 commit comments

Comments
 (0)