match.c revision 60573
157429Smarkm/*
260573Skris *
357429Smarkm * match.c
460573Skris *
557429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi>
660573Skris *
757429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
857429Smarkm *                    All rights reserved
960573Skris *
1057429Smarkm * Created: Thu Jun 22 01:17:50 1995 ylo
1160573Skris *
1257429Smarkm * Simple pattern matching, with '*' and '?' as wildcards.
1360573Skris *
1457429Smarkm */
1557429Smarkm
1657429Smarkm#include "includes.h"
1760573SkrisRCSID("$Id: match.c,v 1.6 2000/04/14 10:30:31 markus Exp $");
1857429Smarkm
1957429Smarkm#include "ssh.h"
2057429Smarkm
2157429Smarkm/*
2257429Smarkm * Returns true if the given string matches the pattern (which may contain ?
2357429Smarkm * and * as wildcards), and zero if it does not match.
2457429Smarkm */
2557429Smarkm
2660573Skrisint
2757429Smarkmmatch_pattern(const char *s, const char *pattern)
2857429Smarkm{
2957429Smarkm	for (;;) {
3057429Smarkm		/* If at end of pattern, accept if also at end of string. */
3157429Smarkm		if (!*pattern)
3257429Smarkm			return !*s;
3357429Smarkm
3457429Smarkm		if (*pattern == '*') {
3557429Smarkm			/* Skip the asterisk. */
3657429Smarkm			pattern++;
3757429Smarkm
3857429Smarkm			/* If at end of pattern, accept immediately. */
3957429Smarkm			if (!*pattern)
4057429Smarkm				return 1;
4157429Smarkm
4257429Smarkm			/* If next character in pattern is known, optimize. */
4357429Smarkm			if (*pattern != '?' && *pattern != '*') {
4457429Smarkm				/*
4557429Smarkm				 * Look instances of the next character in
4657429Smarkm				 * pattern, and try to match starting from
4757429Smarkm				 * those.
4857429Smarkm				 */
4957429Smarkm				for (; *s; s++)
5057429Smarkm					if (*s == *pattern &&
5157429Smarkm					    match_pattern(s + 1, pattern + 1))
5257429Smarkm						return 1;
5357429Smarkm				/* Failed. */
5457429Smarkm				return 0;
5557429Smarkm			}
5657429Smarkm			/*
5757429Smarkm			 * Move ahead one character at a time and try to
5857429Smarkm			 * match at each position.
5957429Smarkm			 */
6057429Smarkm			for (; *s; s++)
6157429Smarkm				if (match_pattern(s, pattern))
6257429Smarkm					return 1;
6357429Smarkm			/* Failed. */
6457429Smarkm			return 0;
6557429Smarkm		}
6657429Smarkm		/*
6757429Smarkm		 * There must be at least one more character in the string.
6857429Smarkm		 * If we are at the end, fail.
6957429Smarkm		 */
7057429Smarkm		if (!*s)
7157429Smarkm			return 0;
7257429Smarkm
7357429Smarkm		/* Check if the next character of the string is acceptable. */
7457429Smarkm		if (*pattern != '?' && *pattern != *s)
7557429Smarkm			return 0;
7657429Smarkm
7757429Smarkm		/* Move to the next character, both in string and in pattern. */
7857429Smarkm		s++;
7957429Smarkm		pattern++;
8057429Smarkm	}
8157429Smarkm	/* NOTREACHED */
8257429Smarkm}
8358582Skris
8458582Skris/*
8558582Skris * Tries to match the host name (which must be in all lowercase) against the
8658582Skris * comma-separated sequence of subpatterns (each possibly preceded by ! to
8758582Skris * indicate negation).  Returns true if there is a positive match; zero
8858582Skris * otherwise.
8958582Skris */
9058582Skris
9158582Skrisint
9258582Skrismatch_hostname(const char *host, const char *pattern, unsigned int len)
9358582Skris{
9458582Skris	char sub[1024];
9558582Skris	int negated;
9658582Skris	int got_positive;
9758582Skris	unsigned int i, subi;
9858582Skris
9958582Skris	got_positive = 0;
10058582Skris	for (i = 0; i < len;) {
10158582Skris		/* Check if the subpattern is negated. */
10258582Skris		if (pattern[i] == '!') {
10358582Skris			negated = 1;
10458582Skris			i++;
10558582Skris		} else
10658582Skris			negated = 0;
10758582Skris
10858582Skris		/*
10958582Skris		 * Extract the subpattern up to a comma or end.  Convert the
11058582Skris		 * subpattern to lowercase.
11158582Skris		 */
11258582Skris		for (subi = 0;
11358582Skris		     i < len && subi < sizeof(sub) - 1 && pattern[i] != ',';
11458582Skris		     subi++, i++)
11558582Skris			sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i];
11658582Skris		/* If subpattern too long, return failure (no match). */
11758582Skris		if (subi >= sizeof(sub) - 1)
11858582Skris			return 0;
11958582Skris
12058582Skris		/* If the subpattern was terminated by a comma, skip the comma. */
12158582Skris		if (i < len && pattern[i] == ',')
12258582Skris			i++;
12358582Skris
12458582Skris		/* Null-terminate the subpattern. */
12558582Skris		sub[subi] = '\0';
12658582Skris
12758582Skris		/* Try to match the subpattern against the host name. */
12858582Skris		if (match_pattern(host, sub)) {
12958582Skris			if (negated)
13058582Skris				return 0;	/* Fail */
13158582Skris			else
13258582Skris				got_positive = 1;
13358582Skris		}
13458582Skris	}
13558582Skris
13658582Skris	/*
13758582Skris	 * Return success if got a positive match.  If there was a negative
13858582Skris	 * match, we have already returned zero and never get here.
13958582Skris	 */
14058582Skris	return got_positive;
14158582Skris}
142