--- a/main.c +++ b/main.c @@ -93,6 +93,7 @@ static void usage(const char *argv0) { #if IR " -i []\tEnable lirc remote control support (lirc config file ~/.lircrc used if filename not specified)\n" #endif + " -I \tNetwork interface used to send discovery\n" " -m \t\tSet mac address, format: ab:cd:ef:12:34:56\n" " -M \tSet the squeezelite player model name sent to the server (default: " MODEL_NAME_STRING ")\n" " -n \t\tSet the player name\n" @@ -292,6 +293,8 @@ int main(int argc, char **argv) { extern bool user_rates; char *logfile = NULL; u8_t mac[6]; + char *iface = NULL; + in_addr_t bcast_addr = 0; unsigned stream_buf_size = STREAMBUF_SIZE; unsigned output_buf_size = 0; // set later unsigned rates[MAX_SUPPORTED_SAMPLERATES] = { 0 }; @@ -332,6 +335,7 @@ int main(int argc, char **argv) { int maxSampleRate = 0; + memset(mac, 0, sizeof(mac)); char *optarg = NULL; int optind = 1; int i; @@ -339,8 +343,6 @@ int main(int argc, char **argv) { #define MAXCMDLINE 512 char cmdline[MAXCMDLINE] = ""; - get_mac(mac); - for (i = 0; i < argc && (strlen(argv[i]) + strlen(cmdline) + 2 < MAXCMDLINE); i++) { strcat(cmdline, argv[i]); strcat(cmdline, " "); @@ -348,7 +350,7 @@ int main(int argc, char **argv) { while (optind < argc && strlen(argv[optind]) >= 2 && argv[optind][0] == '-') { char *opt = argv[optind] + 1; - if (strstr("oabcCdefmMnNpPrsZ" + if (strstr("oabcCdefImMnNpPrsZ" #if ALSA "UVO" #endif @@ -442,6 +444,9 @@ int main(int argc, char **argv) { case 'f': logfile = optarg; break; + case 'I': + iface = optarg; + break; case 'm': { int byte = 0; @@ -755,6 +760,11 @@ int main(int argc, char **argv) { winsock_init(); #endif + if (!(bcast_addr = get_iface_info(log_slimproto, iface, mac))) { + fprintf(stderr, "Error binding to network or none given\n"); + exit(1); + } + stream_init(log_stream, stream_buf_size); if (!strcmp(output_device, "-")) { @@ -801,7 +811,7 @@ int main(int argc, char **argv) { exit(1); } - slimproto(log_slimproto, server, mac, name, namefile, modelname, maxSampleRate); + slimproto(log_slimproto, server, bcast_addr, mac, name, namefile, modelname, maxSampleRate); decode_close(); stream_close(); --- a/slimproto.c +++ b/slimproto.c @@ -119,7 +119,7 @@ void send_packet(u8_t *packet, size_t le } } -static void sendHELO(bool reconnect, const char *fixed_cap, const char *var_cap, u8_t mac[6]) { +static void sendHELO(bool reconnect, const char *fixed_cap, const char *var_cap, u8_t *mac) { #define BASE_CAP "Model=squeezelite,AccuratePlayPoints=1,HasDigitalOut=1,HasPolarityInversion=1,Balance=1,Firmware=" VERSION #define SSL_CAP "CanHTTPS=1" const char *base_cap; @@ -768,7 +768,7 @@ void wake_controller(void) { wake_signal(wake_e); } -in_addr_t discover_server(char *default_server) { +in_addr_t discover_server(char *default_server, in_addr_t bcast_addr) { struct sockaddr_in d; struct sockaddr_in s; char *buf; @@ -785,7 +785,7 @@ in_addr_t discover_server(char *default_ memset(&d, 0, sizeof(d)); d.sin_family = AF_INET; d.sin_port = htons(PORT); - d.sin_addr.s_addr = htonl(INADDR_BROADCAST); + d.sin_addr.s_addr = bcast_addr; pollinfo.fd = disc_sock; pollinfo.events = POLLIN; @@ -820,7 +820,7 @@ in_addr_t discover_server(char *default_ #define FIXED_CAP_LEN 256 #define VAR_CAP_LEN 128 -void slimproto(log_level level, char *server, u8_t mac[6], const char *name, const char *namefile, const char *modelname, int maxSampleRate) { +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) { struct sockaddr_in serv_addr; static char fixed_cap[FIXED_CAP_LEN], var_cap[VAR_CAP_LEN] = ""; bool reconnect = false; @@ -841,7 +841,7 @@ void slimproto(log_level level, char *se } if (!slimproto_ip) { - slimproto_ip = discover_server(server); + slimproto_ip = discover_server(server, bcast_addr); } if (!slimproto_port) { @@ -926,7 +926,7 @@ void slimproto(log_level level, char *se // rediscover server if it was not set at startup if (!server && ++failed_connect > 5) { - slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL); + slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL, bcast_addr); } } else { --- a/squeezelite.h +++ b/squeezelite.h @@ -456,7 +456,7 @@ char* strcasestr(const char *haystack, c char *next_param(char *src, char c); u32_t gettime_ms(void); -void get_mac(u8_t *mac); +in_addr_t get_iface_info(log_level level, char *iface, u8_t *mac); void set_nonblock(sockfd s); int connect_timeout(sockfd sock, const struct sockaddr *addr, socklen_t addrlen, int timeout); void server_addr(char *server, in_addr_t *ip_ptr, unsigned *port_ptr); @@ -513,7 +513,7 @@ void buf_init(struct buffer *buf, size_t void buf_destroy(struct buffer *buf); // slimproto.c -void slimproto(log_level level, char *server, u8_t mac[6], const char *name, const char *namefile, const char *modelname, int maxSampleRate); +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); void slimproto_stop(void); void wake_controller(void); --- a/utils.c +++ b/utils.c @@ -22,11 +22,11 @@ #include "squeezelite.h" #if LINUX || OSX || FREEBSD -#include +#include #include -#include -#if FREEBSD #include +#include +#if FREEBSD || OSX #include #include #endif @@ -49,15 +49,11 @@ #include #endif #endif -#if OSX -#include -#include -#include -#include -#endif #include +static log_level loglevel; + // logging functions const char *logtime(void) { static char buf[100]; @@ -120,58 +116,94 @@ u32_t gettime_ms(void) { #endif } -// mac address -#if LINUX && !defined(SUN) -// search first 4 interfaces returned by IFCONF -void get_mac(u8_t mac[]) { - char *utmac; - struct ifconf ifc; - struct ifreq *ifr, *ifend; - struct ifreq ifreq; - struct ifreq ifs[4]; +// Get broadcast address for interface (given or first available) +// Return MAC address if none given +#if LINUX || OSX || FREEBSD - utmac = getenv("UTMAC"); - if (utmac) - { - if ( strlen(utmac) == 17 ) - { - if (sscanf(utmac,"%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", - &mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]) == 6) - { - return; - } - } +in_addr_t get_iface_info(log_level level, char *iface, u8_t *mac) { + struct ifaddrs *addrs, *ifa; + struct sockaddr *sdl; + char ifname[16]; + unsigned char *ptr; + in_addr_t bcast_addr = 0; + int have_mac = 0, have_ifname = 0; + + loglevel = level; + + // Check for non-zero MAC + if (mac[0] | mac[1] | mac[2] != 0) + have_mac = 1; + + // Copy interface name, if it was provided. + if (iface != NULL) { + if (strlen(iface) > sizeof(ifname)) + return -1; + strncpy(ifname, iface, sizeof(ifname) - 1); + have_ifname = 1; } - mac[0] = mac[1] = mac[2] = mac[3] = mac[4] = mac[5] = 0; + if (getifaddrs(&addrs) == 0) { + //iterate to find corresponding ethernet address + for (ifa = addrs; ifa; ifa = ifa->ifa_next) { + // Skip LOOPBACK interfaces, DOWN interfaces and interfaces that + // don't support BROADCAST. + if (ifa->ifa_flags & IFF_LOOPBACK + || !ifa->ifa_flags & IFF_UP + || !ifa->ifa_flags & IFF_BROADCAST) { + continue; + } - int s = socket(AF_INET, SOCK_DGRAM, 0); + if (!have_ifname) { + // We have found a valid interface name. Keep it. + strncpy(ifname, ifa->ifa_name, sizeof(ifname) - 1); + have_ifname = 1; + } else { + if (strncmp(ifname, ifa->ifa_name, sizeof(ifname)) != 0) { + // This is not the interface we're looking for. + continue; + } + } - ifc.ifc_len = sizeof(ifs); - ifc.ifc_req = ifs; - if (ioctl(s, SIOCGIFCONF, &ifc) == 0) { - ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq)); + // Check address family. + if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET && + ((struct sockaddr_in *)ifa->ifa_broadaddr)->sin_addr.s_addr != 0) { + // Get broadcast address and MAC address + bcast_addr = ((struct sockaddr_in *)ifa->ifa_broadaddr)->sin_addr.s_addr; + break; + } + else { + // Address is not IPv4 + if (iface == NULL) + have_ifname = 0; + } + } - for (ifr = ifc.ifc_req; ifr < ifend; ifr++) { - if (ifr->ifr_addr.sa_family == AF_INET) { - - strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name) - 1); - if (ioctl (s, SIOCGIFHWADDR, &ifreq) == 0) { - memcpy(mac, ifreq.ifr_hwaddr.sa_data, 6); - if (mac[0]+mac[1]+mac[2] != 0) { - break; - } + // Find MAC address matching interface + if (!have_mac && bcast_addr != 0) { + for (ifa = addrs; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && ifa->ifa_addr->sa_family == PF_PACKET && + strncmp(ifname, ifa->ifa_name, sizeof(ifname)) == 0) { + sdl = (struct sockaddr *)(ifa->ifa_addr); + ptr = (unsigned char *)sdl->sa_data; + memcpy(mac, ptr + 10, 6); + have_mac = 1; } } } + + freeifaddrs(addrs); } - close(s); + LOG_INFO("Interface: %s, broadcast: %08X, macaddr = %02x:%02x:%02x:%02x:%02x:%02x", + ifname, bcast_addr, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + return bcast_addr; } #endif + #if SUN void get_mac(u8_t mac[]) { struct arpreq parpreq; @@ -238,30 +270,6 @@ void get_mac(u8_t mac[]) { } #endif -#if OSX || FREEBSD -void get_mac(u8_t mac[]) { - struct ifaddrs *addrs, *ptr; - const struct sockaddr_dl *dlAddr; - const unsigned char *base; - - mac[0] = mac[1] = mac[2] = mac[3] = mac[4] = mac[5] = 0; - - if (getifaddrs(&addrs) == 0) { - ptr = addrs; - while (ptr) { - if (ptr->ifa_addr->sa_family == AF_LINK && ((const struct sockaddr_dl *) ptr->ifa_addr)->sdl_type == IFT_ETHER) { - dlAddr = (const struct sockaddr_dl *)ptr->ifa_addr; - base = (const unsigned char*) &dlAddr->sdl_data[dlAddr->sdl_nlen]; - memcpy(mac, base, min(dlAddr->sdl_alen, 6)); - break; - } - ptr = ptr->ifa_next; - } - freeifaddrs(addrs); - } -} -#endif - #if WIN #pragma comment(lib, "IPHLPAPI.lib") void get_mac(u8_t mac[]) {