mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-11-04 05:15:22 +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