From f5512e8b88acf612b985163b93087f04b7270af0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Sat, 6 Jul 2024 10:09:20 +0200 Subject: [PATCH] module-protocol-simple: use interface address as server address Move the address:port parsing code to the net helper. Add a default address option. Pass the interface address to protocol-simple and use this as the default address for listening. This makes sure that when the user passes tcp:3400 that we don't end up publishing 0.0.0.0:3400 but the actual address of the interface we are listening on so that the snapcast discover can use this to notify the snapcast server. Fixes #4093 --- src/modules/module-protocol-simple.c | 69 +++++++++++--------------- src/modules/module-snapcast-discover.c | 11 +++- src/modules/network-utils.h | 51 +++++++++++++++++++ 3 files changed, 88 insertions(+), 43 deletions(-) diff --git a/src/modules/module-protocol-simple.c b/src/modules/module-protocol-simple.c index a294eadee..560a7e8fa 100644 --- a/src/modules/module-protocol-simple.c +++ b/src/modules/module-protocol-simple.c @@ -56,6 +56,8 @@ * sink for each connected client. * - `playback`: boolean if playback is enabled. This will create a playback or * source stream for each connected client. + * - `local.ifname = `: interface name to use + * - `local.ifaddress = `: interface address to use * - `server.address = []`: an array of server addresses to listen on as * tcp:(:). * - `capture.props`: optional properties for the capture stream @@ -208,6 +210,8 @@ struct impl { struct pw_properties *capture_props; struct pw_properties *playback_props; + char *ifname; + char *ifaddress; bool capture; bool playback; @@ -654,47 +658,16 @@ error: return; } -static uint16_t parse_port(const char *str, uint16_t def) -{ - uint32_t val; - if (spa_atou32(str, &val, 0) && val <= 65535u) - return val; - return def; -} - -static int make_tcp_socket(struct server *server, const char *name) +static int make_tcp_socket(struct server *server, const char *name, const char *ifname, + const char *ifaddress) { struct sockaddr_storage addr; int res, fd, on; - uint16_t port; - char *br = NULL, *col, *n; socklen_t len = 0; - n = strdupa(name); - - col = strrchr(n, ':'); - if (n[0] == '[') { - br = strchr(n, ']'); - if (br == NULL) - return -EINVAL; - n++; - *br = 0; - } else { - } - if (br && col && col < br) - col = NULL; - - if (col) { - *col = '\0'; - port = parse_port(col+1, DEFAULT_PORT); - } else { - port = parse_port(n, DEFAULT_PORT); - n = strdupa("0.0.0.0"); - } - - if ((res = pw_net_parse_address(n, port, &addr, &len)) < 0) { - pw_log_error("%p: can't parse address:%s port:%d: %s", server, - n, port, spa_strerror(res)); + if ((res = pw_net_parse_address_port(name, ifaddress, DEFAULT_PORT, &addr, &len)) < 0) { + pw_log_error("%p: can't parse address %s: %s", server, + name, spa_strerror(res)); goto error; } @@ -703,7 +676,13 @@ static int make_tcp_socket(struct server *server, const char *name) pw_log_error("%p: socket() failed: %m", server); goto error; } - +#ifdef SO_BINDTODEVICE + if (ifname && setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)) < 0) { + res = -errno; + pw_log_error("%p: setsockopt(SO_BINDTODEVICE) failed: %m", server); + goto error; + } +#endif on = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &on, sizeof(on)) < 0) pw_log_warn("%p: setsockopt(): %m", server); @@ -764,7 +743,7 @@ static struct server *create_server(struct impl *impl, const char *address) spa_list_append(&impl->server_list, &server->link); if (spa_strstartswith(address, "tcp:")) { - fd = make_tcp_socket(server, address+4); + fd = make_tcp_socket(server, address+4, impl->ifname, impl->ifaddress); } else { pw_log_error("address %s does not start with tcp:", address); fd = -EINVAL; @@ -799,6 +778,8 @@ static void impl_free(struct impl *impl) pw_properties_free(impl->capture_props); pw_properties_free(impl->playback_props); pw_properties_free(impl->props); + free(impl->ifname); + free(impl->ifaddress); free(impl); } @@ -976,6 +957,11 @@ static int parse_params(struct impl *impl) pw_properties_setf(impl->playback_props, PW_KEY_NODE_RATE, "1/%u", impl->playback_info.rate); + str = pw_properties_get(impl->props, "local.ifname"); + impl->ifname = str ? strdup(str) : NULL; + str = pw_properties_get(impl->props, "local.ifaddress"); + impl->ifaddress = str ? strdup(str) : NULL; + if ((str = pw_properties_get(impl->props, "server.address")) == NULL) str = DEFAULT_SERVER; @@ -1056,9 +1042,10 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) uint16_t port = 0; bool ipv4; - if (pw_net_get_ip(&s->addr, ip, sizeof(ip), &ipv4, &port) >= 0) - fprintf(f, " \"%s%s%s:%d\"", ipv4 ? "" : "[", - ip, ipv4 ? "" : "]", port); + if (pw_net_get_ip(&s->addr, ip, sizeof(ip), &ipv4, &port) < 0) + continue; + + fprintf(f, " \"%s%s%s:%d\"", ipv4 ? "" : "[", ip, ipv4 ? "" : "]", port); } fprintf(f, " ]"); fclose(f); diff --git a/src/modules/module-snapcast-discover.c b/src/modules/module-snapcast-discover.c index 0874423e0..3f7a5fb5f 100644 --- a/src/modules/module-snapcast-discover.c +++ b/src/modules/module-snapcast-discover.c @@ -35,6 +35,8 @@ #include "module-protocol-pulse/format.h" #include "module-zeroconf-discover/avahi-poll.h" +#include "network-utils.h" + /** \page page_module_snapcast_discover Snapcast Discover * * Automatically creates a Snapcast sink device based on zeroconf @@ -465,9 +467,9 @@ static int add_snapcast_stream(struct impl *impl, struct tunnel *t, while (spa_json_get_string(&it[1], v, sizeof(v)) > 0) { t->server_address = strdup(v); snapcast_connect(t); - break; + return 0; } - return 0; + return -ENOENT; } static inline uint32_t format_from_name(const char *name, size_t len) @@ -688,6 +690,7 @@ static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiPr ifreq.ifr_ifindex = interface; ioctl(fd, SIOCGIFNAME, &ifreq, sizeof(ifreq)); pw_properties_setf(props, "snapcast.ifname", "%s", ifreq.ifr_name); + pw_properties_setf(props, "local.ifname", "%s", ifreq.ifr_name); struct ifaddrs *if_addr, *ifp; if (getifaddrs(&if_addr) < 0) @@ -711,6 +714,10 @@ static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiPr family == AF_INET ? "" : "[", hbuf, family == AF_INET ? "" : "]"); + pw_properties_setf(props, "local.ifaddress", "%s%s%s", + family == AF_INET ? "" : "[", + hbuf, + family == AF_INET ? "" : "]"); } else { pw_log_warn("error: %m %d %s", res, gai_strerror(res)); } diff --git a/src/modules/network-utils.h b/src/modules/network-utils.h index 74e4028da..6765b273d 100644 --- a/src/modules/network-utils.h +++ b/src/modules/network-utils.h @@ -38,6 +38,44 @@ static inline int pw_net_parse_address(const char *address, uint16_t port, return 0; } +static inline uint16_t pw_net_parse_port(const char *str, uint16_t def) +{ + uint32_t val; + if (spa_atou32(str, &val, 0) && val <= 65535u) + return val; + return def; +} + +static inline int pw_net_parse_address_port(const char *address, + const char *default_address, uint16_t default_port, + struct sockaddr_storage *addr, socklen_t *len) +{ + uint16_t port; + char *br = NULL, *col, *n; + + n = strdupa(address); + + col = strrchr(n, ':'); + if (n[0] == '[') { + br = strchr(n, ']'); + if (br == NULL) + return -EINVAL; + n++; + *br = 0; + } + if (br && col && col < br) + col = NULL; + + if (col) { + *col = '\0'; + port = pw_net_parse_port(col+1, default_port); + } else { + port = pw_net_parse_port(n, default_port); + n = strdupa(default_address ? default_address : "0.0.0.0"); + } + return pw_net_parse_address(n, port, addr, len); +} + static inline int pw_net_get_ip(const struct sockaddr_storage *sa, char *ip, size_t len, bool *ip4, uint16_t *port) { if (sa->ss_family == AF_INET) { @@ -75,4 +113,17 @@ static inline char *pw_net_get_ip_fmt(const struct sockaddr_storage *sa, char *i return ip; } +static inline bool pw_net_addr_is_any(struct sockaddr_storage *addr) +{ + if (addr->ss_family == AF_INET) { + struct sockaddr_in *sa = (struct sockaddr_in*)addr; + return sa->sin_addr.s_addr == INADDR_ANY; + } else if (addr->ss_family == AF_INET6) { + struct sockaddr_in6 *sa = (struct sockaddr_in6*)addr; + return memcmp(&sa->sin6_addr, &in6addr_any, sizeof(sa->sin6_addr)); + } + return false; +} + + #endif /* NETWORK_UTILS_H */