| /* |
| * Author: Tatu Ylonen <ylo@cs.hut.fi> |
| * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
| * All rights reserved |
| * Simple pattern matching, with '*' and '?' as wildcards. |
| * |
| * As far as I am concerned, the code I have written for this software |
| * can be used freely for any purpose. Any derived versions of this |
| * software must be clearly marked as such, and if the derived work is |
| * incompatible with the protocol description in the RFC file, it must be |
| * called by a name other than "ssh" or "Secure Shell". |
| */ |
| |
| #include "includes.h" |
| RCSID("$OpenBSD: match.c,v 1.11 2001/01/21 19:05:52 markus Exp $"); |
| |
| #include "match.h" |
| |
| /* |
| * Returns true if the given string matches the pattern (which may contain ? |
| * and * as wildcards), and zero if it does not match. |
| */ |
| |
| int |
| match_pattern(const char *s, const char *pattern) |
| { |
| for (;;) { |
| /* If at end of pattern, accept if also at end of string. */ |
| if (!*pattern) |
| return !*s; |
| |
| if (*pattern == '*') { |
| /* Skip the asterisk. */ |
| pattern++; |
| |
| /* If at end of pattern, accept immediately. */ |
| if (!*pattern) |
| return 1; |
| |
| /* If next character in pattern is known, optimize. */ |
| if (*pattern != '?' && *pattern != '*') { |
| /* |
| * Look instances of the next character in |
| * pattern, and try to match starting from |
| * those. |
| */ |
| for (; *s; s++) |
| if (*s == *pattern && |
| match_pattern(s + 1, pattern + 1)) |
| return 1; |
| /* Failed. */ |
| return 0; |
| } |
| /* |
| * Move ahead one character at a time and try to |
| * match at each position. |
| */ |
| for (; *s; s++) |
| if (match_pattern(s, pattern)) |
| return 1; |
| /* Failed. */ |
| return 0; |
| } |
| /* |
| * There must be at least one more character in the string. |
| * If we are at the end, fail. |
| */ |
| if (!*s) |
| return 0; |
| |
| /* Check if the next character of the string is acceptable. */ |
| if (*pattern != '?' && *pattern != *s) |
| return 0; |
| |
| /* Move to the next character, both in string and in pattern. */ |
| s++; |
| pattern++; |
| } |
| /* NOTREACHED */ |
| } |
| |
| /* |
| * Tries to match the host name (which must be in all lowercase) against the |
| * comma-separated sequence of subpatterns (each possibly preceded by ! to |
| * indicate negation). Returns -1 if negation matches, 1 if there is |
| * a positive match, 0 if there is no match at all. |
| */ |
| |
| int |
| match_hostname(const char *host, const char *pattern, u_int len) |
| { |
| char sub[1024]; |
| int negated; |
| int got_positive; |
| u_int i, subi; |
| |
| got_positive = 0; |
| for (i = 0; i < len;) { |
| /* Check if the subpattern is negated. */ |
| if (pattern[i] == '!') { |
| negated = 1; |
| i++; |
| } else |
| negated = 0; |
| |
| /* |
| * Extract the subpattern up to a comma or end. Convert the |
| * subpattern to lowercase. |
| */ |
| for (subi = 0; |
| i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; |
| subi++, i++) |
| sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i]; |
| /* If subpattern too long, return failure (no match). */ |
| if (subi >= sizeof(sub) - 1) |
| return 0; |
| |
| /* If the subpattern was terminated by a comma, skip the comma. */ |
| if (i < len && pattern[i] == ',') |
| i++; |
| |
| /* Null-terminate the subpattern. */ |
| sub[subi] = '\0'; |
| |
| /* Try to match the subpattern against the host name. */ |
| if (match_pattern(host, sub)) { |
| if (negated) |
| return -1; /* Negative */ |
| else |
| got_positive = 1; /* Positive */ |
| } |
| } |
| |
| /* |
| * Return success if got a positive match. If there was a negative |
| * match, we have already returned -1 and never get here. |
| */ |
| return got_positive; |
| } |