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