Run control and timing services on v4 and v6

Add pointers to the RAOP services (control & timing) to raop_session;
depending on the family, raop_session_make() will set them to the proper
services.
This commit is contained in:
Julien BLACHE 2010-05-14 17:38:05 +02:00
parent 4a199cdcce
commit efbea91ed4

View File

@ -108,6 +108,9 @@ struct raop_session
union sockaddr_all sa; union sockaddr_all sa;
struct raop_service *timing_svc;
struct raop_service *control_svc;
struct raop_session *next; struct raop_session *next;
}; };
@ -174,10 +177,12 @@ static char *raop_aes_key_b64;
static char *raop_aes_iv_b64; static char *raop_aes_iv_b64;
/* AirTunes v2 time synchronization */ /* AirTunes v2 time synchronization */
static struct raop_service timing_svc; static struct raop_service timing_4svc;
static struct raop_service timing_6svc;
/* AirTunes v2 playback synchronization / control */ /* AirTunes v2 playback synchronization / control */
static struct raop_service control_svc; static struct raop_service control_4svc;
static struct raop_service control_6svc;
static int sync_counter; static int sync_counter;
/* AirTunes v2 audio stream */ /* AirTunes v2 audio stream */
@ -1134,7 +1139,8 @@ raop_send_req_setup(struct raop_session *rs, evrtsp_req_cb cb)
raop_add_headers(rs, req); raop_add_headers(rs, req);
/* Request UDP transport, AirTunes v2 streaming */ /* Request UDP transport, AirTunes v2 streaming */
ret = snprintf(hdr, sizeof(hdr), "RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=%u;timing_port=%u", control_svc.port, timing_svc.port); ret = snprintf(hdr, sizeof(hdr), "RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=%u;timing_port=%u",
rs->control_svc->port, rs->timing_svc->port);
if ((ret < 0) || (ret >= sizeof(hdr))) if ((ret < 0) || (ret >= sizeof(hdr)))
{ {
DPRINTF(E_LOG, L_RAOP, "Transport header exceeds buffer length\n"); DPRINTF(E_LOG, L_RAOP, "Transport header exceeds buffer length\n");
@ -1366,6 +1372,9 @@ raop_session_make(struct raop_device *rd, raop_status_cb cb)
rs->password = rd->password; rs->password = rd->password;
rs->timing_svc = &timing_4svc;
rs->control_svc = &control_4svc;
rs->sa.sin.sin_family = AF_INET; rs->sa.sin.sin_family = AF_INET;
ret = inet_pton(AF_INET, rd->v4_address, &rs->sa.sin.sin_addr.s_addr); ret = inet_pton(AF_INET, rd->v4_address, &rs->sa.sin.sin_addr.s_addr);
if (ret <= 0) if (ret <= 0)
@ -1686,9 +1695,12 @@ raop_v2_timing_cb(int fd, short what, void *arg)
struct ntp_stamp recv_stamp; struct ntp_stamp recv_stamp;
struct ntp_stamp xmit_stamp; struct ntp_stamp xmit_stamp;
struct raop_session *rs; struct raop_session *rs;
struct raop_service *svc;
int len; int len;
int ret; int ret;
svc = (struct raop_service *)arg;
ret = raop_v2_timing_get_clock_ntp(&recv_stamp); ret = raop_v2_timing_get_clock_ntp(&recv_stamp);
if (ret < 0) if (ret < 0)
{ {
@ -1698,7 +1710,7 @@ raop_v2_timing_cb(int fd, short what, void *arg)
} }
len = sizeof(sa.ss); len = sizeof(sa.ss);
ret = recvfrom(timing_svc.fd, req, sizeof(req), 0, &sa.sa, (socklen_t *)&len); ret = recvfrom(svc->fd, req, sizeof(req), 0, &sa.sa, (socklen_t *)&len);
if (ret < 0) if (ret < 0)
{ {
DPRINTF(E_LOG, L_RAOP, "Error reading timing request: %s\n", strerror(errno)); DPRINTF(E_LOG, L_RAOP, "Error reading timing request: %s\n", strerror(errno));
@ -1713,7 +1725,9 @@ raop_v2_timing_cb(int fd, short what, void *arg)
goto readd; goto readd;
} }
if (len != sizeof(struct sockaddr_in)) if ((svc == &timing_4svc) && (len != sizeof(struct sockaddr_in)))
goto readd;
else if ((svc == &timing_6svc) && (len != sizeof(struct sockaddr_in6)))
goto readd; goto readd;
for (rs = sessions; rs; rs = rs->next) for (rs = sessions; rs; rs = rs->next)
@ -1775,7 +1789,7 @@ raop_v2_timing_cb(int fd, short what, void *arg)
memcpy(res + 28, &xmit_stamp.frac, 4); memcpy(res + 28, &xmit_stamp.frac, 4);
} }
ret = sendto(timing_svc.fd, res, sizeof(res), 0, &sa.sa, len); ret = sendto(svc->fd, res, sizeof(res), 0, &sa.sa, len);
if (ret < 0) if (ret < 0)
{ {
DPRINTF(E_LOG, L_RAOP, "Could not send timing reply: %s\n", strerror(errno)); DPRINTF(E_LOG, L_RAOP, "Could not send timing reply: %s\n", strerror(errno));
@ -1784,7 +1798,7 @@ raop_v2_timing_cb(int fd, short what, void *arg)
} }
readd: readd:
ret = event_add(&timing_svc.ev, NULL); ret = event_add(&svc->ev, NULL);
if (ret < 0) if (ret < 0)
{ {
DPRINTF(E_LOG, L_RAOP, "Couldn't re-add event for timing requests\n"); DPRINTF(E_LOG, L_RAOP, "Couldn't re-add event for timing requests\n");
@ -1794,75 +1808,143 @@ raop_v2_timing_cb(int fd, short what, void *arg)
} }
static int static int
raop_v2_timing_start(void) raop_v2_timing_start_one(struct raop_service *svc, int family)
{ {
union sockaddr_all sa; union sockaddr_all sa;
int on;
int len; int len;
int ret; int ret;
#ifdef __linux__ #ifdef __linux__
timing_svc.fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); svc->fd = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
#else #else
timing_svc.fd = socket(AF_INET, SOCK_DGRAM, 0); svc->fd = socket(family, SOCK_DGRAM, 0);
#endif #endif
if (timing_svc.fd < 0) if (svc->fd < 0)
{ {
DPRINTF(E_LOG, L_RAOP, "Couldn't make timing socket: %s\n", strerror(errno)); DPRINTF(E_LOG, L_RAOP, "Couldn't make timing socket: %s\n", strerror(errno));
return -1; return -1;
} }
sa.sin.sin_family = AF_INET; if (family == AF_INET6)
sa.sin.sin_addr.s_addr = INADDR_ANY; {
sa.sin.sin_port = 0; on = 1;
ret = setsockopt(svc->fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
if (ret < 0)
{
DPRINTF(E_LOG, L_RAOP, "Could not set IPV6_V6ONLY on timing socket: %s\n", strerror(errno));
ret = bind(timing_svc.fd, &sa.sa, sizeof(struct sockaddr_in)); goto out_fail;
}
}
sa.ss.ss_family = family;
switch (family)
{
case AF_INET:
sa.sin.sin_addr.s_addr = INADDR_ANY;
sa.sin.sin_port = 0;
len = sizeof(sa.sin);
break;
case AF_INET6:
sa.sin6.sin6_addr = in6addr_any;
sa.sin6.sin6_port = 0;
len = sizeof(sa.sin6);
break;
}
ret = bind(svc->fd, &sa.sa, len);
if (ret < 0) if (ret < 0)
{ {
DPRINTF(E_LOG, L_RAOP, "Couldn't bind timing socket: %s\n", strerror(errno)); DPRINTF(E_LOG, L_RAOP, "Couldn't bind timing socket: %s\n", strerror(errno));
close(timing_svc.fd); goto out_fail;
return -1;
} }
len = sizeof(sa.ss); len = sizeof(sa.ss);
ret = getsockname(timing_svc.fd, &sa.sa, (socklen_t *)&len); ret = getsockname(svc->fd, &sa.sa, (socklen_t *)&len);
if (ret < 0) if (ret < 0)
{ {
DPRINTF(E_LOG, L_RAOP, "Couldn't get timing socket name: %s\n", strerror(errno)); DPRINTF(E_LOG, L_RAOP, "Couldn't get timing socket name: %s\n", strerror(errno));
close(timing_svc.fd); goto out_fail;
return -1;
} }
timing_svc.port = ntohs(sa.sin.sin_port); switch (family)
{
case AF_INET:
svc->port = ntohs(sa.sin.sin_port);
DPRINTF(E_DBG, L_RAOP, "Timing IPv4 port: %d\n", svc->port);
break;
DPRINTF(E_DBG, L_RAOP, "Timing port: %d\n", timing_svc.port); case AF_INET6:
svc->port = ntohs(sa.sin6.sin6_port);
DPRINTF(E_DBG, L_RAOP, "Timing IPv6 port: %d\n", svc->port);
break;
}
event_set(&timing_svc.ev, timing_svc.fd, EV_READ, raop_v2_timing_cb, NULL); event_set(&svc->ev, svc->fd, EV_READ, raop_v2_timing_cb, svc);
event_base_set(evbase_player, &timing_svc.ev); event_base_set(evbase_player, &svc->ev);
ret = event_add(&timing_svc.ev, NULL); ret = event_add(&svc->ev, NULL);
if (ret < 0) if (ret < 0)
{ {
DPRINTF(E_LOG, L_RAOP, "Couldn't add event for timing requests\n"); DPRINTF(E_LOG, L_RAOP, "Couldn't add event for timing requests\n");
close(timing_svc.fd); goto out_fail;
return -1;
} }
return 0; return 0;
out_fail:
close(svc->fd);
svc->fd = -1;
svc->port = 0;
return -1;
} }
static void static void
raop_v2_timing_stop(void) raop_v2_timing_stop(void)
{ {
if (event_initialized(&timing_svc.ev)) if (event_initialized(&timing_4svc.ev))
event_del(&timing_svc.ev); event_del(&timing_4svc.ev);
close(timing_svc.fd); if (event_initialized(&timing_6svc.ev))
event_del(&timing_6svc.ev);
timing_svc.fd = -1; close(timing_4svc.fd);
timing_svc.port = 0;
timing_4svc.fd = -1;
timing_4svc.port = 0;
close(timing_6svc.fd);
timing_6svc.fd = -1;
timing_6svc.port = 0;
}
static int
raop_v2_timing_start(void)
{
int ret;
ret = raop_v2_timing_start_one(&timing_6svc, AF_INET6);
if (ret < 0)
DPRINTF(E_WARN, L_RAOP, "Could not start timing service on IPv6\n");
ret = raop_v2_timing_start_one(&timing_4svc, AF_INET);
if (ret < 0)
{
DPRINTF(E_LOG, L_RAOP, "Could not start timing service on IPv4\n");
raop_v2_timing_stop();
return -1;
}
return 0;
} }
/* AirTunes v2 playback synchronization */ /* AirTunes v2 playback synchronization */
@ -1876,6 +1958,7 @@ raop_v2_control_send_sync(uint64_t next_pkt, struct timespec *init)
uint64_t cur_pos; uint64_t cur_pos;
uint32_t cur_pos32; uint32_t cur_pos32;
uint32_t next_pkt32; uint32_t next_pkt32;
int len;
int ret; int ret;
memset(msg, 0, sizeof(msg)); memset(msg, 0, sizeof(msg));
@ -1918,70 +2001,151 @@ raop_v2_control_send_sync(uint64_t next_pkt, struct timespec *init)
if (rs->state != RAOP_STREAMING) if (rs->state != RAOP_STREAMING)
continue; continue;
rs->sa.sin.sin_port = htons(rs->control_port); switch (rs->sa.ss.ss_family)
{
case AF_INET:
rs->sa.sin.sin_port = htons(rs->control_port);
len = sizeof(rs->sa.sin);
break;
ret = sendto(control_svc.fd, msg, sizeof(msg), 0, &rs->sa.sa, sizeof(struct sockaddr_in)); case AF_INET6:
rs->sa.sin6.sin6_port = htons(rs->control_port);
len = sizeof(rs->sa.sin6);
break;
default:
DPRINTF(E_WARN, L_RAOP, "Unknown family %d\n", rs->sa.ss.ss_family);
continue;
}
ret = sendto(rs->control_svc->fd, msg, sizeof(msg), 0, &rs->sa.sa, len);
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_RAOP, "Could not send playback sync to device %s: %s\n", rs->devname, strerror(errno)); DPRINTF(E_LOG, L_RAOP, "Could not send playback sync to device %s: %s\n", rs->devname, strerror(errno));
} }
} }
static int static int
raop_v2_control_start(void) raop_v2_control_start_one(struct raop_service *svc, int family)
{ {
union sockaddr_all sa; union sockaddr_all sa;
int on;
int len; int len;
int ret; int ret;
#ifdef __linux__ #ifdef __linux__
control_svc.fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); svc->fd = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
#else #else
control_svc.fd = socket(AF_INET, SOCK_DGRAM, 0); svc->fd = socket(family, SOCK_DGRAM, 0);
#endif #endif
if (control_svc.fd < 0) if (svc->fd < 0)
{ {
DPRINTF(E_LOG, L_RAOP, "Couldn't make control socket: %s\n", strerror(errno)); DPRINTF(E_LOG, L_RAOP, "Couldn't make control socket: %s\n", strerror(errno));
return -1; return -1;
} }
sa.sin.sin_family = AF_INET; if (family == AF_INET6)
sa.sin.sin_addr.s_addr = INADDR_ANY; {
sa.sin.sin_port = 0; on = 1;
ret = setsockopt(svc->fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
if (ret < 0)
{
DPRINTF(E_LOG, L_RAOP, "Could not set IPV6_V6ONLY on control socket: %s\n", strerror(errno));
ret = bind(control_svc.fd, &sa.sa, sizeof(struct sockaddr_in)); goto out_fail;
}
}
sa.ss.ss_family = family;
switch (family)
{
case AF_INET:
sa.sin.sin_addr.s_addr = INADDR_ANY;
sa.sin.sin_port = 0;
len = sizeof(sa.sin);
break;
case AF_INET6:
sa.sin6.sin6_addr = in6addr_any;
sa.sin6.sin6_port = 0;
len = sizeof(sa.sin6);
break;
}
ret = bind(svc->fd, &sa.sa, len);
if (ret < 0) if (ret < 0)
{ {
DPRINTF(E_LOG, L_RAOP, "Couldn't bind control socket: %s\n", strerror(errno)); DPRINTF(E_LOG, L_RAOP, "Couldn't bind control socket: %s\n", strerror(errno));
close(control_svc.fd); goto out_fail;
return -1;
} }
len = sizeof(sa.ss); len = sizeof(sa.ss);
ret = getsockname(control_svc.fd, &sa.sa, (socklen_t *)&len); ret = getsockname(svc->fd, &sa.sa, (socklen_t *)&len);
if (ret < 0) if (ret < 0)
{ {
DPRINTF(E_LOG, L_RAOP, "Couldn't get control socket name: %s\n", strerror(errno)); DPRINTF(E_LOG, L_RAOP, "Couldn't get control socket name: %s\n", strerror(errno));
close(control_svc.fd); goto out_fail;
return -1;
} }
control_svc.port = ntohs(sa.sin.sin_port); switch (family)
{
case AF_INET:
svc->port = ntohs(sa.sin.sin_port);
DPRINTF(E_DBG, L_RAOP, "Control IPv4 port: %d\n", svc->port);
break;
DPRINTF(E_DBG, L_RAOP, "Control port: %d\n", control_svc.port); case AF_INET6:
svc->port = ntohs(sa.sin6.sin6_port);
DPRINTF(E_DBG, L_RAOP, "Control IPv6 port: %d\n", svc->port);
break;
}
return 0; return 0;
out_fail:
close(svc->fd);
svc->fd = -1;
svc->port = 0;
return -1;
} }
static void static void
raop_v2_control_stop(void) raop_v2_control_stop(void)
{ {
close(control_svc.fd); close(control_4svc.fd);
control_svc.fd = -1; control_4svc.fd = -1;
control_svc.port = 0; control_4svc.port = 0;
close(control_6svc.fd);
control_6svc.fd = -1;
control_6svc.port = 0;
}
static int
raop_v2_control_start(void)
{
int ret;
ret = raop_v2_control_start_one(&control_6svc, AF_INET6);
if (ret < 0)
DPRINTF(E_WARN, L_RAOP, "Could not start control service on IPv6\n");
ret = raop_v2_control_start_one(&control_4svc, AF_INET);
if (ret < 0)
{
DPRINTF(E_LOG, L_RAOP, "Could not start control service on IPv4\n");
raop_v2_control_stop();
return -1;
}
return 0;
} }
@ -2715,11 +2879,17 @@ raop_init(void)
gpg_error_t gc_err; gpg_error_t gc_err;
int ret; int ret;
timing_svc.fd = -1; timing_4svc.fd = -1;
timing_svc.port = 0; timing_4svc.port = 0;
control_svc.fd = -1; timing_6svc.fd = -1;
control_svc.port = 0; timing_6svc.port = 0;
control_4svc.fd = -1;
control_4svc.port = 0;
control_6svc.fd = -1;
control_6svc.port = 0;
sessions = NULL; sessions = NULL;