Skip to content

Instantly share code, notes, and snippets.

@nareix
Last active August 29, 2015 14:13
Show Gist options
  • Save nareix/9b0e11721da34487d7ac to your computer and use it in GitHub Desktop.
Save nareix/9b0e11721da34487d7ac to your computer and use it in GitHub Desktop.
DACP Client
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Only root or user avahi can own the Avahi service -->
<policy user="avahi">
<allow own="org.freedesktop.Avahi"/>
</policy>
<policy user="root">
<allow own="org.freedesktop.Avahi"/>
</policy>
<!-- Allow anyone to invoke methods on Avahi server, except SetHostName -->
<policy context="default">
<allow send_destination="org.freedesktop.Avahi"/>
<allow receive_sender="org.freedesktop.Avahi"/>
<deny send_destination="org.freedesktop.Avahi"
send_interface="org.freedesktop.Avahi.Server" send_member="SetHostName"/>
</policy>
<!-- Allow everything, including access to SetHostName to users of the group "netdev" -->
<policy group="netdev">
<allow send_destination="org.freedesktop.Avahi"/>
<allow receive_sender="org.freedesktop.Avahi"/>
</policy>
<policy user="root">
<allow send_destination="org.freedesktop.Avahi"/>
<allow receive_sender="org.freedesktop.Avahi"/>
</policy>
</busconfig>
#include <poll.h>
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <locale.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <avahi-common/simple-watch.h>
#include <avahi-common/error.h>
#include <avahi-common/malloc.h>
#include <avahi-common/domain.h>
#include <avahi-common/llist.h>
#include <avahi-client/client.h>
#include <avahi-client/lookup.h>
static void *memdup(void *p, int size) {
void *r = malloc(size);
memcpy(r, p, size);
return r;
}
typedef struct {
char addr[32];
int port;
} host_t;
typedef struct {
AvahiSimplePoll *poll;
AvahiClient *cli;
AvahiServiceBrowser *br;
int ref_nr;
struct {
char *name_prefix;
char *type;
} match;
host_t *found;
} ctx_t;
static void client_callback(AvahiClient *cli, AvahiClientState state, void *ud);
static void ctx_find_service(ctx_t *c) {
c->poll = avahi_simple_poll_new();
if (c->poll == NULL) {
fprintf(stderr, "Failed to create simple poll object.\n");
goto cleanup;
}
c->ref_nr = 1;
int err;
c->cli = avahi_client_new(
avahi_simple_poll_get(c->poll), 0, client_callback,
c, &err
);
if (c->cli == NULL) {
fprintf(stderr, "Failed to create client object. error=%d\n", err);
goto cleanup;
}
avahi_simple_poll_loop(c->poll);
cleanup:
if (c->br)
avahi_service_browser_free(c->br);
if (c->cli)
avahi_client_free(c->cli);
if (c->poll)
avahi_simple_poll_free(c->poll);
}
static void ctx_cleanup(ctx_t *c) {
if (c->found)
free(c->found);
}
static void ctx_ref(ctx_t *c) {
c->ref_nr++;
}
static void ctx_unref(ctx_t *c) {
c->ref_nr--;
if (c->ref_nr == 0)
avahi_simple_poll_quit(c->poll);
}
typedef struct {
ctx_t *c;
AvahiServiceResolver *resolver;
} srvctx_t;
static srvctx_t *srvctx_new(ctx_t *c) {
srvctx_t *sc = (srvctx_t *)malloc(sizeof(srvctx_t));
sc->c = c;
ctx_ref(c);
return sc;
}
static srvctx_t *srvctx_unref(srvctx_t *sc) {
ctx_unref(sc->c);
if (sc->resolver)
avahi_service_resolver_free(sc->resolver);
free(sc);
}
static int startswith(char *s, char *pat) {
int n = strlen(pat);
return !strncmp(s, pat, n);
}
static int ctx_ismatch(ctx_t *c, const char *name) {
if (c->match.name_prefix)
return startswith((char *)name, c->match.name_prefix);
return 1;
}
static void service_resolver_callback(
AvahiServiceResolver *r,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiResolverEvent event,
const char *name,
const char *type,
const char *domain,
const char *host_name,
const AvahiAddress *a,
uint16_t port,
AvahiStringList *txt,
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
void *ud)
{
srvctx_t *sc = (srvctx_t *)ud;
switch (event) {
case AVAHI_RESOLVER_FOUND: {
char address[AVAHI_ADDRESS_STR_MAX];
avahi_address_snprint(address, sizeof(address), a);
fprintf(stderr, "found: name=%s type=%s addr=%s port=%d\n", name, type, address, port);
ctx_t *c = sc->c;
if (ctx_ismatch(c, name)) {
if (c->found)
free(c->found);
c->found = (host_t *)malloc(sizeof(host_t));
strcpy(c->found->addr, address);
c->found->port = port;
} else {
fprintf(stderr, "resolve: name not match(%s vs %s)\n", name, c->match.name_prefix);
}
srvctx_unref(sc);
break;
}
case AVAHI_RESOLVER_FAILURE: {
srvctx_unref(sc);
break;
}
}
}
static const char *browser_event_to_string(AvahiBrowserEvent event) {
switch (event) {
case AVAHI_BROWSER_NEW : return "NEW";
case AVAHI_BROWSER_REMOVE : return "REMOVE";
case AVAHI_BROWSER_CACHE_EXHAUSTED : return "CACHE_EXHAUSTED";
case AVAHI_BROWSER_ALL_FOR_NOW : return "ALL_FOR_NOW";
case AVAHI_BROWSER_FAILURE : return "FAILURE";
}
return "?";
}
static void service_browser_callback(
AvahiServiceBrowser *b,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiBrowserEvent event,
const char *name,
const char *type,
const char *domain,
AvahiLookupResultFlags flags,
void *ud)
{
ctx_t *c = (ctx_t *)ud;
fprintf(stderr, "resolve: browser event=%s if=%d prot=%d type=%s name=%s\n",
browser_event_to_string(event),
interface, protocol, type, name);
switch (event) {
case AVAHI_BROWSER_NEW: {
srvctx_t *sc = srvctx_new(c);
sc->resolver = avahi_service_resolver_new(c->cli,
interface, protocol, name, type, domain,
AVAHI_PROTO_UNSPEC, 0, service_resolver_callback, sc
);
if (sc->resolver == NULL)
srvctx_unref(sc);
break;
}
case AVAHI_BROWSER_CACHE_EXHAUSTED:
ctx_unref(c);
break;
}
}
static void client_callback(AvahiClient *cli, AvahiClientState state, void *ud) {
ctx_t *c = (ctx_t *)ud;
switch (state) {
case AVAHI_CLIENT_FAILURE:
ctx_unref(c);
break;
case AVAHI_CLIENT_S_REGISTERING:
case AVAHI_CLIENT_S_RUNNING:
case AVAHI_CLIENT_S_COLLISION: {
fprintf(stderr, "resolve: browse service type=%s\n", c->match.type);
c->br = avahi_service_browser_new(
cli,
AVAHI_IF_UNSPEC,
AVAHI_PROTO_INET,
c->match.type,
NULL,
0,
service_browser_callback,
c);
}
break;
}
}
static void run_dcap_cmd(host_t *host, char *op, char *active_remote) {
char *cmd = (char *)malloc(128 + strlen(active_remote));
sprintf(cmd, "curl 'http://%s:%d/ctrl-int/1/%s' -H 'Active-Remote: %s' -H 'Host: starlight.local.'",
host->addr, host->port, op, active_remote);
fprintf(stderr, "cmd: %s\n", cmd);
system(cmd);
free(cmd);
}
typedef struct {
int stat;
host_t *host;
char *srv_name;
char *active_remote;
int p_cmd[2];
} srv_t;
enum {
INIT,
RESOLVING,
RESOLVED,
};
static int sockfd_new() {
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
return -1;
int port = 3391;
struct sockaddr_in si;
memset(&si, 0, sizeof(si));
si.sin_family = AF_INET;
si.sin_port = htons(port);
si.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(fd, (struct sockaddr *)&si, sizeof(si)) < 0)
return -1;
fprintf(stderr, "binded :%d\n", port);
return fd;
}
static void sockfd_recv(int fd, char *buf, int len) {
struct sockaddr sa;
socklen_t salen = sizeof(sa);
int i = recvfrom(fd, buf, len, 0, &sa, &salen);
if (i > 0)
buf[i] = 0;
}
static int resolve_itunes_ctrl(srv_t *srv, char *srv_name, char *active_remote) {
if (srv->srv_name) {
free(srv->srv_name);
srv->srv_name = NULL;
}
if (srv->active_remote) {
free(srv->active_remote);
srv->active_remote = NULL;
}
if (srv->host) {
free(srv->host);
srv->host = NULL;
}
fprintf(stderr, "resolve: srv=%s active_remote=%s\n", srv_name, active_remote);
ctx_t rsv = { .match = {.name_prefix = srv_name, .type = "_dacp._tcp"} };
ctx_find_service(&rsv);
if (rsv.found) {
srv->srv_name = strdup(srv_name);
srv->active_remote = strdup(active_remote);
srv->host = (host_t *)memdup(rsv.found, sizeof(host_t));
fprintf(stderr, "resolve: host=%s:%d srvname=%s\n", srv->host->addr, srv->host->port, srv->srv_name);
} else {
fprintf(stderr, "resolve: srv=%s failed\n", srv_name);
}
ctx_cleanup(&rsv);
return !!rsv.found;
}
static void run_cmd(srv_t *srv, char *s) {
int n = strlen(s);
char *srv_name = (char *)malloc(n);
char *active_remote = (char *)malloc(n);
fprintf(stderr, "cmd: %s\n", s);
if (sscanf(s, "resolve,%[^,],%[^,]", srv_name, active_remote) == 2) {
int n = 10;
while (n > 0 && !resolve_itunes_ctrl(srv, srv_name, active_remote)) {
sleep(1);
n--;
}
} else if (srv->host != NULL) {
run_dcap_cmd(srv->host, s, srv->active_remote);
}
cleanup:
free(srv_name);
free(active_remote);
}
static void poll_loop(srv_t *s) {
int sockfd = sockfd_new();
for (;;) {
struct pollfd pfds[] = {
{.fd = sockfd, .events = POLLIN|POLLERR|POLLHUP|POLLNVAL},
};
int r = poll(pfds, 1, -1);
if (r == 1) {
if (pfds[0].revents & POLLIN) {
char buf[2048] = {};
sockfd_recv(sockfd, buf, sizeof(buf));
run_cmd(s, buf);
}
if (pfds[0].revents & (POLLERR|POLLHUP|POLLNVAL))
break;
}
}
}
int main(int argc, char *argv[]) {
srv_t s = {};
poll_loop(&s);
return 0;
}
*** rtsp.c 2015-01-03 04:40:57.097217399 +0800
--- ../shairport-jz/rtsp.c 2014-12-01 06:10:03.133102531 +0800
***************
*** 30,36 ****
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
- #include <netinet/tcp.h>
#include <sys/select.h>
#include <signal.h>
#include <stdio.h>
--- 30,35 ----
*************** static int msg_add_header(rtsp_message *
*** 174,181 ****
return 1;
}
- //fprintf(stderr, "%s: %s\n", name, value);
-
msg->name[msg->nheaders] = strdup(name);
msg->value[msg->nheaders] = strdup(value);
msg->nheaders++;
--- 173,178 ----
*************** static void msg_free(rtsp_message *msg)
*** 202,245 ****
free(msg);
}
- static void dacp_update_params(char *dacp_id, char *active_remote) {
- static struct {
- char *dacp_id;
- char *active_remote;
- } p;
-
- static int sockfd;
- if (sockfd == 0)
- sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- if (dacp_id == NULL || active_remote == NULL)
- return;
-
- int changed = 0;
-
- if (p.dacp_id == NULL || strcmp(p.dacp_id, dacp_id))
- changed++;
- if (p.active_remote == NULL || strcmp(p.active_remote, active_remote))
- changed++;
-
- if (changed) {
- if (p.dacp_id) free(p.dacp_id);
- if (p.active_remote) free(p.active_remote);
- p.dacp_id = strdup(dacp_id);
- p.active_remote = strdup(active_remote);
-
- struct sockaddr_in si;
- memset(&si, 0, sizeof(si));
- si.sin_family = AF_INET;
- si.sin_port = htons(3391);
- inet_aton("127.0.0.1", &si.sin_addr);
-
- char *s = (char *)malloc(strlen(p.dacp_id) + strlen(p.active_remote) + 128);
- sprintf(s, "resolve,iTunes_Ctrl_%s,%s", p.dacp_id, p.active_remote);
- fprintf(stderr, "DACP update: %s\n", s);
- sendto(sockfd, s, strlen(s)+1, 0, (struct sockaddr *)&si, sizeof(si));
- free(s);
- }
- }
static int msg_handle_line(rtsp_message **pmsg, char *line) {
rtsp_message *msg = *pmsg;
--- 199,204 ----
*************** static int msg_handle_line(rtsp_message
*** 282,295 ****
debug(2, " %s: %s\n", line, p);
return -1;
} else {
- char *active_remote = msg_get_header(msg, "Active-Remote");
- char *dacp_id = msg_get_header(msg, "DACP-ID");
-
- dacp_update_params(dacp_id, active_remote);
-
- //fprintf(stderr, "Active-Remote: %s\n", active_remote);
- //fprintf(stderr, "DACP-ID: %s\n", dacp_id);
-
char *cl = msg_get_header(msg, "Content-Length");
if (cl)
return atoi(cl);
--- 241,246 ----
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment