📄 parse.c
字号:
/* * parse.c Parse a policy language * * Version: $Id: parse.c,v 1.25 2008/03/07 10:03:35 aland Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2004 Alan DeKok <aland@ox.org> * Copyright 2006 The FreeRADIUS server project */#include <freeradius-devel/ident.h>RCSID("$Id: parse.c,v 1.25 2008/03/07 10:03:35 aland Exp $")#include "rlm_policy.h"#ifdef HAVE_DIRENT_H#include <dirent.h>#ifdef HAVE_SYS_STAT_H#include <sys/stat.h>#endif#endif#include <freeradius-devel/modules.h>const FR_NAME_NUMBER policy_return_codes[] = { { "reject", RLM_MODULE_REJECT }, { "fail", RLM_MODULE_FAIL }, { "ok", RLM_MODULE_OK }, { "handled", RLM_MODULE_HANDLED }, { "invalid", RLM_MODULE_INVALID }, { "userlock", RLM_MODULE_USERLOCK }, { "notfound", RLM_MODULE_NOTFOUND }, { "noop", RLM_MODULE_NOOP }, { "updated", RLM_MODULE_UPDATED }, { NULL, RLM_MODULE_NUMCODES }};/* * Explanations of what the lexical tokens are. */static const FR_NAME_NUMBER policy_explanations[] = { { "invalid input", POLICY_LEX_BAD }, { "end of file", POLICY_LEX_EOF }, { "end of line", POLICY_LEX_EOL }, { "whitespace", POLICY_LEX_WHITESPACE }, { "hash mark", POLICY_LEX_HASH }, { "left bracket", POLICY_LEX_L_BRACKET }, { "right bracket", POLICY_LEX_R_BRACKET }, { "{", POLICY_LEX_LC_BRACKET }, { "}", POLICY_LEX_RC_BRACKET }, { "comma", POLICY_LEX_COMMA }, { "logical AND", POLICY_LEX_L_AND }, { "logical OR", POLICY_LEX_L_OR }, { "AND", POLICY_LEX_AND }, { "OR", POLICY_LEX_OR }, { "logical NOT", POLICY_LEX_L_NOT }, { "assignment", POLICY_LEX_ASSIGN }, { "comparison", POLICY_LEX_CMP_EQUALS }, { "comparison", POLICY_LEX_CMP_NOT_EQUALS }, { "comparison", POLICY_LEX_LT }, { "comparison", POLICY_LEX_GT }, { "comparison", POLICY_LEX_LE }, { "comparison", POLICY_LEX_GT }, { "comparison", POLICY_LEX_RX_EQUALS }, { "comparison", POLICY_LEX_RX_NOT_EQUALS }, { "double quoted string", POLICY_LEX_DOUBLE_QUOTED_STRING }, { "single quoted string", POLICY_LEX_SINGLE_QUOTED_STRING }, { "back quoted string", POLICY_LEX_BACK_QUOTED_STRING }, { "bare word", POLICY_LEX_BARE_WORD }, { NULL, -1 }};const FR_NAME_NUMBER rlm_policy_tokens[] = { { "EOF", POLICY_LEX_EOF }, { "#", POLICY_LEX_HASH }, { "(", POLICY_LEX_L_BRACKET }, { ")", POLICY_LEX_R_BRACKET }, { "{", POLICY_LEX_LC_BRACKET }, { "}", POLICY_LEX_RC_BRACKET }, { ",", POLICY_LEX_COMMA }, { "&&", POLICY_LEX_L_AND }, { "||", POLICY_LEX_L_OR }, { "&", POLICY_LEX_AND }, { "|", POLICY_LEX_OR }, { "!", POLICY_LEX_L_NOT }, { "=", POLICY_LEX_ASSIGN }, { "==", POLICY_LEX_CMP_EQUALS }, { "!=", POLICY_LEX_CMP_NOT_EQUALS }, { "=*", POLICY_LEX_CMP_TRUE }, { "!*", POLICY_LEX_CMP_FALSE }, { "<", POLICY_LEX_LT }, { ">", POLICY_LEX_GT }, { "<=", POLICY_LEX_LE }, { ">=", POLICY_LEX_GT }, { "=~", POLICY_LEX_RX_EQUALS }, { "!~", POLICY_LEX_RX_NOT_EQUALS }, { "^=", POLICY_LEX_BEFORE_HEAD_ASSIGN }, { "^==", POLICY_LEX_BEFORE_WHERE_ASSIGN }, { "^.", POLICY_LEX_BEFORE_HEAD_EQUALS }, { "^.=", POLICY_LEX_BEFORE_WHERE_EQUALS }, { "$=", POLICY_LEX_AFTER_TAIL_ASSIGN }, { "$==", POLICY_LEX_AFTER_WHERE_ASSIGN }, { "$.", POLICY_LEX_AFTER_TAIL_EQUALS }, { "$.=", POLICY_LEX_AFTER_WHERE_EQUALS }, { ".=", POLICY_LEX_CONCAT_EQUALS }, { ":=", POLICY_LEX_SET_EQUALS }, { "double quoted string", POLICY_LEX_DOUBLE_QUOTED_STRING }, { "single quoted string", POLICY_LEX_SINGLE_QUOTED_STRING }, { "back quoted string", POLICY_LEX_BACK_QUOTED_STRING }, { "bare word", POLICY_LEX_BARE_WORD }, { NULL, -1 }};/* * Hand-coded lexical analysis of a string. * Handed input string, updates token, possible a decoded * string in buffer, and returns the pointer to the next token. * * Lexical tokens cannot cross a string boundary. */static const char *policy_lex_string(const char *input, policy_lex_t *token, char *buffer, size_t buflen){ rad_assert(input != NULL); if (buffer) *buffer = '\0'; switch (*input) { case '\0': *token = POLICY_LEX_EOL; return NULL; /* nothing more to do */ case ' ': case '\t': case '\r': case '\n': /* * Skip over all of the whitespace in one swell foop. */ *token = POLICY_LEX_WHITESPACE; while ((*input == ' ') || (*input == '\t') || (*input == '\r') || (*input == '\n')) input++; return input; /* point to next non-whitespace character */ case '#': /* ignore everything to the end of the line */ *token = POLICY_LEX_EOL; return NULL; case '(': *token = POLICY_LEX_L_BRACKET; return input + 1; case ')': *token = POLICY_LEX_R_BRACKET; return input + 1; case '{': *token = POLICY_LEX_LC_BRACKET; return input + 1; case '}': *token = POLICY_LEX_RC_BRACKET; return input + 1; case ',': *token = POLICY_LEX_COMMA; return input + 1; case '+': switch (input[1]) { case '=': *token = POLICY_LEX_PLUS_EQUALS; input++; break; default: *token = POLICY_LEX_PLUS; break; } return input + 1; case '-': switch (input[1]) { case '=': *token = POLICY_LEX_MINUS_EQUALS; input++; break; default: *token = POLICY_LEX_MINUS; break; } return input + 1; case '.': if (input[1] == '=') { *token = POLICY_LEX_CONCAT_EQUALS; return input + 2; } *token = POLICY_LEX_BAD; return input + 1; case '^': if (input[1] == '.' ) { if (input[2] == '=') { *token = POLICY_LEX_BEFORE_WHERE_EQUALS; return input + 3; } else { *token = POLICY_LEX_BEFORE_HEAD_EQUALS; return input + 2; } } else if (input[1] == '=') { if (input[2] == '=') { *token = POLICY_LEX_BEFORE_WHERE_ASSIGN; return input + 3; } else { *token = POLICY_LEX_BEFORE_HEAD_ASSIGN; return input + 2; } } *token = POLICY_LEX_BAD; return input + 1; case '$': if (input[1] == '.' ) { if (input[2] == '=') { *token = POLICY_LEX_AFTER_WHERE_EQUALS; return input + 3; } else { *token = POLICY_LEX_AFTER_TAIL_EQUALS; return input + 2; } } else if (input[1] == '=') { if (input[2] == '=') { *token = POLICY_LEX_AFTER_WHERE_ASSIGN; return input + 3; } else { *token = POLICY_LEX_AFTER_TAIL_ASSIGN; return input + 2; } } *token = POLICY_LEX_BAD; return input + 1; case ':': if (input[1] == '=') { *token = POLICY_LEX_SET_EQUALS; return input + 2; } *token = POLICY_LEX_BAD; return input + 1; case '&': switch (input[1]) { case '&': *token = POLICY_LEX_L_AND; input++; break; case '=': *token = POLICY_LEX_AND_EQUALS; input++; break; default: *token = POLICY_LEX_AND; } return input + 1; case '|': switch (input[1]) { case '|': *token = POLICY_LEX_L_OR; input++; break; case '=': *token = POLICY_LEX_OR_EQUALS; input++; break; default: *token = POLICY_LEX_OR; } return input + 1; case '!': switch (input[1]) { case '=': input++; *token = POLICY_LEX_CMP_NOT_EQUALS; break; case '~': input++; *token = POLICY_LEX_RX_NOT_EQUALS; break; case '*': input++; *token = POLICY_LEX_CMP_FALSE; break; default: *token = POLICY_LEX_L_NOT; } return input + 1; case '=': switch (input[1]) { case '=': input++; *token = POLICY_LEX_CMP_EQUALS; break; case '~': input++; *token = POLICY_LEX_RX_EQUALS; break; case '*': input++; *token = POLICY_LEX_CMP_TRUE; break; default: *token = POLICY_LEX_ASSIGN; } return input + 1; case '<': if (input[1] == '=') { input++; *token = POLICY_LEX_LE; } else { *token = POLICY_LEX_LT; } return input + 1; case '>': if (input[1] == '=') { input++; *token = POLICY_LEX_GE; } else { *token = POLICY_LEX_GT; } return input + 1; case '"': if (buflen < 2) { *token = POLICY_LEX_BAD; return input + 1; } input++; while (*input != '"') { /* * Strings can't pass EOL. */ if (!*input) { return POLICY_LEX_BAD; } /* * FIXME: Embedded quotes? */ *(buffer++) = *(input++); buflen--; /* * FIXME: Print more warnings? */ if (buflen == 1) { break; } } *buffer = '\0'; *token = POLICY_LEX_DOUBLE_QUOTED_STRING; return input + 1; /* skip trailing '"' */ default: /* bare word */ break; } /* * It's a bare word, with nowhere to put it. Die. */ if (!buffer) { *token = POLICY_LEX_BAD; return input + 1; } /* * Getting one character is stupid. */ if (buflen < 2) { *token = POLICY_LEX_BAD; return input + 1; } /* * Bare words are [-a-zA-Z0-9.]+ */ while (*input) { if (!(((*input >= '0') && (*input <= '9')) || ((*input >= 'a') && (*input <= 'z')) || ((*input >= 'A') && (*input <= 'Z')) || (*input == '-') || (*input == '.') || (*input == ':') || (*input == '_'))) { break; } *(buffer++) = *(input++); buflen--; /* * FIXME: Print more warnings? */ if (buflen == 1) { break; } } *buffer = '\0'; *token = POLICY_LEX_BARE_WORD; return input;}/* * We want to lexically analyze a file, so we need a wrapper * around the lexical analysis of strings. */typedef struct policy_lex_file_t { FILE *fp; const char *parse; const char *filename; int lineno; int debug; rbtree_t *policies; policy_lex_t token; char buffer[1024];} policy_lex_file_t;#define POLICY_LEX_FLAG_RETURN_EOL (1 << 0)#define POLICY_LEX_FLAG_PEEK (1 << 1)#define POLICY_LEX_FLAG_PRINT_TOKEN (1 << 2)#define debug_tokens if ((lexer->debug & POLICY_DEBUG_PRINT_TOKENS) && fr_log_fp) fr_printf_log/* * Function to return a token saying what it read, and possibly * a buffer of the quoted string or bare word. */static policy_lex_t policy_lex_file(policy_lex_file_t *lexer, int flags, char *mystring, size_t mystringlen){ policy_lex_t token = POLICY_LEX_BARE_WORD; /* to prime it */ if (lexer->debug & POLICY_DEBUG_PRINT_TOKENS) { flags |= POLICY_LEX_FLAG_PRINT_TOKEN; } if (!lexer->fp) { return POLICY_LEX_EOF; } /* * Starting off, the buffer needs to be primed. */ if (!lexer->parse) { lexer->parse = fgets(lexer->buffer, sizeof(lexer->buffer), lexer->fp); if (!lexer->parse) { return POLICY_LEX_EOF; } lexer->lineno = 1; } /* buffer is primed, read stuff */ if (lexer->token != POLICY_LEX_BAD) { token = lexer->token; lexer->token = POLICY_LEX_BAD; return token; } /* * Ignore whitespace, and keep filling the buffer */ while (lexer->parse) { const char *next; next = policy_lex_string(lexer->parse, &token, mystring, mystringlen); switch (token) { case POLICY_LEX_WHITESPACE: /* skip whitespace */ lexer->parse = next; continue; case POLICY_LEX_EOL: /* read another line */ lexer->parse = fgets(lexer->buffer, sizeof(lexer->buffer), lexer->fp); lexer->lineno++; if (flags & POLICY_LEX_FLAG_RETURN_EOL) { return POLICY_LEX_EOL; } break; /* read another token */ default: /* return the token */ if (!(flags & POLICY_LEX_FLAG_PEEK)) { lexer->parse = next; } if (flags & POLICY_LEX_FLAG_PRINT_TOKEN) { debug_tokens("[%s token %s] ", (flags & POLICY_LEX_FLAG_PEEK) ? "peek " : "", fr_int2str(rlm_policy_tokens, token, "?")); } return token; break; } } /* loop until EOF */ /* * Close it for the user. */ fclose(lexer->fp); lexer->fp = NULL; return POLICY_LEX_EOF;}/* * Push a token back onto the input. * * FIXME: Push words, too?
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -