char *get_version_string() {
char *version_string = malloc(1024);
- if (version_string) {
+ if (version_string) {
#ifdef CONFIG_USE_GIT_VERSION_STRING
- if (git_version_string[0] != '\0')
+ if (git_version_string[0] != '\0')
strcpy(version_string, git_version_string);
else
#endif
return result;
}
-char *get_device_id() {
- char *response = NULL;
+int get_device_id(uint8_t *id, int int_length) {
+ int response = 0;
struct ifaddrs *ifaddr = NULL;
struct ifaddrs *ifa = NULL;
int i = 0;
+ uint8_t *t = id;
+ for (i = 0; i < int_length ; i++) {
+ *t++ = 0;
+ }
if (getifaddrs(&ifaddr) == -1) {
debug(1, "getifaddrs");
+ response = -1;
} else {
+ t = id;
int found = 0;
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if ((ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_PACKET)) {
- char obf[256] = {0};
- char *obfp = obf;
struct sockaddr_ll *s = (struct sockaddr_ll *)ifa->ifa_addr;
if ((strcmp(ifa->ifa_name, "lo") != 0) && (found == 0)) {
- for (i = 0; i < s->sll_halen; i++) {
- snprintf(obfp, 4, "%02x:", s->sll_addr[i]);
- obfp += 3;
+ for (i = 0; ((i < s->sll_halen) && (i < int_length)); i++) {
+ *t++ = s->sll_addr[i];
}
- obfp -= 1;
- *obfp = 0;
- response = strdup(obf);
found = 1;
}
}
// char *errfile;
char *configfile;
char *regtype; // The regtype is the service type followed by the protocol, separated by a dot, by
- // default “_raop._tcp.”.
+ // default “_raop._tcp.” for AirPlay 1.
+ char *regtype2; // The regtype is the service type followed by the protocol, separated by a dot, by
+ // default “_raop._tcp.” for AirPlay 2.
char *interface; // a string containg the interface name, or NULL if nothing specified
int interface_index; // only valid if the interface string is non-NULL
double audio_backend_buffer_desired_length; // this will be the length in seconds of the
// negative otherwise
int32_t mod32Difference(uint32_t a, uint32_t b);
-char *get_device_id();
+
+int get_device_id(uint8_t *id, int int_length);
+
+#ifdef CONFIG_USE_GIT_VERSION_STRING
+extern char git_version_string[];
+#endif
+
#endif // _COMMON_H
#endif
NULL};
-void mdns_register(char **txt_records) {
- char *mdns_service_name = alloca(strlen(config.service_name) + 14);
- char *p = mdns_service_name;
-#ifndef CONFIG_AIRPLAY_2
+void mdns_register(char **txt_records, char **secondary_txt_records) {
+ char *ap1_service_name = alloca(strlen(config.service_name) + 14);
+ char *p = ap1_service_name;
int i;
for (i = 0; i < 6; i++) {
snprintf(p, 3, "%02X", config.hw_addr[i]);
p += 2;
}
*p++ = '@';
-#endif
strcpy(p, config.service_name);
mdns_backend **b = NULL;
for (b = mdns_backends; *b; b++) {
if (strcmp((*b)->name, config.mdns_name) != 0) // Not the one we are looking for
continue;
- int error = (*b)->mdns_register(mdns_service_name, config.port, txt_records);
+ int error = (*b)->mdns_register(ap1_service_name, config.service_name, config.port, txt_records, secondary_txt_records);
if (error >= 0) {
config.mdns = *b;
}
if (*b == NULL)
warn("%s mDNS backend not found");
} else {
+ // default -- pick the first back end
for (b = mdns_backends; *b; b++) {
- int error = (*b)->mdns_register(mdns_service_name, config.port, txt_records);
+ int error = (*b)->mdns_register(ap1_service_name, config.service_name, config.port, txt_records, secondary_txt_records);
if (error >= 0) {
config.mdns = *b;
break;
extern int mdns_pid;
void mdns_unregister(void);
-void mdns_register(char **txt_records);
+void mdns_register(char **txt_records, char **secondary_txt_records);
void mdns_dacp_monitor_start();
void mdns_dacp_monitor_stop(void);
void mdns_dacp_monitor_set_id(const char *dacp_id);
typedef struct {
char *name;
- int (*mdns_register)(char *apname, int port, char **txt_records);
+ int (*mdns_register)(char *ap1name, char *ap2name, int port, char **txt_records, char **secondary_txt_records);
void (*mdns_unregister)(void);
void (*mdns_dacp_monitor_start)();
void (*mdns_dacp_monitor_set_id)(const char *);
dacp_browser_struct private_dbs;
AvahiStringList *text_record_string_list = NULL;
+AvahiStringList *ap2_text_record_string_list = NULL;
// static AvahiServiceBrowser *sb = NULL;
static AvahiClient *client = NULL;
// static AvahiThreadedPoll *service_poll = NULL;
static char *service_name = NULL;
+char *ap2_service_name = NULL;
+
static int port = 0;
static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface,
/* A service name collision with a remote service
* happened. Let's pick a new name */
- debug(2, "avahi name collision -- look for another");
+ debug(1, "avahi name collision -- look for another");
n = avahi_alternative_service_name(service_name);
if (service_name)
avahi_free(service_name);
selected_interface = config.interface_index;
else
selected_interface = AVAHI_IF_UNSPEC;
- if (text_record_string_list) {
+ if (ap2_text_record_string_list) {
+ ret = avahi_entry_group_add_service_strlst(group, selected_interface, AVAHI_PROTO_UNSPEC, 0,
+ ap2_service_name, config.regtype2, NULL, NULL, port,
+ ap2_text_record_string_list);
+ }
+ if ((ret == 0) && (text_record_string_list)) {
ret = avahi_entry_group_add_service_strlst(group, selected_interface, AVAHI_PROTO_UNSPEC, 0,
service_name, config.regtype, NULL, NULL, port,
text_record_string_list);
+ }
if (ret == 0) {
ret = avahi_entry_group_commit(group);
debug(2, "avahi: avahi_entry_group_commit %d", ret);
} else {
debug(1, "avahi: unexpected positive return");
}
- } else {
- debug(1, "Can't find a valid text_record_string_list");
- }
}
}
}
}
-static int avahi_register(char *srvname, int srvport, char **txt_records) {
+static int avahi_register(char *ap1name, char *ap2name, int srvport, char **txt_records,char **secondary_txt_records) {
// debug(1, "avahi_register.");
- service_name = strdup(srvname);
+ service_name = strdup(ap1name);
+ if (ap2name != NULL)
+ ap2_service_name = strdup(ap2name);
text_record_string_list = avahi_string_list_new_from_array((const char **)txt_records, -1);
+ if (secondary_txt_records != NULL)
+ ap2_text_record_string_list = avahi_string_list_new_from_array((const char **)secondary_txt_records, -1);
+
port = srvport;
int err;
if (service_name) {
debug(2, "avahi: free the service name.");
free(service_name);
+ service_name = NULL;
} else
debug(1, "avahi attempt to free NULL service name");
+ if (ap2_service_name) {
+ debug(2, "avahi: free the ap2 service_name.");
+ free(ap2_service_name);
+ ap2_service_name = NULL;
+ }
+
if (text_record_string_list) {
debug(2, "avahi free text_record_string_list");
avahi_string_list_free(text_record_string_list);
- }
+ text_record_string_list = NULL;
+ } else
+ debug(1, "avahi attempt to free NULL text_record_string_list");
+
- service_name = NULL;
+ if (ap2_text_record_string_list) {
+ debug(2, "avahi free ap_2text_record_string_list");
+ avahi_string_list_free(ap2_text_record_string_list);
+ ap2_text_record_string_list = NULL;
+ }
}
void avahi_dacp_monitor_start(void) {
*p = 0;
p += 2;
msg_add_header(msg, line, p);
- debug(3, " %s: %s.", line, p);
+ // debug(3, " %s: %s.", line, p);
return -1;
} else {
char *cl = msg_get_header(msg, "Content-Length");
resp->respcode = 200;
msg_add_header(resp, "Public",
"ANNOUNCE, SETUP, RECORD, "
- "PAUSE, FLUSH, TEARDOWN, "
- "OPTIONS, GET_PARAMETER, SET_PARAMETER, GET");
+ "PAUSE, FLUSH, FLUSHBUFFERED, TEARDOWN, "
+ "OPTIONS, POST, GET, PUT");
}
void handle_teardown_2(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req,
char *hdr = msg_get_header(req, "Apple-Challenge");
if (!hdr)
return;
-
+ debug(1,"Apple Challenge");
SOCKADDR fdsa;
socklen_t sa_len = sizeof(fdsa);
getsockname(fd, (struct sockaddr *)&fdsa, &sa_len);
maxfd = sockfd[i];
}
+ char **t1; // ap1 test records
+ char **t2; // two text records
+
+ t1 = NULL;
+ t2 = NULL;
+
// make up a txt record
char *txt_records[64];
char **p = txt_records;
+ t1 = p; // first set of text records
+
+// make up a firmware version
+ char firmware_version[64];
+#ifdef CONFIG_USE_GIT_VERSION_STRING
+ if (git_version_string[0] != '\0')
+ snprintf(firmware_version, sizeof(firmware_version), "fv=%s",git_version_string);
+ else
+#endif
+ snprintf(firmware_version, sizeof(firmware_version), "fv=%s",PACKAGE_VERSION);
#ifdef CONFIG_AIRPLAY_2
- *p++ = "srcvers=366.0";
- char deviceIdString[64];
- snprintf(deviceIdString, sizeof(deviceIdString), "deviceid=%s", config.airplay_device_id);
- *p++ = deviceIdString;
- // features is a 64 bit number, least significant 32 bits
- char featuresString[64];
+ char ap1_featuresString[64];
uint64_t features_hi = config.airplay_features;
features_hi = (features_hi >> 32) & 0xffffffff;
uint64_t features_lo = config.airplay_features;
features_lo = features_lo & 0xffffffff;
- snprintf(featuresString, sizeof(featuresString), "features=0x%" PRIx64 ",0x%" PRIx64 "",
+ snprintf(ap1_featuresString, sizeof(ap1_featuresString), "ft=0x%" PRIX64 ",0x%" PRIX64 "",
features_lo, features_hi);
- *p++ = featuresString;
- char statusflagsString[32];
- snprintf(statusflagsString, sizeof(statusflagsString), "flags=0x%" PRIx32,
- config.airplay_statusflags);
- *p++ = statusflagsString;
- *p++ = "protovers=1.1";
- *p++ = "acl=0";
- *p++ = "rsf=0x0";
- *p++ = "fv=p20.78000.12";
- *p++ = "model=SPS";
- char piString[64];
- snprintf(piString, sizeof(piString), "pi=%s", config.airplay_pi);
- *p++ = piString;
- char gidString[64];
- snprintf(gidString, sizeof(gidString), "gid=%s", config.airplay_gid);
- *p++ = gidString;
- *p++ = "gcgl=0";
char pkString[128];
snprintf(pkString, sizeof(pkString), "pk=");
pkString_make(pkString + strlen("pk="), sizeof(pkString) - strlen("pk="),
config.airplay_device_id);
+
+// the ap1 text record is different if it is set up for ap2
+ *p++ = "cn=0,1";
+ *p++ = "da=true";
+ *p++ = "et=0,4";
+ *p++ = ap1_featuresString;
+ *p++ = firmware_version;
+ *p++ = "md=2";
+ *p++ = "am=SPS";
+ *p++ = "sf=0x4";
+ *p++ = "tp=UDP";
+ *p++ = "vn=65537";
+ *p++ = "vs=366.0";
*p++ = pkString;
*p++ = NULL;
+
#else
// here, just replicate what happens in mdns.h when using those #defines
*p++ = "sf=0x4";
- *p++ = "fv=76400.10";
+ *p++ = firmware_version;
*p++ = "am=ShairportSync";
*p++ = "vs=105.1";
*p++ = "tp=TCP,UDP";
*p++ = "ek=1";
*p++ = "cn=0,1";
*p++ = "ch=2";
- *p++ = "am=ShairportSync";
*p++ = "txtvers=1";
if (config.password == 0)
*p++ = "pw=false";
else
*p++ = "pw=true";
+
+ *p++ = NULL;
+#endif
+
+
+#ifdef CONFIG_AIRPLAY_2
+ // make up a secondary set of text records
+ char *secondary_txt_records[64];
+ p = secondary_txt_records;
+ t2 = p; // second set of text records in AirPlay 2 only
+
+ *p++ = "srcvers=366.0";
+ char deviceIdString[64];
+ snprintf(deviceIdString, sizeof(deviceIdString), "deviceid=%s", config.airplay_device_id);
+ *p++ = deviceIdString;
+ char featuresString[64];
+ snprintf(featuresString, sizeof(featuresString), "features=0x%" PRIX64 ",0x%" PRIX64 "",
+ features_lo, features_hi);
+ *p++ = featuresString;
+ char statusflagsString[32];
+ snprintf(statusflagsString, sizeof(statusflagsString), "flags=0x%" PRIX32,
+ config.airplay_statusflags);
+ *p++ = statusflagsString;
+ *p++ = "protovers=1.1";
+ *p++ = "acl=0";
+ *p++ = "rsf=0x0";
+ *p++ = firmware_version;
+ *p++ = "model=SPS";
+ char piString[64];
+ snprintf(piString, sizeof(piString), "pi=%s", config.airplay_pi);
+ *p++ = piString;
+ char gidString[64];
+ snprintf(gidString, sizeof(gidString), "gid=%s", config.airplay_gid);
+ *p++ = gidString;
+ *p++ = "gcgl=0";
+ *p++ = pkString;
*p++ = NULL;
#endif
- /*
- debug(1,"txt_record:");
- p = txt_records;
- while (*p)
- debug(1," %s", *p++);
- */
- mdns_register(txt_records);
+ mdns_register(t1,t2);
pthread_setcancelstate(oldState, NULL);
int acceptfd;
/* if the regtype hasn't been set, do it now */
if (config.regtype == NULL)
-#ifdef CONFIG_AIRPLAY_2
- config.regtype = strdup("_airplay._tcp");
-#else
config.regtype = strdup("_raop._tcp");
+#ifdef CONFIG_AIRPLAY_2
+ if (config.regtype2 == NULL)
+ config.regtype2 = strdup("_airplay._tcp");
#endif
if (tdebuglev != 0)
if (config.regtype)
free(config.regtype);
+#ifdef CONFIG_AIRPLAY_2
+ if (config.regtype2)
+ free(config.regtype2);
+#endif
+
#ifdef CONFIG_LIBDAEMON
if (this_is_the_daemon_process) {
// set defaults
+ // get a device id -- the first non-local MAC address
+ get_device_id((uint8_t *)&config.hw_addr, 6);
+
// get the endianness
union {
uint32_t u32;
// required (transient pairing), no Homekit access control 0x204: Audio cable attached,
// OneTimePairingRequired 0x604: Audio cable attached, OneTimePairingRequired, allow Homekit
// access control
+
+ // note: 0x300401F4A00 works but with weird delays and stuff
+
config.airplay_statusflags = 0x4;
// Set to NULL to work with transient pairing
config.airplay_pin = NULL;
- // get a device id -- the first non-local MAC address, not necessarily the one in use
- config.airplay_device_id = get_device_id();
- if (config.airplay_device_id) {
- debug(1, "Started in Airplay 2 mode on device \"%s\"!", config.airplay_device_id);
- } else
- debug(1, "Started in Airplay 2 mode!");
+
+ // use the config.hw_addr to generated an airplay_device_id
+ {
+ char obf[256] = {0};
+ char *obfp = obf;
+ int i;
+ for (i = 0; i < 6; i++) {
+ snprintf(obfp, 4, "%02X:", config.hw_addr[i]);
+ obfp += 3;
+ }
+ obfp -= 1;
+ *obfp = 0;
+ config.airplay_device_id = strdup(obf);
+ }
+ debug(1, "Started in Airplay 2 mode on device \"%s\"!", config.airplay_device_id);
// now generate a UUID
// from https://stackoverflow.com/questions/51053568/generating-a-random-uuid-in-c
debug(1, "loudness is %d.", config.loudness);
debug(1, "loudness reference level is %f", config.loudness_reference_volume_db);
- uint8_t ap_md5[16];
-
#ifdef CONFIG_SOXR
pthread_create(&soxr_time_check_thread, NULL, &soxr_time_check, NULL);
#endif
+/*
+ uint8_t ap_md5[16];
+
#ifdef CONFIG_OPENSSL
MD5_CTX ctx;
MD5_Init(&ctx);
md5_update(&tctx, (unsigned char *)config.service_name, strlen(config.service_name));
md5_finish(&tctx, ap_md5);
#endif
+
memcpy(config.hw_addr, ap_md5, sizeof(config.hw_addr));
+*/
+
#ifdef CONFIG_METADATA
metadata_init(); // create the metadata pipe if necessary
#endif