mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 02:37:10 +00:00 
			
		
		
		
	pbx_spool: Gracefully handle long lines in call files
Per the linked issue, we aren't checking the buffer filled by fgets() to determine if it contains a newline, so we will fail to correctly parse the trailing portion of a long line. This patch increases the buffer size from 256 to 1024, and skips any line that exceeds that length, logging a warning in the process. ASTERISK-17067 #close Reported by: Dave Olszewski Change-Id: I51bcf270c1b4347ba05b43f18dc2094c76f5d7b0
This commit is contained in:
		
							
								
								
									
										276
									
								
								pbx/pbx_spool.c
									
									
									
									
									
								
							
							
						
						
									
										276
									
								
								pbx/pbx_spool.c
									
									
									
									
									
								
							| @@ -161,139 +161,171 @@ static struct outgoing *new_outgoing(const char *fn) | ||||
| 	return o; | ||||
| } | ||||
|  | ||||
| static int apply_outgoing(struct outgoing *o, FILE *f) | ||||
| static void parse_line(char *line, unsigned int lineno, struct outgoing *o) | ||||
| { | ||||
| 	char buf[256]; | ||||
| 	char *c, *c2; | ||||
| 	int lineno = 0; | ||||
| 	struct ast_variable *var, *last = o->vars; | ||||
| 	char *c; | ||||
|  | ||||
| 	while (last && last->next) { | ||||
| 		last = last->next; | ||||
| 	/* Trim comments */ | ||||
| 	c = line; | ||||
| 	while ((c = strchr(c, '#'))) { | ||||
| 		if ((c == line) || (*(c-1) == ' ') || (*(c-1) == '\t')) { | ||||
| 			*c = '\0'; | ||||
| 			break; | ||||
| 		} | ||||
| 		c++; | ||||
| 	} | ||||
|  | ||||
| 	while(fgets(buf, sizeof(buf), f)) { | ||||
| 	c = line; | ||||
| 	while ((c = strchr(c, ';'))) { | ||||
| 		if ((c > line) && (c[-1] == '\\')) { | ||||
| 			memmove(c - 1, c, strlen(c) + 1); | ||||
| 		} else { | ||||
| 			*c = '\0'; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Trim trailing white space */ | ||||
| 	ast_trim_blanks(line); | ||||
| 	if (ast_strlen_zero(line)) { | ||||
| 		return; | ||||
| 	} | ||||
| 	c = strchr(line, ':'); | ||||
| 	if (!c) { | ||||
| 		ast_log(LOG_NOTICE, "Syntax error at line %d of %s\n", lineno, o->fn); | ||||
| 		return; | ||||
| 	} | ||||
| 	*c = '\0'; | ||||
| 	c = ast_skip_blanks(c + 1); | ||||
| #if 0 | ||||
| 	printf("'%s' is '%s' at line %d\n", line, c, lineno); | ||||
| #endif | ||||
| 	if (!strcasecmp(line, "channel")) { | ||||
| 		char *c2; | ||||
| 		if ((c2 = strchr(c, '/'))) { | ||||
| 			*c2 = '\0'; | ||||
| 			c2++; | ||||
| 			ast_string_field_set(o, tech, c); | ||||
| 			ast_string_field_set(o, dest, c2); | ||||
| 		} else { | ||||
| 			ast_log(LOG_NOTICE, "Channel should be in form Tech/Dest at line %d of %s\n", lineno, o->fn); | ||||
| 		} | ||||
| 	} else if (!strcasecmp(line, "callerid")) { | ||||
| 		char cid_name[80] = {0}, cid_num[80] = {0}; | ||||
| 		ast_callerid_split(c, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num)); | ||||
| 		ast_string_field_set(o, cid_num, cid_num); | ||||
| 		ast_string_field_set(o, cid_name, cid_name); | ||||
| 	} else if (!strcasecmp(line, "application")) { | ||||
| 		ast_string_field_set(o, app, c); | ||||
| 	} else if (!strcasecmp(line, "data")) { | ||||
| 		ast_string_field_set(o, data, c); | ||||
| 	} else if (!strcasecmp(line, "maxretries")) { | ||||
| 		if (sscanf(c, "%30d", &o->maxretries) != 1) { | ||||
| 			ast_log(LOG_WARNING, "Invalid max retries at line %d of %s\n", lineno, o->fn); | ||||
| 			o->maxretries = 0; | ||||
| 		} | ||||
| 	} else if (!strcasecmp(line, "codecs")) { | ||||
| 		ast_format_cap_update_by_allow_disallow(o->capabilities, c, 1); | ||||
| 	} else if (!strcasecmp(line, "context")) { | ||||
| 		ast_string_field_set(o, context, c); | ||||
| 	} else if (!strcasecmp(line, "extension")) { | ||||
| 		ast_string_field_set(o, exten, c); | ||||
| 	} else if (!strcasecmp(line, "priority")) { | ||||
| 		if ((sscanf(c, "%30d", &o->priority) != 1) || (o->priority < 1)) { | ||||
| 			ast_log(LOG_WARNING, "Invalid priority at line %d of %s\n", lineno, o->fn); | ||||
| 			o->priority = 1; | ||||
| 		} | ||||
| 	} else if (!strcasecmp(line, "retrytime")) { | ||||
| 		if ((sscanf(c, "%30d", &o->retrytime) != 1) || (o->retrytime < 1)) { | ||||
| 			ast_log(LOG_WARNING, "Invalid retrytime at line %d of %s\n", lineno, o->fn); | ||||
| 			o->retrytime = 300; | ||||
| 		} | ||||
| 	} else if (!strcasecmp(line, "waittime")) { | ||||
| 		if ((sscanf(c, "%30d", &o->waittime) != 1) || (o->waittime < 1)) { | ||||
| 			ast_log(LOG_WARNING, "Invalid waittime at line %d of %s\n", lineno, o->fn); | ||||
| 			o->waittime = 45; | ||||
| 		} | ||||
| 	} else if (!strcasecmp(line, "retry")) { | ||||
| 		o->retries++; | ||||
| 	} else if (!strcasecmp(line, "startretry")) { | ||||
| 		if (sscanf(c, "%30ld", &o->callingpid) != 1) { | ||||
| 			ast_log(LOG_WARNING, "Unable to retrieve calling PID!\n"); | ||||
| 			o->callingpid = 0; | ||||
| 		} | ||||
| 	} else if (!strcasecmp(line, "endretry") || !strcasecmp(line, "abortretry")) { | ||||
| 		o->callingpid = 0; | ||||
| 		o->retries++; | ||||
| 	} else if (!strcasecmp(line, "delayedretry")) { | ||||
| 	} else if (!strcasecmp(line, "setvar") || !strcasecmp(line, "set")) { | ||||
| 		char *c2 = c; | ||||
|  | ||||
| 		strsep(&c2, "="); | ||||
| 		if (c2) { | ||||
| 			struct ast_variable *var = ast_variable_new(c, c2, o->fn); | ||||
|  | ||||
| 			if (var) { | ||||
| 				/* | ||||
| 				 * Always insert at the end, because some people | ||||
| 				 * want to treat the spool file as a script | ||||
| 				 */ | ||||
| 				struct ast_variable **tail = &o->vars; | ||||
|  | ||||
| 				while (*tail) { | ||||
| 					tail = &(*tail)->next; | ||||
| 				} | ||||
| 				*tail = var; | ||||
| 			} | ||||
| 		} else { | ||||
| 			ast_log(LOG_WARNING, "Malformed \"%s\" argument.  Should be \"%s: variable=value\"\n", line, line); | ||||
| 		} | ||||
| 	} else if (!strcasecmp(line, "account")) { | ||||
| 		ast_string_field_set(o, account, c); | ||||
| 	} else if (!strcasecmp(line, "alwaysdelete")) { | ||||
| 		ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ALWAYS_DELETE); | ||||
| 	} else if (!strcasecmp(line, "archive")) { | ||||
| 		ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ARCHIVE); | ||||
| 	} else if (!strcasecmp(line, "early_media")) { | ||||
| 		ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_EARLY_MEDIA); | ||||
| 	} else { | ||||
| 		ast_log(LOG_WARNING, "Unknown keyword '%s' at line %d of %s\n", line, lineno, o->fn); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| #define LINE_BUFFER_SIZE 1024 | ||||
|  | ||||
| static int apply_outgoing(struct outgoing *o, FILE *f) | ||||
| { | ||||
| 	char buf[LINE_BUFFER_SIZE]; | ||||
| 	unsigned int lineno = 0; | ||||
|  | ||||
| 	while (fgets(buf, sizeof(buf), f)) { | ||||
| 		size_t len = strlen(buf); | ||||
|  | ||||
| 		lineno++; | ||||
| 		/* Trim comments */ | ||||
| 		c = buf; | ||||
| 		while ((c = strchr(c, '#'))) { | ||||
| 			if ((c == buf) || (*(c-1) == ' ') || (*(c-1) == '\t')) | ||||
| 				*c = '\0'; | ||||
| 			else | ||||
| 				c++; | ||||
|  | ||||
| 		if (buf[len - 1] == '\n' || feof(f)) { | ||||
| 			/* We have a line, parse it */ | ||||
| 			parse_line(buf, lineno, o); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		c = buf; | ||||
| 		while ((c = strchr(c, ';'))) { | ||||
| 			if ((c > buf) && (c[-1] == '\\')) { | ||||
| 				memmove(c - 1, c, strlen(c) + 1); | ||||
| 				c++; | ||||
| 			} else { | ||||
| 				*c = '\0'; | ||||
| 		/* Crazy long line, skip it */ | ||||
| 		ast_log(LOG_WARNING, "Skipping extremely long line at line %d of %s\n", lineno, o->fn); | ||||
|  | ||||
| 		/* Consume the rest of the problematic line */ | ||||
| 		while (fgets(buf, sizeof(buf), f)) { | ||||
| 			len = strlen(buf); | ||||
| 			if (buf[len - 1] == '\n' || feof(f)) { | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/* Trim trailing white space */ | ||||
| 		ast_trim_blanks(buf); | ||||
| 		if (ast_strlen_zero(buf)) { | ||||
| 			continue; | ||||
| 		} | ||||
| 		c = strchr(buf, ':'); | ||||
| 		if (!c) { | ||||
| 			ast_log(LOG_NOTICE, "Syntax error at line %d of %s\n", lineno, o->fn); | ||||
| 			continue; | ||||
| 		} | ||||
| 		*c = '\0'; | ||||
| 		c = ast_skip_blanks(c + 1); | ||||
| #if 0 | ||||
| 		printf("'%s' is '%s' at line %d\n", buf, c, lineno); | ||||
| #endif | ||||
| 		if (!strcasecmp(buf, "channel")) { | ||||
| 			if ((c2 = strchr(c, '/'))) { | ||||
| 				*c2 = '\0'; | ||||
| 				c2++; | ||||
| 				ast_string_field_set(o, tech, c); | ||||
| 				ast_string_field_set(o, dest, c2); | ||||
| 			} else { | ||||
| 				ast_log(LOG_NOTICE, "Channel should be in form Tech/Dest at line %d of %s\n", lineno, o->fn); | ||||
| 			} | ||||
| 		} else if (!strcasecmp(buf, "callerid")) { | ||||
| 			char cid_name[80] = {0}, cid_num[80] = {0}; | ||||
| 			ast_callerid_split(c, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num)); | ||||
| 			ast_string_field_set(o, cid_num, cid_num); | ||||
| 			ast_string_field_set(o, cid_name, cid_name); | ||||
| 		} else if (!strcasecmp(buf, "application")) { | ||||
| 			ast_string_field_set(o, app, c); | ||||
| 		} else if (!strcasecmp(buf, "data")) { | ||||
| 			ast_string_field_set(o, data, c); | ||||
| 		} else if (!strcasecmp(buf, "maxretries")) { | ||||
| 			if (sscanf(c, "%30d", &o->maxretries) != 1) { | ||||
| 				ast_log(LOG_WARNING, "Invalid max retries at line %d of %s\n", lineno, o->fn); | ||||
| 				o->maxretries = 0; | ||||
| 			} | ||||
| 		} else if (!strcasecmp(buf, "codecs")) { | ||||
| 			ast_format_cap_update_by_allow_disallow(o->capabilities, c, 1); | ||||
| 		} else if (!strcasecmp(buf, "context")) { | ||||
| 			ast_string_field_set(o, context, c); | ||||
| 		} else if (!strcasecmp(buf, "extension")) { | ||||
| 			ast_string_field_set(o, exten, c); | ||||
| 		} else if (!strcasecmp(buf, "priority")) { | ||||
| 			if ((sscanf(c, "%30d", &o->priority) != 1) || (o->priority < 1)) { | ||||
| 				ast_log(LOG_WARNING, "Invalid priority at line %d of %s\n", lineno, o->fn); | ||||
| 				o->priority = 1; | ||||
| 			} | ||||
| 		} else if (!strcasecmp(buf, "retrytime")) { | ||||
| 			if ((sscanf(c, "%30d", &o->retrytime) != 1) || (o->retrytime < 1)) { | ||||
| 				ast_log(LOG_WARNING, "Invalid retrytime at line %d of %s\n", lineno, o->fn); | ||||
| 				o->retrytime = 300; | ||||
| 			} | ||||
| 		} else if (!strcasecmp(buf, "waittime")) { | ||||
| 			if ((sscanf(c, "%30d", &o->waittime) != 1) || (o->waittime < 1)) { | ||||
| 				ast_log(LOG_WARNING, "Invalid waittime at line %d of %s\n", lineno, o->fn); | ||||
| 				o->waittime = 45; | ||||
| 			} | ||||
| 		} else if (!strcasecmp(buf, "retry")) { | ||||
| 			o->retries++; | ||||
| 		} else if (!strcasecmp(buf, "startretry")) { | ||||
| 			if (sscanf(c, "%30ld", &o->callingpid) != 1) { | ||||
| 				ast_log(LOG_WARNING, "Unable to retrieve calling PID!\n"); | ||||
| 				o->callingpid = 0; | ||||
| 			} | ||||
| 		} else if (!strcasecmp(buf, "endretry") || !strcasecmp(buf, "abortretry")) { | ||||
| 			o->callingpid = 0; | ||||
| 			o->retries++; | ||||
| 		} else if (!strcasecmp(buf, "delayedretry")) { | ||||
| 		} else if (!strcasecmp(buf, "setvar") || !strcasecmp(buf, "set")) { | ||||
| 			c2 = c; | ||||
| 			strsep(&c2, "="); | ||||
| 			if (c2) { | ||||
| 				var = ast_variable_new(c, c2, o->fn); | ||||
| 				if (var) { | ||||
| 					/* Always insert at the end, because some people want to treat the spool file as a script */ | ||||
| 					if (last) { | ||||
| 						last->next = var; | ||||
| 					} else { | ||||
| 						o->vars = var; | ||||
| 					} | ||||
| 					last = var; | ||||
| 				} | ||||
| 			} else | ||||
| 				ast_log(LOG_WARNING, "Malformed \"%s\" argument.  Should be \"%s: variable=value\"\n", buf, buf); | ||||
| 		} else if (!strcasecmp(buf, "account")) { | ||||
| 			ast_string_field_set(o, account, c); | ||||
| 		} else if (!strcasecmp(buf, "alwaysdelete")) { | ||||
| 			ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ALWAYS_DELETE); | ||||
| 		} else if (!strcasecmp(buf, "archive")) { | ||||
| 			ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ARCHIVE); | ||||
| 		} else if (!strcasecmp(buf, "early_media")) { | ||||
| 			ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_EARLY_MEDIA); | ||||
| 		} else { | ||||
| 			ast_log(LOG_WARNING, "Unknown keyword '%s' at line %d of %s\n", buf, lineno, o->fn); | ||||
| 		} | ||||
| 	} | ||||
| 	if (ast_strlen_zero(o->tech) || ast_strlen_zero(o->dest) || (ast_strlen_zero(o->app) && ast_strlen_zero(o->exten))) { | ||||
| 		ast_log(LOG_WARNING, "At least one of app or extension must be specified, along with tech and dest in file %s\n", o->fn); | ||||
|  | ||||
| 	if (ast_strlen_zero(o->tech) | ||||
| 		|| ast_strlen_zero(o->dest) | ||||
| 		|| (ast_strlen_zero(o->app) && ast_strlen_zero(o->exten))) { | ||||
| 		ast_log(LOG_WARNING, "At least one of app or extension must be specified, " | ||||
| 			"along with tech and dest in file %s\n", o->fn); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	return 0; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user