mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	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
This commit is contained in:
		
							parent
							
								
									38d7dedf0c
								
							
						
					
					
						commit
						f5512e8b88
					
				
					 3 changed files with 88 additions and 43 deletions
				
			
		| 
						 | 
					@ -56,6 +56,8 @@
 | 
				
			||||||
 *               sink for each connected client.
 | 
					 *               sink for each connected client.
 | 
				
			||||||
 *  - `playback`: boolean if playback is enabled. This will create a playback or
 | 
					 *  - `playback`: boolean if playback is enabled. This will create a playback or
 | 
				
			||||||
 *               source stream for each connected client.
 | 
					 *               source stream for each connected client.
 | 
				
			||||||
 | 
					 *  - `local.ifname = <str>`: interface name to use
 | 
				
			||||||
 | 
					 *  - `local.ifaddress = <str>`: interface address to use
 | 
				
			||||||
 *  - `server.address = []`: an array of server addresses to listen on as
 | 
					 *  - `server.address = []`: an array of server addresses to listen on as
 | 
				
			||||||
 *                            tcp:(<ip>:)<port>.
 | 
					 *                            tcp:(<ip>:)<port>.
 | 
				
			||||||
 *  - `capture.props`: optional properties for the capture stream
 | 
					 *  - `capture.props`: optional properties for the capture stream
 | 
				
			||||||
