blob: e101fc0bbed18795d7faf407a637acb92d6833db [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001--- a/main.c
2+++ b/main.c
3@@ -93,6 +93,7 @@ static void usage(const char *argv0) {
4 #if IR
5 " -i [<filename>]\tEnable lirc remote control support (lirc config file ~/.lircrc used if filename not specified)\n"
6 #endif
7+ " -I <interface>\tNetwork interface used to send discovery\n"
8 " -m <mac addr>\t\tSet mac address, format: ab:cd:ef:12:34:56\n"
9 " -M <modelname>\tSet the squeezelite player model name sent to the server (default: " MODEL_NAME_STRING ")\n"
10 " -n <name>\t\tSet the player name\n"
11@@ -285,6 +286,8 @@ int main(int argc, char **argv) {
12 extern bool user_rates;
13 char *logfile = NULL;
14 u8_t mac[6];
15+ char *iface = NULL;
16+ in_addr_t bcast_addr = 0;
17 unsigned stream_buf_size = STREAMBUF_SIZE;
18 unsigned output_buf_size = 0; // set later
19 unsigned rates[MAX_SUPPORTED_SAMPLERATES] = { 0 };
20@@ -325,6 +328,7 @@ int main(int argc, char **argv) {
21
22 int maxSampleRate = 0;
23
24+ memset(mac, 0, sizeof(mac));
25 char *optarg = NULL;
26 int optind = 1;
27 int i;
28@@ -332,8 +336,6 @@ int main(int argc, char **argv) {
29 #define MAXCMDLINE 512
30 char cmdline[MAXCMDLINE] = "";
31
32- get_mac(mac);
33-
34 for (i = 0; i < argc && (strlen(argv[i]) + strlen(cmdline) + 2 < MAXCMDLINE); i++) {
35 strcat(cmdline, argv[i]);
36 strcat(cmdline, " ");
37@@ -341,7 +343,7 @@ int main(int argc, char **argv) {
38
39 while (optind < argc && strlen(argv[optind]) >= 2 && argv[optind][0] == '-') {
40 char *opt = argv[optind] + 1;
41- if (strstr("oabcCdefmMnNpPrs"
42+ if (strstr("oabcCdefImMnNpPrs"
43 #if ALSA
44 "UVO"
45 #endif
46@@ -442,6 +444,9 @@ int main(int argc, char **argv) {
47 case 'f':
48 logfile = optarg;
49 break;
50+ case 'I':
51+ iface = optarg;
52+ break;
53 case 'm':
54 {
55 int byte = 0;
56@@ -755,6 +760,11 @@ int main(int argc, char **argv) {
57 winsock_init();
58 #endif
59
60+ if (!(bcast_addr = get_iface_info(log_slimproto, iface, mac))) {
61+ fprintf(stderr, "Error binding to network or none given\n");
62+ exit(1);
63+ }
64+
65 stream_init(log_stream, stream_buf_size);
66
67 if (!strcmp(output_device, "-")) {
68@@ -798,7 +808,7 @@ int main(int argc, char **argv) {
69 exit(1);
70 }
71
72- slimproto(log_slimproto, server, mac, name, namefile, modelname, maxSampleRate);
73+ slimproto(log_slimproto, server, bcast_addr, mac, name, namefile, modelname, maxSampleRate);
74
75 decode_close();
76 stream_close();
77--- a/slimproto.c
78+++ b/slimproto.c
79@@ -113,7 +113,7 @@ void send_packet(u8_t *packet, size_t le
80 }
81 }
82
83-static void sendHELO(bool reconnect, const char *fixed_cap, const char *var_cap, u8_t mac[6]) {
84+static void sendHELO(bool reconnect, const char *fixed_cap, const char *var_cap, u8_t *mac) {
85 #define BASE_CAP "Model=squeezelite,AccuratePlayPoints=1,HasDigitalOut=1,HasPolarityInversion=1,Firmware=" VERSION
86 #define SSL_CAP "CanHTTPS=1"
87 const char *base_cap;
88@@ -761,7 +761,7 @@ void wake_controller(void) {
89 wake_signal(wake_e);
90 }
91
92-in_addr_t discover_server(char *default_server) {
93+in_addr_t discover_server(char *default_server, in_addr_t bcast_addr) {
94 struct sockaddr_in d;
95 struct sockaddr_in s;
96 char *buf;
97@@ -778,7 +778,7 @@ in_addr_t discover_server(char *default_
98 memset(&d, 0, sizeof(d));
99 d.sin_family = AF_INET;
100 d.sin_port = htons(PORT);
101- d.sin_addr.s_addr = htonl(INADDR_BROADCAST);
102+ d.sin_addr.s_addr = bcast_addr;
103
104 pollinfo.fd = disc_sock;
105 pollinfo.events = POLLIN;
106@@ -813,7 +813,7 @@ in_addr_t discover_server(char *default_
107 #define FIXED_CAP_LEN 256
108 #define VAR_CAP_LEN 128
109
110-void slimproto(log_level level, char *server, u8_t mac[6], const char *name, const char *namefile, const char *modelname, int maxSampleRate) {
111+void slimproto(log_level level, char *server, in_addr_t bcast_addr, u8_t mac[6], const char *name, const char *namefile, const char *modelname, int maxSampleRate) {
112 struct sockaddr_in serv_addr;
113 static char fixed_cap[FIXED_CAP_LEN], var_cap[VAR_CAP_LEN] = "";
114 bool reconnect = false;
115@@ -834,7 +834,7 @@ void slimproto(log_level level, char *se
116 }
117
118 if (!slimproto_ip) {
119- slimproto_ip = discover_server(server);
120+ slimproto_ip = discover_server(server, bcast_addr);
121 }
122
123 if (!slimproto_port) {
124@@ -915,7 +915,7 @@ void slimproto(log_level level, char *se
125
126 // rediscover server if it was not set at startup
127 if (!server && ++failed_connect > 5) {
128- slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL);
129+ slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL, bcast_addr);
130 }
131
132 } else {
133--- a/squeezelite.h
134+++ b/squeezelite.h
135@@ -455,7 +455,7 @@ char* strcasestr(const char *haystack, c
136
137 char *next_param(char *src, char c);
138 u32_t gettime_ms(void);
139-void get_mac(u8_t *mac);
140+in_addr_t get_iface_info(log_level level, char *iface, u8_t *mac);
141 void set_nonblock(sockfd s);
142 int connect_timeout(sockfd sock, const struct sockaddr *addr, socklen_t addrlen, int timeout);
143 void server_addr(char *server, in_addr_t *ip_ptr, unsigned *port_ptr);
144@@ -511,7 +511,7 @@ void buf_init(struct buffer *buf, size_t
145 void buf_destroy(struct buffer *buf);
146
147 // slimproto.c
148-void slimproto(log_level level, char *server, u8_t mac[6], const char *name, const char *namefile, const char *modelname, int maxSampleRate);
149+void slimproto(log_level level, char *server, in_addr_t bcast_addr, u8_t mac[6], const char *name, const char *namefile, const char *modelname, int maxSampleRate);
150 void slimproto_stop(void);
151 void wake_controller(void);
152
153--- a/utils.c
154+++ b/utils.c
155@@ -22,11 +22,11 @@
156 #include "squeezelite.h"
157
158 #if LINUX || OSX || FREEBSD
159-#include <sys/ioctl.h>
160+#include <sys/types.h>
161 #include <net/if.h>
162-#include <netdb.h>
163-#if FREEBSD
164 #include <ifaddrs.h>
165+#include <netdb.h>
166+#if FREEBSD || OSX
167 #include <net/if_dl.h>
168 #include <net/if_types.h>
169 #endif
170@@ -49,15 +49,11 @@
171 #include <ctype.h>
172 #endif
173 #endif
174-#if OSX
175-#include <net/if_dl.h>
176-#include <net/if_types.h>
177-#include <ifaddrs.h>
178-#include <netdb.h>
179-#endif
180
181 #include <fcntl.h>
182
183+static log_level loglevel;
184+
185 // logging functions
186 const char *logtime(void) {
187 static char buf[100];
188@@ -119,58 +115,94 @@ u32_t gettime_ms(void) {
189 #endif
190 }
191
192-// mac address
193-#if LINUX && !defined(SUN)
194-// search first 4 interfaces returned by IFCONF
195-void get_mac(u8_t mac[]) {
196- char *utmac;
197- struct ifconf ifc;
198- struct ifreq *ifr, *ifend;
199- struct ifreq ifreq;
200- struct ifreq ifs[4];
201+// Get broadcast address for interface (given or first available)
202+// Return MAC address if none given
203+#if LINUX || OSX || FREEBSD
204
205- utmac = getenv("UTMAC");
206- if (utmac)
207- {
208- if ( strlen(utmac) == 17 )
209- {
210- if (sscanf(utmac,"%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx",
211- &mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]) == 6)
212- {
213- return;
214- }
215- }
216+in_addr_t get_iface_info(log_level level, char *iface, u8_t *mac) {
217+ struct ifaddrs *addrs, *ifa;
218+ struct sockaddr *sdl;
219+ char ifname[16];
220+ unsigned char *ptr;
221+ in_addr_t bcast_addr = 0;
222+ int have_mac = 0, have_ifname = 0;
223+
224+ loglevel = level;
225+
226+ // Check for non-zero MAC
227+ if (mac[0] | mac[1] | mac[2] != 0)
228+ have_mac = 1;
229+
230+ // Copy interface name, if it was provided.
231+ if (iface != NULL) {
232+ if (strlen(iface) > sizeof(ifname))
233+ return -1;
234
235+ strncpy(ifname, iface, sizeof(ifname) - 1);
236+ have_ifname = 1;
237 }
238
239- mac[0] = mac[1] = mac[2] = mac[3] = mac[4] = mac[5] = 0;
240+ if (getifaddrs(&addrs) == 0) {
241+ //iterate to find corresponding ethernet address
242+ for (ifa = addrs; ifa; ifa = ifa->ifa_next) {
243+ // Skip LOOPBACK interfaces, DOWN interfaces and interfaces that
244+ // don't support BROADCAST.
245+ if (ifa->ifa_flags & IFF_LOOPBACK
246+ || !ifa->ifa_flags & IFF_UP
247+ || !ifa->ifa_flags & IFF_BROADCAST) {
248+ continue;
249+ }
250
251- int s = socket(AF_INET, SOCK_DGRAM, 0);
252+ if (!have_ifname) {
253+ // We have found a valid interface name. Keep it.
254+ strncpy(ifname, ifa->ifa_name, sizeof(ifname) - 1);
255+ have_ifname = 1;
256+ } else {
257+ if (strncmp(ifname, ifa->ifa_name, sizeof(ifname)) != 0) {
258+ // This is not the interface we're looking for.
259+ continue;
260+ }
261+ }
262
263- ifc.ifc_len = sizeof(ifs);
264- ifc.ifc_req = ifs;
265
266- if (ioctl(s, SIOCGIFCONF, &ifc) == 0) {
267- ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
268+ // Check address family.
269+ if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET &&
270+ ((struct sockaddr_in *)ifa->ifa_broadaddr)->sin_addr.s_addr != 0) {
271+ // Get broadcast address and MAC address
272+ bcast_addr = ((struct sockaddr_in *)ifa->ifa_broadaddr)->sin_addr.s_addr;
273+ break;
274+ }
275+ else {
276+ // Address is not IPv4
277+ if (iface == NULL)
278+ have_ifname = 0;
279+ }
280+ }
281
282- for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
283- if (ifr->ifr_addr.sa_family == AF_INET) {
284-
285- strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name) - 1);
286- if (ioctl (s, SIOCGIFHWADDR, &ifreq) == 0) {
287- memcpy(mac, ifreq.ifr_hwaddr.sa_data, 6);
288- if (mac[0]+mac[1]+mac[2] != 0) {
289- break;
290- }
291+ // Find MAC address matching interface
292+ if (!have_mac && bcast_addr != 0) {
293+ for (ifa = addrs; ifa; ifa = ifa->ifa_next) {
294+ if (ifa->ifa_addr && ifa->ifa_addr->sa_family == PF_PACKET &&
295+ strncmp(ifname, ifa->ifa_name, sizeof(ifname)) == 0) {
296+ sdl = (struct sockaddr *)(ifa->ifa_addr);
297+ ptr = (unsigned char *)sdl->sa_data;
298+ memcpy(mac, ptr + 10, 6);
299+ have_mac = 1;
300 }
301 }
302 }
303+
304+ freeifaddrs(addrs);
305 }
306
307- close(s);
308+ LOG_INFO("Interface: %s, broadcast: %08X, macaddr = %02x:%02x:%02x:%02x:%02x:%02x",
309+ ifname, bcast_addr, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
310+
311+ return bcast_addr;
312 }
313 #endif
314
315+
316 #if SUN
317 void get_mac(u8_t mac[]) {
318 struct arpreq parpreq;
319@@ -237,30 +269,6 @@ void get_mac(u8_t mac[]) {
320 }
321 #endif
322
323-#if OSX || FREEBSD
324-void get_mac(u8_t mac[]) {
325- struct ifaddrs *addrs, *ptr;
326- const struct sockaddr_dl *dlAddr;
327- const unsigned char *base;
328-
329- mac[0] = mac[1] = mac[2] = mac[3] = mac[4] = mac[5] = 0;
330-
331- if (getifaddrs(&addrs) == 0) {
332- ptr = addrs;
333- while (ptr) {
334- if (ptr->ifa_addr->sa_family == AF_LINK && ((const struct sockaddr_dl *) ptr->ifa_addr)->sdl_type == IFT_ETHER) {
335- dlAddr = (const struct sockaddr_dl *)ptr->ifa_addr;
336- base = (const unsigned char*) &dlAddr->sdl_data[dlAddr->sdl_nlen];
337- memcpy(mac, base, min(dlAddr->sdl_alen, 6));
338- break;
339- }
340- ptr = ptr->ifa_next;
341- }
342- freeifaddrs(addrs);
343- }
344-}
345-#endif
346-
347 #if WIN
348 #pragma comment(lib, "IPHLPAPI.lib")
349 void get_mac(u8_t mac[]) {