]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Advertise the _raop._tcp service as well as the _airplay._tcp service. Use a real...
authorMike Brady <4265913+mikebrady@users.noreply.github.com>
Mon, 24 May 2021 11:07:05 +0000 (12:07 +0100)
committerMike Brady <4265913+mikebrady@users.noreply.github.com>
Mon, 24 May 2021 11:07:05 +0000 (12:07 +0100)
common.c
common.h
mdns.c
mdns.h
mdns_avahi.c
rtsp.c
shairport.c

index ce43d004694dc7b7655d6bf7a31c7033d7a6e35b..4bdb78abe72095b08fcec708907bc48775127d58 100644 (file)
--- a/common.c
+++ b/common.c
@@ -1609,9 +1609,9 @@ void pthread_cleanup_debug_mutex_unlock(void *arg) { pthread_mutex_unlock((pthre
 
 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
@@ -1939,29 +1939,29 @@ int32_t mod32Difference(uint32_t a, uint32_t b) {
   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;
         }
       }
index 4d8dcc1d75b1f299e03ee23e290b07911a8e2e8e..48769527ce284400b27c7f2d2d0bc1782cc9db47 100644 (file)
--- a/common.h
+++ b/common.h
@@ -209,7 +209,9 @@ typedef struct {
   // 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
@@ -489,7 +491,13 @@ void *memdup(const void *mem, size_t size);
 // 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
 
diff --git a/mdns.c b/mdns.c
index 7eb1f4330aa84862f3ea38cb0e281e762d862383..073e346cb4492eb3f49a8152723efb0939ade904 100644 (file)
--- a/mdns.c
+++ b/mdns.c
@@ -64,17 +64,15 @@ static mdns_backend *mdns_backends[] = {
 #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;
@@ -83,7 +81,7 @@ void mdns_register(char **txt_records) {
     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;
       }
@@ -93,8 +91,9 @@ void mdns_register(char **txt_records) {
     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;
diff --git a/mdns.h b/mdns.h
index 73c15e8e9ca084188b257a930532b3b261d78f33..30900c7854ddfb37d8aef040ee52b7bdcb25a32b 100644 (file)
--- a/mdns.h
+++ b/mdns.h
@@ -8,7 +8,7 @@
 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);
@@ -17,7 +17,7 @@ void mdns_ls_backends(void);
 
 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 *);
index 4ab991c77407e2435d9e8b851fb7f717f50a3f38..4873639c05f2c7615ecc8f00a5004fbedd51e1dc 100644 (file)
@@ -62,6 +62,7 @@ typedef struct {
 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;
@@ -71,6 +72,8 @@ static AvahiThreadedPoll *tpoll = 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,
@@ -187,7 +190,7 @@ static void egroup_callback(AvahiEntryGroup *g, AvahiEntryGroupState state,
 
     /* 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);
@@ -238,10 +241,16 @@ static void register_service(AvahiClient *c) {
       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);
@@ -252,9 +261,6 @@ static void register_service(AvahiClient *c) {
       } else {
         debug(1, "avahi: unexpected positive return");
       }
-    } else {
-      debug(1, "Can't find a valid text_record_string_list");
-    }
   }
 }
 
@@ -312,12 +318,17 @@ static void client_callback(AvahiClient *c, AvahiClientState state,
   }
 }
 
-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;
@@ -362,15 +373,29 @@ static void avahi_unregister(void) {
   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) {
diff --git a/rtsp.c b/rtsp.c
index da94052ede699d0ff3e1c4ebf41f861812ae36e5..ace0e98f1c24b0e27f257df3919d2bc956e3c6f7 100644 (file)
--- a/rtsp.c
+++ b/rtsp.c
@@ -696,7 +696,7 @@ int msg_handle_line(rtsp_message **pmsg, char *line) {
     *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");
@@ -1714,8 +1714,8 @@ void handle_options(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *
   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,
@@ -3414,7 +3414,7 @@ static void apple_challenge(int fd, rtsp_message *req, rtsp_message *resp) {
   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);
@@ -4084,50 +4084,58 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
         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";
@@ -4146,23 +4154,52 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
     *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;
index 89fa58584a52058c00d2b99fba7c65a97b8d1bf8..b2d8886cbd0f956fed02e476d80ea28a6f1cef3b 100644 (file)
@@ -1246,10 +1246,10 @@ int parse_options(int argc, char **argv) {
 
   /* 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)
@@ -1416,6 +1416,11 @@ void exit_function() {
 
       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) {
@@ -1515,6 +1520,9 @@ int main(int argc, char **argv) {
 
   // 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;
@@ -1582,15 +1590,27 @@ int main(int argc, char **argv) {
   // 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
@@ -2014,12 +2034,13 @@ int main(int argc, char **argv) {
   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);
@@ -2047,7 +2068,10 @@ int main(int argc, char **argv) {
   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