match.cc 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /*
  2. * Copyright (C) 1997 Robey Pointer
  3. * Copyright (C) 1999 - 2002 Eggheads Development Team
  4. * Copyright (C) 2002 - 2014 Bryan Drewery
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19. */
  20. /*
  21. * match.c
  22. * wildcard matching functions
  23. *
  24. *
  25. * Once this code was working, I added support for % so that I could
  26. * use the same code both in Eggdrop and in my IrcII client.
  27. * Pleased with this, I added the option of a fourth wildcard, ~,
  28. * which matches varying amounts of whitespace (at LEAST one space,
  29. * though, for sanity reasons).
  30. *
  31. * This code would not have been possible without the prior work and
  32. * suggestions of various sourced. Special thanks to Robey for
  33. * all his time/help tracking down bugs and his ever-helpful advice.
  34. *
  35. * 04/09: Fixed the "*\*" against "*a" bug (caused an endless loop)
  36. *
  37. * Chris Fuller (aka Fred1@IRC & Fwitz@IRC)
  38. * crf@cfox.bchs.uh.edu
  39. *
  40. * I hereby release this code into the public domain
  41. *
  42. */
  43. #include <sys/types.h>
  44. #include <sys/socket.h>
  45. #include <netinet/in.h>
  46. #include <arpa/inet.h>
  47. #include "common.h"
  48. #include "match.h"
  49. #include "misc.h"
  50. #include "rfc1459.h"
  51. #include "socket.h"
  52. #define QUOTE '\\' /* quoting character (overrides wildcards) */
  53. #define WILDS '*' /* matches 0 or more characters (including spaces) */
  54. #define WILDP '%' /* matches 0 or more non-space characters */
  55. #define WILDQ '?' /* matches ecactly one character */
  56. #define WILDT '~' /* matches 1 or more spaces */
  57. #define NOMATCH 0
  58. #define MATCH (match+sofar)
  59. #define PERMATCH (match+saved+sofar)
  60. /* binds matching */
  61. int _wild_match_per(const unsigned char *m, const unsigned char *n)
  62. {
  63. /* null strings should never match */
  64. if ((m == 0) || (n == 0) || (!*n))
  65. return NOMATCH;
  66. const unsigned char *ma = m, *lsm = NULL, *lsn = NULL, *lpm = NULL, *lpn = NULL;
  67. int match = 1, saved = 0, space;
  68. int sofar = 0;
  69. while (*n) {
  70. if (*m == WILDT) { /* Match >=1 space */
  71. space = 0; /* Don't need any spaces */
  72. do {
  73. m++;
  74. space++;
  75. } /* Tally 1 more space ... */
  76. while ((*m == WILDT) || (*m == ' ')); /* for each space or ~ */
  77. sofar += space; /* Each counts as exact */
  78. while (*n == ' ') {
  79. n++;
  80. space--;
  81. } /* Do we have enough? */
  82. if (space <= 0)
  83. continue; /* Had enough spaces! */
  84. }
  85. /* Do the fallback */
  86. else {
  87. switch (*m) {
  88. case 0:
  89. do
  90. m--; /* Search backwards */
  91. while ((m > ma) && (*m == '?')); /* For first non-? char */
  92. if ((m > ma) ? ((*m == '*') && (m[-1] != QUOTE)) : (*m == '*'))
  93. return PERMATCH; /* nonquoted * = match */
  94. break;
  95. case WILDP:
  96. while (*(++m) == WILDP)
  97. ; /* Zap redundant %s */
  98. if (*m != WILDS) { /* Don't both if next=* */
  99. if (*n != ' ') { /* WILDS can't match ' ' */
  100. lpm = m;
  101. lpn = n; /* Save '%' fallback spot */
  102. saved += sofar;
  103. sofar = 0; /* And save tally count */
  104. }
  105. continue; /* Done with '%' */
  106. }
  107. /* FALL THROUGH */
  108. case WILDS:
  109. do
  110. m++; /* Zap redundant wilds */
  111. while ((*m == WILDS) || (*m == WILDP));
  112. lsm = m;
  113. lsn = n;
  114. lpm = 0; /* Save '*' fallback spot */
  115. match += (saved + sofar); /* Save tally count */
  116. saved = sofar = 0;
  117. continue; /* Done with '*' */
  118. case WILDQ:
  119. m++;
  120. n++;
  121. continue; /* Match one char */
  122. case QUOTE:
  123. m++; /* Handle quoting */
  124. }
  125. if (rfc_char_equal(*m, *n)) { /* If matching */
  126. m++;
  127. n++;
  128. sofar++;
  129. continue; /* Tally the match */
  130. }
  131. #ifdef WILDT
  132. }
  133. #endif
  134. if (lpm) { /* Try to fallback on '%' */
  135. n = ++lpn;
  136. m = lpm;
  137. sofar = 0; /* Restore position */
  138. if ((*n | 32) == 32)
  139. lpm = 0; /* Can't match 0 or ' ' */
  140. continue; /* Next char, please */
  141. }
  142. if (lsm) { /* Try to fallback on '*' */
  143. n = ++lsn;
  144. m = lsm; /* Restore position */
  145. saved = sofar = 0;
  146. continue; /* Next char, please */
  147. }
  148. return NOMATCH; /* No fallbacks=No match */
  149. }
  150. while ((*m == WILDS) || (*m == WILDP))
  151. m++; /* Zap leftover %s & *s */
  152. return (*m) ? NOMATCH : PERMATCH; /* End of both = match */
  153. }
  154. /* general/host matching */
  155. int _wild_match(const unsigned char *m, const unsigned char *n)
  156. {
  157. const unsigned char *ma = m, *na = n;
  158. /* null strings should never match */
  159. if ((ma == 0) || (na == 0) || (!*ma) || (!*na))
  160. return NOMATCH;
  161. const unsigned char *lsm = NULL, *lsn = NULL;
  162. int match = 1;
  163. int sofar = 0;
  164. /* find the end of each string */
  165. while (*(++m))
  166. ;
  167. m--;
  168. while (*(++n))
  169. ;
  170. n--;
  171. while (n >= na) {
  172. /* If the mask runs out of chars before the string, fall back on
  173. * a wildcard or fail. */
  174. if (m < ma) {
  175. if (lsm) {
  176. n = --lsn;
  177. m = lsm;
  178. if (n < na) lsm = 0;
  179. sofar = 0;
  180. }
  181. else return NOMATCH;
  182. }
  183. switch (*m) {
  184. case WILDS: /* Matches anything */
  185. do
  186. m--; /* Zap redundant wilds */
  187. while ((m >= ma) && (*m == WILDS));
  188. lsm = m;
  189. lsn = n;
  190. match += sofar;
  191. sofar = 0; /* Update fallback pos */
  192. if (m < ma) return MATCH;
  193. continue; /* Next char, please */
  194. case WILDQ:
  195. m--;
  196. n--;
  197. continue; /* '?' always matches */
  198. }
  199. if (rfc_char_equal(*m, *n)) { /* If matching char */
  200. m--;
  201. n--;
  202. sofar++; /* Tally the match */
  203. continue; /* Next char, please */
  204. }
  205. if (lsm) { /* To to fallback on '*' */
  206. n = --lsn;
  207. m = lsm;
  208. if (n < na)
  209. lsm = 0; /* Rewind to saved pos */
  210. sofar = 0;
  211. continue; /* Next char, please */
  212. }
  213. return NOMATCH; /* No fallback=No match */
  214. }
  215. while ((m >= ma) && (*m == WILDS))
  216. m--; /* Zap leftover %s & *s */
  217. return (m >= ma) ? NOMATCH : MATCH; /* Start of both = match */
  218. }
  219. static inline int
  220. comp_with_mask(void *addr, void *dest, unsigned int mask)
  221. {
  222. int n = mask >> 3;
  223. if (memcmp(addr, dest, n) == 0)
  224. {
  225. int leftover = mask % 8;
  226. if (leftover == 0)
  227. return (1);
  228. int m = ((~0U) << (8 - leftover));
  229. if ((((unsigned char *) addr)[n] & m) == (((unsigned char *) dest)[n] & m))
  230. return (1);
  231. }
  232. return (0);
  233. }
  234. /* match_cidr()
  235. *
  236. * Input - mask, address
  237. * Ouput - + = Matched 0 = Did not match
  238. */
  239. int
  240. match_cidr(const char *m, const char *a)
  241. {
  242. const char *len_p = strrchr(m, '/');
  243. if(len_p == NULL)
  244. return 0;
  245. const char *ip_mask_p = strrchr(m, '@');
  246. if(ip_mask_p == NULL)
  247. return 0;
  248. const char *ip_p = strrchr(a, '@');
  249. if(ip_p == NULL)
  250. return 0;
  251. char mask[NICKLEN + UHOSTLEN + 6] = "", *ipmask = NULL, *ip = NULL, *len = NULL;
  252. strlcpy(mask, m, sizeof(mask));
  253. ipmask = mask + (ip_mask_p - m);
  254. char address[NICKLEN + UHOSTLEN + 6] = "";
  255. strlcpy(address, a, sizeof(address));
  256. ip = address + (ip_p - a);
  257. *ipmask++ = '\0';
  258. len = mask + (len_p - m);
  259. *len++ = '\0';
  260. if (!str_isdigit(len))
  261. return 0;
  262. int cidrlen = atoi(len);
  263. if(cidrlen <= 0)
  264. return 0;
  265. *ip++ = '\0';
  266. int ret = 0;
  267. int aftype = 0;
  268. sockname_t ipaddr, maskaddr;
  269. bzero(&ipaddr, sizeof(ipaddr));
  270. bzero(&maskaddr, sizeof(maskaddr));
  271. if (!strchr(ip, ':') && !strchr(ipmask, ':'))
  272. aftype = ipaddr.family = maskaddr.family = AF_INET;
  273. #ifdef USE_IPV6
  274. else if (strchr(ip, ':') && strchr(ipmask, ':'))
  275. aftype = ipaddr.family = maskaddr.family = AF_INET6;
  276. #endif /* USE_IPV6 */
  277. else
  278. return 0;
  279. #ifdef USE_IPV6
  280. if (aftype == AF_INET6) {
  281. if (cidrlen > 128)
  282. return 0;
  283. inet_pton(aftype, ip, &ipaddr.u.ipv6.sin6_addr);
  284. inet_pton(aftype, ipmask, &maskaddr.u.ipv6.sin6_addr);
  285. if (comp_with_mask(&ipaddr.u.ipv6.sin6_addr.s6_addr, &maskaddr.u.ipv6.sin6_addr.s6_addr, cidrlen) &&
  286. ((ret = wild_match(mask, address))))
  287. return ret;
  288. } else if (aftype == AF_INET) {
  289. #endif /* USE_IPV6 */
  290. if (cidrlen > 32)
  291. return 0;
  292. inet_pton(aftype, ip, &ipaddr.u.ipv4.sin_addr);
  293. inet_pton(aftype, ipmask, &maskaddr.u.ipv4.sin_addr);
  294. if (comp_with_mask(&ipaddr.u.ipv4.sin_addr.s_addr, &maskaddr.u.ipv4.sin_addr.s_addr, cidrlen) &&
  295. ((ret = wild_match(mask, address))))
  296. return ret;
  297. #ifdef USE_IPV6
  298. }
  299. #endif
  300. return 0;
  301. }
  302. /* vim: set sts=2 sw=2 ts=8 et: */