Fixes for iTunes v10.5 time-outs.

Details:

iTunes v10.5 clients changed how they responded to DAAP
protocol, and started disconnecting when the forked-daapd server
sent an empty "refresh" reply ("mupd" protocol).  This problem is
also coupled with session-timeout ("mstm" and "msal"); when these
server capabilities were enabled, iTunes 10.x clients did not poll
for updates and eventually disconnected.

I investigated DAAP network packets using Wireshark.  I found that
a true iTunes server sends a set of server capabilities in a specific
order, and order matters to the client.  When the correct order is used,
the client correctly polls for updates and does not disconnect.

This change:
  1. Send server capabilities in different order (daap_reply_server_info).
  2. Disables 5-minute update refresh.
  3. Disables 30-minute inactivity time-out.

Testing:
This server version successfully stayed connected to the
following clients:
  * iTunes 10.5.2
  * iTunes 10.4.2
  * iTunes 9.7.1
  * Rhythmbox 0.12.8
The clients stayed connected for at least several hours,
sometimes days, with activity or no activity.
This commit is contained in:
Craig Markwardt 2012-01-02 02:15:18 -05:00
parent 54d3951c20
commit bd10978d52
1 changed files with 50 additions and 40 deletions

View File

@ -58,9 +58,11 @@ extern struct event_base *evbase_httpd;
/* Session timeout in seconds */
#define DAAP_SESSION_TIMEOUT 1800
#define DAAP_SESSION_TIMEOUT 0 /* By default the session never times out */
/* We announce this timeout to the client when returning server capabilities */
#define DAAP_SESSION_TIMEOUT_CAPABILITY 1800
/* Update requests refresh interval in seconds */
#define DAAP_UPDATE_REFRESH 300
#define DAAP_UPDATE_REFRESH 0
struct uri_map {
@ -130,7 +132,7 @@ daap_session_free(void *item)
s = (struct daap_session *)item;
evtimer_del(&s->timeout);
if (event_initialized(&s->timeout)) evtimer_del(&s->timeout);
free(s);
}
@ -173,8 +175,10 @@ daap_session_register(void)
next_session_id++;
evtimer_set(&s->timeout, daap_session_timeout_cb, s);
event_base_set(evbase_httpd, &s->timeout);
if (DAAP_SESSION_TIMEOUT > 0) {
evtimer_set(&s->timeout, daap_session_timeout_cb, s);
event_base_set(evbase_httpd, &s->timeout);
}
node = avl_insert(daap_sessions, s);
if (!node)
@ -185,12 +189,14 @@ daap_session_register(void)
return NULL;
}
evutil_timerclear(&tv);
tv.tv_sec = DAAP_SESSION_TIMEOUT;
ret = evtimer_add(&s->timeout, &tv);
if (ret < 0)
DPRINTF(E_LOG, L_DAAP, "Could not add session timeout event for session %d\n", s->id);
if (DAAP_SESSION_TIMEOUT > 0) {
evutil_timerclear(&tv);
tv.tv_sec = DAAP_SESSION_TIMEOUT;
ret = evtimer_add(&s->timeout, &tv);
if (ret < 0)
DPRINTF(E_LOG, L_DAAP, "Could not add session timeout event for session %d\n", s->id);
}
return s;
}
@ -225,14 +231,18 @@ daap_session_find(struct evhttp_request *req, struct evkeyvalq *query, struct ev
s = (struct daap_session *)node->item;
event_del(&s->timeout);
if (DAAP_SESSION_TIMEOUT > 0) {
event_del(&s->timeout);
}
evutil_timerclear(&tv);
tv.tv_sec = DAAP_SESSION_TIMEOUT;
ret = evtimer_add(&s->timeout, &tv);
if (ret < 0)
DPRINTF(E_LOG, L_DAAP, "Could not add session timeout event for session %d\n", s->id);
if (DAAP_SESSION_TIMEOUT > 0) {
ret = evtimer_add(&s->timeout, &tv);
if (ret < 0)
DPRINTF(E_LOG, L_DAAP, "Could not add session timeout event for session %d\n", s->id);
}
return s;
@ -269,7 +279,7 @@ static void
update_free(struct daap_update_request *ur)
{
if (event_initialized(&ur->timeout))
evtimer_del(&ur->timeout);
evtimer_del(&ur->timeout);
free(ur);
}
@ -678,25 +688,23 @@ daap_reply_server_info(struct evhttp_request *req, struct evbuffer *evbuf, char
dmap_add_container(evbuf, "msrv", len - 8);
dmap_add_int(evbuf, "mstt", 200); /* 12 */
dmap_add_int(evbuf, "mpro", mpro); /* 12 */
dmap_add_int(evbuf, "apro", apro); /* 12 */
dmap_add_string(evbuf, "minm", name); /* 8 + strlen(name) */
dmap_add_int(evbuf, "mstm", DAAP_SESSION_TIMEOUT); /* 12 */
dmap_add_char(evbuf, "msal", 1); /* 9 */
dmap_add_int(evbuf, "apro", apro); /* 12 */
dmap_add_char(evbuf, "mslr", 1); /* 9 */
dmap_add_int(evbuf, "mstm", DAAP_SESSION_TIMEOUT_CAPABILITY); /* 12 */
dmap_add_char(evbuf, "msal", 1); /* 9 */
dmap_add_char(evbuf, "msau", (passwd) ? 2 : 0); /* 9 */
dmap_add_char(evbuf, "msup", 1); /* 9 */
dmap_add_char(evbuf, "mspi", 1); /* 9 */
dmap_add_char(evbuf, "msex", 1); /* 9 */
dmap_add_char(evbuf, "msix", 1); /* 9 */
dmap_add_char(evbuf, "msbr", 1); /* 9 */
dmap_add_char(evbuf, "msqy", 1); /* 9 */
dmap_add_char(evbuf, "mspi", 1); /* 9 */
dmap_add_int(evbuf, "msdc", 1); /* 12 */
/* Advertise updates support even though we don't send updates */
dmap_add_char(evbuf, "msup", 1); /* 9 */
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
}
@ -887,22 +895,24 @@ daap_reply_update(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
}
memset(ur, 0, sizeof(struct daap_update_request));
evtimer_set(&ur->timeout, update_refresh_cb, ur);
event_base_set(evbase_httpd, &ur->timeout);
evutil_timerclear(&tv);
tv.tv_sec = DAAP_UPDATE_REFRESH;
ret = evtimer_add(&ur->timeout, &tv);
if (ret < 0)
{
DPRINTF(E_LOG, L_DAAP, "Could not add update timeout event\n");
dmap_send_error(req, "mupd", "Could not register timer");
update_free(ur);
return;
}
if (DAAP_UPDATE_REFRESH > 0) {
evtimer_set(&ur->timeout, update_refresh_cb, ur);
event_base_set(evbase_httpd, &ur->timeout);
evutil_timerclear(&tv);
tv.tv_sec = DAAP_UPDATE_REFRESH;
ret = evtimer_add(&ur->timeout, &tv);
if (ret < 0)
{
DPRINTF(E_LOG, L_DAAP, "Could not add update timeout event\n");
dmap_send_error(req, "mupd", "Could not register timer");
update_free(ur);
return;
}
}
/* NOTE: we may need to keep reqd_rev in there too */
ur->req = req;