| 
						 | 
					@ -208,6 +210,8 @@ struct impl {
 | 
				
			||||||
	struct pw_properties *capture_props;
 | 
						struct pw_properties *capture_props;
 | 
				
			||||||
	struct pw_properties *playback_props;
 | 
						struct pw_properties *playback_props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						char *ifname;
 | 
				
			||||||
 | 
						char *ifaddress;
 | 
				
			||||||
	bool capture;
 | 
						bool capture;
 | 
				
			||||||
	bool playback;
 | 
						bool playback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -654,47 +658,16 @@ error:
 | 
				
			||||||
	return;
 | 
						return;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static uint16_t parse_port(const char *str, uint16_t def)
 | 
					static int make_tcp_socket(struct server *server, const char *name, const char *ifname,
 | 
				
			||||||
{
 | 
							const char *ifaddress)
 | 
				
			||||||
	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)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct sockaddr_storage addr;
 | 
						struct sockaddr_storage addr;
 | 
				
			||||||
	int res, fd, on;
 | 
						int res, fd, on;
 | 
				
			||||||
	uint16_t port;
 | 
					 | 
				
			||||||
	char *br = NULL, *col, *n;
 | 
					 | 
				
			||||||
	socklen_t len = 0;
 | 
						socklen_t len = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	n = strdupa(name);
 | 
						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,
 | 
				
			||||||
	col = strrchr(n, ':');
 | 
									name, spa_strerror(res));
 | 
				
			||||||
	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));
 | 
					 | 
				
			||||||
		goto error;
 | 
							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);
 | 
							pw_log_error("%p: socket() failed: %m", server);
 | 
				
			||||||
		goto error;
 | 
							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;
 | 
						on = 1;
 | 
				
			||||||
	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &on, sizeof(on)) < 0)
 | 
						if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &on, sizeof(on)) < 0)
 | 
				
			||||||
		pw_log_warn("%p: setsockopt(): %m", server);
 | 
							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);
 | 
						spa_list_append(&impl->server_list, &server->link);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (spa_strstartswith(address, "tcp:")) {
 | 
						if (spa_strstartswith(address, "tcp:")) {
 | 
				
			||||||
		fd = make_tcp_socket(server, address+4);
 | 
							fd = make_tcp_socket(server, address+4, impl->ifname, impl->ifaddress);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		pw_log_error("address %s does not start with tcp:", address);
 | 
							pw_log_error("address %s does not start with tcp:", address);
 | 
				
			||||||
		fd = -EINVAL;
 | 
							fd = -EINVAL;
 | 
				
			||||||
| 
						 | 
					@ -799,6 +778,8 @@ static void impl_free(struct impl *impl)
 | 
				
			||||||
	pw_properties_free(impl->capture_props);
 | 
						pw_properties_free(impl->capture_props);
 | 
				
			||||||
	pw_properties_free(impl->playback_props);
 | 
						pw_properties_free(impl->playback_props);
 | 
				
			||||||
	pw_properties_free(impl->props);
 | 
						pw_properties_free(impl->props);
 | 
				
			||||||
 | 
						free(impl->ifname);
 | 
				
			||||||
 | 
						free(impl->ifaddress);
 | 
				
			||||||
	free(impl);
 | 
						free(impl);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -976,6 +957,11 @@ static int parse_params(struct impl *impl)
 | 
				
			||||||
		pw_properties_setf(impl->playback_props, PW_KEY_NODE_RATE,
 | 
							pw_properties_setf(impl->playback_props, PW_KEY_NODE_RATE,
 | 
				
			||||||
				"1/%u", impl->playback_info.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)
 | 
						if ((str = pw_properties_get(impl->props, "server.address")) == NULL)
 | 
				
			||||||
		str = DEFAULT_SERVER;
 | 
							str = DEFAULT_SERVER;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1056,9 +1042,10 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
 | 
				
			||||||
		uint16_t port = 0;
 | 
							uint16_t port = 0;
 | 
				
			||||||
		bool ipv4;
 | 
							bool ipv4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (pw_net_get_ip(&s->addr, ip, sizeof(ip), &ipv4, &port) >= 0)
 | 
							if (pw_net_get_ip(&s->addr, ip, sizeof(ip), &ipv4, &port) < 0)
 | 
				
			||||||
			fprintf(f, " \"%s%s%s:%d\"", ipv4 ? "" : "[",
 | 
								continue;
 | 
				
			||||||
					ip, ipv4 ? "" : "]", port);
 | 
					
 | 
				
			||||||
 | 
							fprintf(f, " \"%s%s%s:%d\"", ipv4 ? "" : "[", ip, ipv4 ? "" : "]", port);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	fprintf(f, " ]");
 | 
						fprintf(f, " ]");
 | 
				
			||||||
	fclose(f);
 | 
						fclose(f);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,6 +35,8 @@
 | 
				
			||||||
#include "module-protocol-pulse/format.h"
 | 
					#include "module-protocol-pulse/format.h"
 | 
				
			||||||
#include "module-zeroconf-discover/avahi-poll.h"
 | 
					#include "module-zeroconf-discover/avahi-poll.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "network-utils.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** \page page_module_snapcast_discover Snapcast Discover
 | 
					/** \page page_module_snapcast_discover Snapcast Discover
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Automatically creates a Snapcast sink device based on zeroconf
 | 
					 * Automatically creates a Snapcast sink device based on zeroconf
 | 
				
			||||||
| 
						 | 
					@ -465,10 +467,10 @@ static int add_snapcast_stream(struct impl *impl, struct tunnel *t,
 | 
				
			||||||
	while (spa_json_get_string(&it[1], v, sizeof(v)) > 0) {
 | 
						while (spa_json_get_string(&it[1], v, sizeof(v)) > 0) {
 | 
				
			||||||
		t->server_address = strdup(v);
 | 
							t->server_address = strdup(v);
 | 
				
			||||||
		snapcast_connect(t);
 | 
							snapcast_connect(t);
 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return -ENOENT;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline uint32_t format_from_name(const char *name, size_t len)
 | 
					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;
 | 
						ifreq.ifr_ifindex = interface;
 | 
				
			||||||
	ioctl(fd, SIOCGIFNAME, &ifreq, sizeof(ifreq));
 | 
						ioctl(fd, SIOCGIFNAME, &ifreq, sizeof(ifreq));
 | 
				
			||||||
	pw_properties_setf(props, "snapcast.ifname", "%s", ifreq.ifr_name);
 | 
						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;
 | 
						struct ifaddrs *if_addr, *ifp;
 | 
				
			||||||
	if (getifaddrs(&if_addr) < 0)
 | 
						if (getifaddrs(&if_addr) < 0)
 | 
				
			||||||
| 
						 | 
					@ -711,6 +714,10 @@ static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiPr
 | 
				
			||||||
					family == AF_INET ? "" : "[",
 | 
										family == AF_INET ? "" : "[",
 | 
				
			||||||
					hbuf,
 | 
										hbuf,
 | 
				
			||||||
					family == AF_INET ? "" : "]");
 | 
										family == AF_INET ? "" : "]");
 | 
				
			||||||
 | 
								pw_properties_setf(props, "local.ifaddress", "%s%s%s",
 | 
				
			||||||
 | 
										family == AF_INET ? "" : "[",
 | 
				
			||||||
 | 
										hbuf,
 | 
				
			||||||
 | 
										family == AF_INET ? "" : "]");
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			pw_log_warn("error: %m %d %s", res, gai_strerror(res));
 | 
								pw_log_warn("error: %m %d %s", res, gai_strerror(res));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,6 +38,44 @@ static inline int pw_net_parse_address(const char *address, uint16_t port,
 | 
				
			||||||
	return 0;
 | 
						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)
 | 
					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) {
 | 
						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;
 | 
						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 */
 | 
					#endif /* NETWORK_UTILS_H */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue