match.c revision 1.6
1/*
2 *
3 * match.c
4 *
5 * Author: Tatu Ylonen <ylo@cs.hut.fi>
6 *
7 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
8 *                    All rights reserved
9 *
10 * Created: Thu Jun 22 01:17:50 1995 ylo
11 *
12 * Simple pattern matching, with '*' and '?' as wildcards.
13 *
14 */
15
16#include "includes.h"
17RCSID("$Id: match.c,v 1.6 2000/04/14 10:30:31 markus Exp $");
18
19#include "ssh.h"
20
21/*
22 * Returns true if the given string matches the pattern (which may contain ?
23 * and * as wildcards), and zero if it does not match.
24 */
25
26int
27match_pattern(const char *s, const char *pattern)
28{
29	for (;;) {
30		/* If at end of pattern, accept if also at end of string. */
31		if (!*pattern)
32			return !*s;
33
34		if (*pattern == '*') {
35			/* Skip the asterisk. */
36			pattern++;
37
38			/* If at end of pattern, accept immediately. */
39			if (!*pattern)
40				return 1;
41
42			/* If next character in pattern is known, optimize. */
43			if (*pattern != '?' && *pattern != '*') {
44				/*
45				 * Look instances of the next character in
46				 * pattern, and try to match starting from
47				 * those.
48				 */
49				for (; *s; s++)
50					if (*s == *pattern &&
51					    match_pattern(s + 1, pattern + 1))
52						return 1;
53				/* Failed. */
54				return 0;
55			}
56			/*
57			 * Move ahead one character at a time and try to
58			 * match at each position.
59			 */
60			for (; *s; s++)
61				if (match_pattern(s, pattern))
62					return 1;
63			/* Failed. */
64			return 0;
65		}
66		/*
67		 * There must be at least one more character in the string.
68		 * If we are at the end, fail.
69		 */
70		if (!*s)
71			return 0;
72
73		/* Check if the next character of the string is acceptable. */
74		if (*pattern != '?' && *pattern != *s)
75			return 0;
76
77		/* Move to the next character, both in string and in pattern. */
78		s++;
79		pattern++;
80	}
81	/* NOTREACHED */
82}
83
84/*
85 * Tries to match the host name (which must be in all lowercase) against the
86 * comma-separated sequence of subpatterns (each possibly preceded by ! to
87 * indicate negation).  Returns true if there is a positive match; zero
88 * otherwise.
89 */
90
91int
92match_hostname(const char *host, const char *pattern, unsigned int len)
93{
94	char sub[1024];
95	int negated;
96	int got_positive;
97	unsigned int i, subi;
98
99	got_positive = 0;
100	for (i = 0; i < len;) {
101		/* Check if the subpattern is negated. */
102		if (pattern[i] == '!') {
103			negated = 1;
104			i++;
105		} else
106			negated = 0;
107
108		/*
109		 * Extract the subpattern up to a comma or end.  Convert the
110		 * subpattern to lowercase.
111		 */
112		for (subi = 0;
113		     i < len && subi < sizeof(sub) - 1 && pattern[i] != ',';
114		     subi++, i++)
115			sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i];
116		/* If subpattern too long, return failure (no match). */
117		if (subi >= sizeof(sub) - 1)
118			return 0;
119
120		/* If the subpattern was terminated by a comma, skip the comma. */
121		if (i < len && pattern[i] == ',')
122			i++;
123
124		/* Null-terminate the subpattern. */
125		sub[subi] = '\0';
126
127		/* Try to match the subpattern against the host name. */
128		if (match_pattern(host, sub)) {
129			if (negated)
130				return 0;	/* Fail */
131			else
132				got_positive = 1;
133		}
134	}
135
136	/*
137	 * Return success if got a positive match.  If there was a negative
138	 * match, we have already returned zero and never get here.
139	 */
140	return got_positive;
141}
142