events: Limit subscriber count

Protect against DoS by limiting the number of subscribers we allow to a
total of 500, and immediately removing subscribers from our list if we
encounter an error communicating with them.
This commit is contained in:
Justin Maggard 2017-08-22 12:20:38 -07:00
parent 14d0110fb4
commit 37346fb0a0

View File

@ -83,18 +83,18 @@ struct subscriber {
struct upnp_event_notify {
LIST_ENTRY(upnp_event_notify) entries;
int s; /* socket */
enum { ECreated=1,
int s; /* socket */
enum { ECreated=1,
EConnecting,
ESending,
EWaitingForResponse,
EFinished,
EError } state;
struct subscriber * sub;
char * buffer;
int buffersize;
struct subscriber * sub;
char * buffer;
int buffersize;
int tosend;
int sent;
int sent;
const char * path;
char addrstr[16];
char portstr[8];
@ -110,6 +110,9 @@ LIST_HEAD(listhead, subscriber) subscriberlist = { NULL };
/* notify list */
LIST_HEAD(listheadnotif, upnp_event_notify) notifylist = { NULL };
#define MAX_SUBSCRIBERS 500
static uint16_t nsubscribers = 0;
/* create a new subscriber */
static struct subscriber *
newSubscriber(const char * eventurl, const char * callback, int callbacklen)
@ -151,12 +154,15 @@ upnpevents_addSubscriber(const char * eventurl,
struct subscriber * tmp;
DPRINTF(E_DEBUG, L_HTTP, "addSubscriber(%s, %.*s, %d)\n",
eventurl, callbacklen, callback, timeout);
if (nsubscribers >= MAX_SUBSCRIBERS)
return NULL;
tmp = newSubscriber(eventurl, callback, callbacklen);
if(!tmp)
return NULL;
if(timeout)
tmp->timeout = time(NULL) + timeout;
LIST_INSERT_HEAD(&subscriberlist, tmp, entries);
nsubscribers++;
upnp_event_create_notify(tmp);
return tmp->uuid;
}
@ -189,6 +195,7 @@ upnpevents_removeSubscriber(const char * sid, int sidlen)
sub->notify->sub = NULL;
}
LIST_REMOVE(sub, entries);
nsubscribers--;
free(sub);
return 0;
}
@ -468,28 +475,27 @@ void upnpevents_processfds(fd_set *readset, fd_set *writeset)
}
if(obj->sub)
obj->sub->notify = NULL;
#if 0 /* Just let it time out instead of explicitly removing the subscriber */
/* remove also the subscriber from the list if there was an error */
if(obj->state == EError && obj->sub) {
LIST_REMOVE(obj->sub, entries);
nsubscribers--;
free(obj->sub);
}
#endif
free(obj->buffer);
LIST_REMOVE(obj, entries);
free(obj);
}
obj = next;
}
/* remove timeouted subscribers */
/* remove timed-out subscribers */
curtime = time(NULL);
for(sub = subscriberlist.lh_first; sub != NULL; ) {
subnext = sub->entries.le_next;
if(sub->timeout && curtime > sub->timeout && sub->notify == NULL) {
LIST_REMOVE(sub, entries);
nsubscribers--;
free(sub);
}
sub = subnext;
}
}