+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <time.h>
+#include <string.h>
+#include <strings.h>
+
+#include "mcast.h"
+#include "sap.h"
+
+#define SAP_ADDR "224.2.127.254"
+#define SAP_PORT 9875
+#define SAP_MAX_SIZE 1024
+
+#define SAP_TIMEOUT 3
+
+#define BUFFSIZE SAP_MAX_SIZE
+
+char *get_url_from_sap(char *service)
+{
+ struct timeval start, curr;
+ struct ip_mreq mreq;
+ unsigned char buffer[BUFFSIZE];
+ char *url = NULL;
+ int fd;
+
+ snprintf(buffer,BUFFSIZE,"udp://%s:%u", SAP_ADDR, SAP_PORT);
+
+ fd = open_mcast(buffer);
+
+ gettimeofday(&start, NULL);
+
+ do {
+ int recvd;
+ int sap_version, sap_addrtype, sap_messagetype, sap_encrypted, sap_compressed;
+ in_addr_t sender_address;
+ unsigned char auth_len;
+ unsigned short msgid;
+ unsigned char *payload, *pos, *host = NULL, *proto = NULL, *port = NULL, *sname = NULL;
+
+ if ((recvd = recv(fd, buffer, BUFFSIZE, 0)) < 1) {
+ perror("recv");
+ return NULL;
+ }
+
+ gettimeofday(&curr, NULL);
+
+ sap_version = (buffer[0] >> 5) & 0x7;
+ sap_addrtype = (buffer[0] >> 4) & 0x1;
+ sap_messagetype = (buffer[0] >> 2) & 0x1;
+ sap_encrypted = (buffer[0] >> 1) & 0x1;
+ sap_compressed = buffer[0] & 0x1;
+ auth_len = buffer[1];
+ msgid = buffer[2] << 8 | buffer[3];
+ memcpy(&sender_address, buffer+4, (sap_addrtype?16:4));
+ payload = buffer + 4 /* (sap_*, auth_len, msgid) */ + (sap_addrtype?16:4) + auth_len;
+
+#ifdef DEBUG
+ printf("\n");
+ printf("SAP-Version: %d\n", sap_version);
+ printf("Adresstyp: %s\n", (sap_addrtype?"IPv6":"IPv4"));
+ printf("Messagetype: %s\n", (sap_messagetype?"Announcement":"Deletion"));
+ printf("Encrypted: %d\n", sap_encrypted);
+ printf("Compressed: %d\n", sap_compressed);
+ printf("Authentication Length: %d\n", auth_len);
+ printf("Sender: %u\n", sender_address);
+ printf("Message Identifier Hash: %u\n", msgid);
+#endif
+
+ if (sap_addrtype)
+ continue; /* We don't support IPv6 for now */
+
+#if 0 /* Getstream gets this wrong, see rfc2974 */
+ if (sap_messagetype)
+ continue; /* We are not interested in deletions */
+#endif
+
+ if (sap_encrypted || sap_compressed)
+ continue;
+ /* RFC 2327
+ * v=0
+ * o=- 6dca 1 IN IP4 192.168.100.17:2000
+ * s=TV Das Erste
+ * t=0 0
+ * c=IN IP4 192.168.100.17/1
+ * m=video 2000 http 33
+ * a=tool:getstream
+ * a=type:broadcast
+ */
+
+ pos = payload;
+ while(*pos != 0 && (pos-buffer) < BUFFSIZE) {
+ if (*pos == 0x0d) {
+ *pos = 0;
+ }
+
+ if (*pos == 0x0a) {
+ *pos = 0;
+
+ if (!strncasecmp("s=", payload, 2)) {
+ sname = payload + 2;
+ } else if (!strncasecmp("c=", payload, 2)) {
+ int poscnt = 0;
+
+ payload += 2;
+ while (*payload != 0) {
+ if (poscnt == 2 && *payload == '/') {
+ *payload = 0;
+ break;
+ }
+
+ if (*payload == ' ') {
+ *payload = 0;
+ poscnt++;
+
+ /* c=<network type> <address type> <connection address> */
+ if (poscnt == 2) {
+ host = payload + 1;
+ }
+
+ if (poscnt > 2) {
+ break;
+ }
+ }
+ payload++;
+ }
+ } else if (!strncasecmp("m=", payload, 2)) {
+ int poscnt = 0;
+
+ payload += 2;
+ while (*payload != 0) {
+ if (*payload == ' ') {
+ *payload = 0;
+ poscnt++;
+
+ /* m=<media> <port> <transport> <fmt list> */
+ if (poscnt == 1) {
+ port = payload + 1;
+ }
+
+ if (poscnt == 2) {
+ proto = payload + 1;
+ }
+
+ if (poscnt > 2) {
+ break;
+ }
+ }
+ payload++;
+ }
+ }
+
+ payload = ++pos;
+ continue;
+ }
+ pos++;
+ }
+
+ if (sname && proto && port) {
+ if (!host) {
+ struct in_addr inaddr;
+
+ inaddr.s_addr = sender_address;
+ host = inet_ntoa(inaddr);
+ }
+
+#ifdef DEBUG
+ printf("%s -> %s://%s:%s\n", sname, proto, host, port);
+#endif
+
+ if (strlen(service) < strlen(sname)) {
+ sname += strlen(sname) - strlen(service);
+ }
+
+ if (!strncasecmp(service, sname, strlen(service))) {
+ int len = strlen(host)+strlen(proto)+strlen(port)+5;
+
+ if (!(url = malloc(len))) {
+ perror("malloc");
+ return NULL;
+ }
+
+ snprintf(url, len, "%s://%s:%s", proto, host, port);
+ url[len-1] = 0;
+ break;
+ }
+ }
+
+ } while(curr.tv_sec < start.tv_sec+SAP_TIMEOUT);
+
+ mreq.imr_multiaddr.s_addr = inet_addr(SAP_ADDR);
+ mreq.imr_interface.s_addr = INADDR_ANY;
+ setsockopt (fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
+
+ return url;
+}