2016-07-27 03:35:46 +02:00
|
|
|
#include "command_match.h"
|
|
|
|
#include "command_parse.h"
|
2016-07-19 23:14:27 +02:00
|
|
|
#include <zebra.h>
|
|
|
|
#include "memory.h"
|
|
|
|
|
2016-07-27 03:35:46 +02:00
|
|
|
/* matcher helper prototypes */
|
2016-07-22 21:04:16 +02:00
|
|
|
static int
|
|
|
|
add_nexthops(struct list *, struct graph_node *);
|
2016-07-21 23:38:03 +02:00
|
|
|
|
2016-07-27 03:35:46 +02:00
|
|
|
static struct list *
|
2016-07-29 17:54:03 +02:00
|
|
|
match_command_r (struct graph_node *, vector, unsigned int);
|
2016-07-28 01:30:21 +02:00
|
|
|
|
|
|
|
static int
|
2016-08-02 18:23:54 +02:00
|
|
|
score_precedence (enum graph_node_type);
|
2016-07-27 03:35:46 +02:00
|
|
|
|
2016-08-01 15:18:25 +02:00
|
|
|
static enum match_type
|
2016-08-02 18:23:54 +02:00
|
|
|
min_match_level(enum node_type);
|
|
|
|
|
|
|
|
static struct graph_node *
|
|
|
|
copy_node (struct graph_node *);
|
|
|
|
|
|
|
|
static void
|
|
|
|
delete_nodelist (void *);
|
2016-08-01 15:18:25 +02:00
|
|
|
|
2016-08-03 04:01:52 +02:00
|
|
|
static struct graph_node *
|
|
|
|
disambiguate (struct graph_node *, struct graph_node *, char *);
|
|
|
|
|
2016-07-27 03:35:46 +02:00
|
|
|
/* token matcher prototypes */
|
2016-08-02 18:23:54 +02:00
|
|
|
static enum match_type
|
|
|
|
match_token (struct graph_node *, char *);
|
|
|
|
|
2016-07-27 03:35:46 +02:00
|
|
|
static enum match_type
|
|
|
|
match_ipv4 (const char *);
|
|
|
|
|
2016-07-22 21:04:16 +02:00
|
|
|
static enum match_type
|
2016-07-27 03:35:46 +02:00
|
|
|
match_ipv4_prefix (const char *);
|
2016-07-21 23:38:03 +02:00
|
|
|
|
2016-07-22 21:04:16 +02:00
|
|
|
static enum match_type
|
2016-07-27 03:35:46 +02:00
|
|
|
match_ipv6 (const char *);
|
2016-07-21 23:38:03 +02:00
|
|
|
|
2016-07-22 21:04:16 +02:00
|
|
|
static enum match_type
|
2016-07-27 03:35:46 +02:00
|
|
|
match_ipv6_prefix (const char *);
|
2016-07-21 23:38:03 +02:00
|
|
|
|
2016-07-22 21:04:16 +02:00
|
|
|
static enum match_type
|
2016-08-02 18:23:54 +02:00
|
|
|
match_range (struct graph_node *, const char *);
|
2016-07-21 23:38:03 +02:00
|
|
|
|
2016-07-22 21:04:16 +02:00
|
|
|
static enum match_type
|
2016-08-01 22:30:14 +02:00
|
|
|
match_word (struct graph_node *, const char *);
|
2016-07-21 23:38:03 +02:00
|
|
|
|
2016-07-22 21:04:16 +02:00
|
|
|
static enum match_type
|
2016-07-27 03:35:46 +02:00
|
|
|
match_number (struct graph_node *, const char *);
|
2016-07-21 23:38:03 +02:00
|
|
|
|
2016-07-27 03:35:46 +02:00
|
|
|
static enum match_type
|
|
|
|
match_variable (struct graph_node *, const char *);
|
2016-07-21 23:38:03 +02:00
|
|
|
|
2016-07-22 21:04:16 +02:00
|
|
|
/* matching functions */
|
2016-08-03 21:22:27 +02:00
|
|
|
static enum matcher_rv matcher_result_value;
|
2016-07-21 23:38:03 +02:00
|
|
|
|
2016-08-03 21:22:27 +02:00
|
|
|
enum matcher_rv
|
|
|
|
match_command (struct graph_node *start,
|
|
|
|
const char *line,
|
|
|
|
struct list **argvv,
|
|
|
|
struct cmd_element **el)
|
2016-07-26 16:02:36 +02:00
|
|
|
{
|
2016-08-03 21:22:27 +02:00
|
|
|
matcher_result_value = MATCHER_NO_MATCH;
|
2016-07-27 03:35:46 +02:00
|
|
|
// parse command
|
|
|
|
vector vline = cmd_make_strvec (line);
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < vector_active(start->children); i++)
|
|
|
|
{
|
2016-08-03 21:22:27 +02:00
|
|
|
// call recursive matcher on each starting child
|
|
|
|
*argvv = match_command_r(vector_slot(start->children, i), vline, 0);
|
|
|
|
if (*argvv) break;
|
2016-07-29 17:54:03 +02:00
|
|
|
}
|
|
|
|
|
2016-08-03 21:22:27 +02:00
|
|
|
if (*argvv) {
|
2016-07-29 17:54:03 +02:00
|
|
|
struct listnode *ln;
|
|
|
|
struct graph_node *gn;
|
2016-08-03 21:22:27 +02:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(*argvv,ln,gn))
|
|
|
|
if (gn->type == END_GN) {
|
|
|
|
*el = gn->element;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
assert(el);
|
2016-07-27 03:35:46 +02:00
|
|
|
}
|
|
|
|
|
2016-08-03 21:22:27 +02:00
|
|
|
return matcher_result_value;
|
2016-07-26 16:02:36 +02:00
|
|
|
}
|
|
|
|
|
2016-07-28 01:30:21 +02:00
|
|
|
/**
|
2016-08-03 21:22:27 +02:00
|
|
|
* Builds an argument list given a DFA and a matching input line.
|
2016-07-28 01:30:21 +02:00
|
|
|
*
|
2016-07-29 17:54:03 +02:00
|
|
|
* First the function determines if the node it is passed matches the first
|
|
|
|
* token of input. If it does not, it returns NULL. If it does match, then it
|
|
|
|
* saves the input token as the head of an argument list.
|
2016-07-28 01:30:21 +02:00
|
|
|
*
|
2016-07-29 17:54:03 +02:00
|
|
|
* The next step is to see if there is further input in the input line. If
|
|
|
|
* there is not, the current node's children are searched to see if any of them
|
|
|
|
* are leaves (type END_GN). If this is the case, then the bottom of the
|
|
|
|
* recursion stack has been reached, and the argument list (with one node) is
|
|
|
|
* returned. If it is not the case, NULL is returned, indicating that there is
|
|
|
|
* no match for the input along this path.
|
2016-07-28 01:30:21 +02:00
|
|
|
*
|
2016-07-29 17:54:03 +02:00
|
|
|
* If there is further input, then the function recurses on each of the current
|
|
|
|
* node's children, passing them the input line minus the token that was just
|
|
|
|
* matched. For each child, the return value of the recursive call is
|
|
|
|
* inspected. If it is null, then there is no match for the input along the
|
|
|
|
* subgraph headed by that child. If it is not null, then there is at least one
|
|
|
|
* input match in that subgraph (more on this in a moment).
|
2016-07-28 01:30:21 +02:00
|
|
|
*
|
|
|
|
* If a recursive call on a child returns a non-null value, then it has matched
|
|
|
|
* the input given it on the subgraph that starts with that child. However, due
|
|
|
|
* to the flexibility of the grammar, it is sometimes the case that two or more
|
|
|
|
* child graphs match the same input (two or more of the recursive calls have
|
2016-07-29 17:54:03 +02:00
|
|
|
* non-NULL return values). This is not a valid state, since only one true
|
|
|
|
* match is possible. In order to resolve this conflict, the function keeps a
|
|
|
|
* reference to the child node that most specifically matches the input. This
|
|
|
|
* is done by assigning each node type a precedence. If a child is found to
|
|
|
|
* match the remaining input, then the precedence values of the current
|
|
|
|
* best-matching child and this new match are compared. The node with higher
|
|
|
|
* precedence is kept, and the other match is discarded. Due to the recursive
|
|
|
|
* nature of this function, it is only necessary to compare the precedence of
|
|
|
|
* immediate children, since all subsequent children will already have been
|
|
|
|
* disambiguated in this way.
|
2016-07-28 01:30:21 +02:00
|
|
|
*
|
|
|
|
* In the event that two children are found to match with the same precedence,
|
2016-07-29 17:54:03 +02:00
|
|
|
* then the input is ambiguous for the passed cmd_element and NULL is returned.
|
2016-07-28 01:30:21 +02:00
|
|
|
*
|
|
|
|
* The ultimate return value is an ordered linked list of nodes that comprise
|
|
|
|
* the best match for the command, each with their `arg` fields pointing to the
|
|
|
|
* matching token string.
|
|
|
|
*
|
|
|
|
* @param[out] start the start node.
|
|
|
|
* @param[in] vline the vectorized input line.
|
|
|
|
* @param[in] n the index of the first input token. Should be 0 for external
|
|
|
|
* callers.
|
|
|
|
*/
|
2016-07-27 03:35:46 +02:00
|
|
|
static struct list *
|
2016-07-29 17:54:03 +02:00
|
|
|
match_command_r (struct graph_node *start, vector vline, unsigned int n)
|
2016-07-27 03:35:46 +02:00
|
|
|
{
|
2016-08-01 15:18:25 +02:00
|
|
|
// get the minimum match level that can count as a full match
|
|
|
|
enum match_type minmatch = min_match_level(start->type);
|
|
|
|
|
2016-08-01 22:30:14 +02:00
|
|
|
// get the current operating token
|
|
|
|
char *token = vector_slot(vline, n);
|
|
|
|
|
2016-07-27 03:35:46 +02:00
|
|
|
// if we don't match this node, die
|
2016-08-01 22:30:14 +02:00
|
|
|
if (match_token(start, token) < minmatch)
|
2016-07-27 03:35:46 +02:00
|
|
|
return NULL;
|
|
|
|
|
2016-07-28 01:30:21 +02:00
|
|
|
// pointers for iterating linklist
|
2016-07-27 03:35:46 +02:00
|
|
|
struct graph_node *gn;
|
|
|
|
struct listnode *ln;
|
|
|
|
|
|
|
|
// get all possible nexthops
|
|
|
|
struct list *next = list_new();
|
|
|
|
add_nexthops(next, start);
|
|
|
|
|
2016-08-02 18:23:54 +02:00
|
|
|
// determine the best match
|
2016-08-03 04:01:52 +02:00
|
|
|
int ambiguous = 0;
|
2016-08-02 18:23:54 +02:00
|
|
|
struct list *bestmatch = NULL;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(next,ln,gn))
|
|
|
|
{
|
|
|
|
// if we've matched all input we're looking for END_GN
|
|
|
|
if (n+1 == vector_active (vline)) {
|
2016-07-28 01:30:21 +02:00
|
|
|
if (gn->type == END_GN) {
|
2016-08-02 18:23:54 +02:00
|
|
|
bestmatch = list_new();
|
|
|
|
listnode_add(bestmatch, copy_node(gn));
|
2016-08-03 04:01:52 +02:00
|
|
|
bestmatch->del = &delete_nodelist;
|
2016-08-02 18:23:54 +02:00
|
|
|
break;
|
2016-07-28 01:30:21 +02:00
|
|
|
}
|
2016-08-02 18:23:54 +02:00
|
|
|
else continue;
|
2016-07-27 03:35:46 +02:00
|
|
|
}
|
|
|
|
|
2016-08-03 21:22:27 +02:00
|
|
|
// else recurse on candidate child node
|
2016-08-02 18:23:54 +02:00
|
|
|
struct list *result = match_command_r (gn, vline, n+1);
|
|
|
|
|
2016-08-03 04:01:52 +02:00
|
|
|
// save the best match, subtle logic at play here
|
2016-08-02 18:23:54 +02:00
|
|
|
if (result) {
|
|
|
|
if (bestmatch) {
|
2016-08-03 04:01:52 +02:00
|
|
|
struct list *to_delete = result;
|
|
|
|
struct graph_node *new = listgetdata(result->head),
|
|
|
|
*old = listgetdata(bestmatch->head);
|
|
|
|
char *nextoken = vector_slot (vline, n+1);
|
|
|
|
struct graph_node *best = disambiguate(new, old, nextoken);
|
|
|
|
ambiguous = !best || (ambiguous && best == old);
|
|
|
|
if (best == new) {
|
|
|
|
to_delete = bestmatch;
|
2016-07-28 01:30:21 +02:00
|
|
|
bestmatch = result;
|
2016-08-02 18:23:54 +02:00
|
|
|
}
|
2016-08-03 04:01:52 +02:00
|
|
|
list_delete (to_delete);
|
2016-07-28 01:30:21 +02:00
|
|
|
}
|
2016-08-02 18:23:54 +02:00
|
|
|
else
|
|
|
|
bestmatch = result;
|
2016-07-27 03:35:46 +02:00
|
|
|
}
|
2016-08-02 18:23:54 +02:00
|
|
|
}
|
2016-07-28 01:30:21 +02:00
|
|
|
|
|
|
|
if (bestmatch) {
|
2016-08-03 21:22:27 +02:00
|
|
|
if (ambiguous) {
|
|
|
|
list_delete(bestmatch);
|
|
|
|
bestmatch = NULL;
|
|
|
|
matcher_result_value = MATCHER_AMBIGUOUS;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// copy current node, set arg and prepend to bestmatch
|
|
|
|
struct graph_node *curr = copy_node(start);
|
|
|
|
curr->arg = XSTRDUP(MTYPE_CMD_TOKENS, token);
|
|
|
|
list_add_node_prev (bestmatch, bestmatch->head, curr);
|
|
|
|
matcher_result_value = MATCHER_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (n+1 == vector_active(vline) && matcher_result_value == MATCHER_NO_MATCH)
|
|
|
|
matcher_result_value = MATCHER_INCOMPLETE;
|
2016-07-28 01:30:21 +02:00
|
|
|
}
|
2016-08-02 18:23:54 +02:00
|
|
|
|
|
|
|
// cleanup
|
|
|
|
list_delete (next);
|
|
|
|
|
|
|
|
return bestmatch;
|
2016-07-29 17:54:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
struct list *
|
2016-08-01 22:30:14 +02:00
|
|
|
match_command_complete (struct graph_node *start, const char *line)
|
2016-07-29 17:54:03 +02:00
|
|
|
{
|
|
|
|
// vectorize command line
|
|
|
|
vector vline = cmd_make_strvec (line);
|
|
|
|
|
|
|
|
// pointer to next input token to match
|
|
|
|
char *token;
|
|
|
|
|
2016-08-02 18:23:54 +02:00
|
|
|
struct list *current = list_new(), // current nodes to match input token against
|
|
|
|
*next = list_new(); // possible next hops after current input token
|
2016-07-29 17:54:03 +02:00
|
|
|
|
|
|
|
// pointers used for iterating lists
|
|
|
|
struct graph_node *gn;
|
|
|
|
struct listnode *node;
|
|
|
|
|
|
|
|
// add all children of start node to list
|
|
|
|
add_nexthops(next, start);
|
|
|
|
|
|
|
|
unsigned int idx;
|
|
|
|
for (idx = 0; idx < vector_active(vline) && next->count > 0; idx++)
|
|
|
|
{
|
|
|
|
list_free (current);
|
|
|
|
current = next;
|
|
|
|
next = list_new();
|
|
|
|
|
|
|
|
token = vector_slot(vline, idx);
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(current,node,gn))
|
|
|
|
{
|
2016-08-01 22:30:14 +02:00
|
|
|
switch (match_token(gn, token)) {
|
2016-08-02 18:23:54 +02:00
|
|
|
case partly_match:
|
|
|
|
if (idx == vector_active(vline) - 1) {
|
|
|
|
listnode_add(next, gn);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case exact_match:
|
2016-08-01 22:30:14 +02:00
|
|
|
add_nexthops(next, gn);
|
|
|
|
break;
|
2016-08-02 18:23:54 +02:00
|
|
|
default:
|
2016-08-01 22:30:14 +02:00
|
|
|
break;
|
2016-07-29 17:54:03 +02:00
|
|
|
}
|
|
|
|
}
|
2016-07-27 03:35:46 +02:00
|
|
|
}
|
2016-07-29 17:54:03 +02:00
|
|
|
|
|
|
|
/* Variable summary
|
|
|
|
* -----------------------------------------------------------------
|
|
|
|
* token = last input token processed
|
|
|
|
* idx = index in `command` of last token processed
|
|
|
|
* current = set of all transitions from the previous input token
|
|
|
|
* next = set of all nodes reachable from all nodes in `matched`
|
|
|
|
*/
|
|
|
|
|
2016-08-03 21:22:27 +02:00
|
|
|
matcher_result_value =
|
|
|
|
idx + 1 == vector_active(vline) && next->count ?
|
|
|
|
MATCHER_OK :
|
|
|
|
MATCHER_NO_MATCH;
|
|
|
|
|
2016-08-03 21:49:02 +02:00
|
|
|
list_free (current);
|
|
|
|
cmd_free_strvec(vline);
|
|
|
|
|
2016-07-29 17:54:03 +02:00
|
|
|
return next;
|
2016-07-27 03:35:46 +02:00
|
|
|
}
|
2016-07-21 23:38:03 +02:00
|
|
|
|
2016-07-29 17:54:03 +02:00
|
|
|
/**
|
|
|
|
* Adds all children that are reachable by one parser hop
|
|
|
|
* to the given list. NUL_GN, SELECTOR_GN, and OPTION_GN
|
|
|
|
* nodes are treated as transparent.
|
|
|
|
*
|
|
|
|
* @param[out] l the list to add the children to
|
|
|
|
* @param[in] node the node to get the children of
|
|
|
|
* @return the number of children added to the list
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
add_nexthops(struct list *l, struct graph_node *node)
|
|
|
|
{
|
|
|
|
int added = 0;
|
|
|
|
struct graph_node *child;
|
|
|
|
for (unsigned int i = 0; i < vector_active(node->children); i++)
|
|
|
|
{
|
|
|
|
child = vector_slot(node->children, i);
|
|
|
|
switch (child->type) {
|
|
|
|
case OPTION_GN:
|
|
|
|
case SELECTOR_GN:
|
|
|
|
case NUL_GN:
|
|
|
|
added += add_nexthops(l, child);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
listnode_add(l, child);
|
|
|
|
added++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return added;
|
|
|
|
}
|
|
|
|
|
2016-08-01 15:18:25 +02:00
|
|
|
/**
|
2016-08-03 04:01:52 +02:00
|
|
|
* Determines the node types for which a partial match may count as a full
|
|
|
|
* match. Enables command abbrevations.
|
2016-08-01 15:18:25 +02:00
|
|
|
*/
|
|
|
|
static enum match_type
|
|
|
|
min_match_level(enum node_type type)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case WORD_GN:
|
|
|
|
return partly_match;
|
|
|
|
default:
|
|
|
|
return exact_match;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-03 04:01:52 +02:00
|
|
|
/* Precedence score used to disambiguate matches. */
|
2016-07-28 01:30:21 +02:00
|
|
|
static int
|
2016-08-02 18:23:54 +02:00
|
|
|
score_precedence (enum graph_node_type type)
|
2016-07-28 01:30:21 +02:00
|
|
|
{
|
2016-08-02 18:23:54 +02:00
|
|
|
switch (type)
|
2016-07-28 01:30:21 +02:00
|
|
|
{
|
2016-08-03 21:22:27 +02:00
|
|
|
// some of these are mutually exclusive, so they share
|
|
|
|
// the same precedence value
|
2016-07-28 01:30:21 +02:00
|
|
|
case IPV4_GN:
|
|
|
|
case IPV4_PREFIX_GN:
|
|
|
|
case IPV6_GN:
|
|
|
|
case IPV6_PREFIX_GN:
|
|
|
|
case NUMBER_GN:
|
2016-08-03 21:22:27 +02:00
|
|
|
return 1;
|
|
|
|
case RANGE_GN:
|
2016-07-28 01:30:21 +02:00
|
|
|
return 2;
|
2016-08-03 04:01:52 +02:00
|
|
|
case WORD_GN:
|
2016-07-28 01:30:21 +02:00
|
|
|
return 3;
|
2016-08-03 04:01:52 +02:00
|
|
|
case VARIABLE_GN:
|
|
|
|
return 4;
|
2016-07-28 01:30:21 +02:00
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-03 04:01:52 +02:00
|
|
|
/* Disambiguation logic to pick the best of two possible matches */
|
|
|
|
static struct graph_node *
|
|
|
|
disambiguate (struct graph_node *first, struct graph_node *second, char *token)
|
|
|
|
{
|
|
|
|
// if the types are different, simply go off of type precedence
|
|
|
|
if (first->type != second->type) {
|
|
|
|
int firstprec = score_precedence(first->type);
|
|
|
|
int secndprec = score_precedence(second->type);
|
|
|
|
if (firstprec != secndprec)
|
|
|
|
return firstprec < secndprec ? first : second;
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if they're the same, return the more exact match
|
|
|
|
enum match_type fmtype = match_token (first, token);
|
|
|
|
enum match_type smtype = match_token (second, token);
|
|
|
|
if (fmtype != smtype)
|
|
|
|
return fmtype > smtype ? first : second;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-08-02 18:23:54 +02:00
|
|
|
static struct graph_node *
|
|
|
|
copy_node (struct graph_node *node)
|
|
|
|
{
|
|
|
|
struct graph_node *new = new_node(node->type);
|
|
|
|
new->children = NULL;
|
|
|
|
new->is_start = node->is_start;
|
|
|
|
new->end = NULL;
|
|
|
|
new->text = node->text ? XSTRDUP(MTYPE_CMD_TOKENS, node->text) : NULL;
|
|
|
|
new->value = node->value;
|
|
|
|
new->min = node->min;
|
|
|
|
new->max = node->max;
|
|
|
|
new->element = node->element ? copy_cmd_element(node->element) : NULL;
|
|
|
|
new->arg = node->arg ? XSTRDUP(MTYPE_CMD_TOKENS, node->arg) : NULL;
|
|
|
|
new->refs = 0;
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
2016-08-03 04:01:52 +02:00
|
|
|
/* Linked list data deletion callback */
|
|
|
|
static void
|
|
|
|
delete_nodelist (void *node)
|
|
|
|
{
|
|
|
|
free_node ((struct graph_node *) node);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* token level matching functions */
|
|
|
|
|
2016-07-22 21:04:16 +02:00
|
|
|
static enum match_type
|
2016-08-01 22:30:14 +02:00
|
|
|
match_token (struct graph_node *node, char *token)
|
2016-07-22 21:04:16 +02:00
|
|
|
{
|
|
|
|
switch (node->type) {
|
|
|
|
case WORD_GN:
|
2016-08-01 22:30:14 +02:00
|
|
|
return match_word (node, token);
|
2016-07-22 21:04:16 +02:00
|
|
|
case IPV4_GN:
|
2016-07-27 03:35:46 +02:00
|
|
|
return match_ipv4 (token);
|
2016-07-22 21:04:16 +02:00
|
|
|
case IPV4_PREFIX_GN:
|
2016-08-01 15:18:25 +02:00
|
|
|
return match_ipv4_prefix (token);
|
2016-07-22 21:04:16 +02:00
|
|
|
case IPV6_GN:
|
2016-08-01 15:18:25 +02:00
|
|
|
return match_ipv6 (token);
|
2016-07-22 21:04:16 +02:00
|
|
|
case IPV6_PREFIX_GN:
|
2016-08-01 15:18:25 +02:00
|
|
|
return match_ipv6_prefix (token);
|
2016-07-22 21:04:16 +02:00
|
|
|
case RANGE_GN:
|
2016-07-27 03:35:46 +02:00
|
|
|
return match_range (node, token);
|
2016-07-22 21:04:16 +02:00
|
|
|
case NUMBER_GN:
|
2016-07-27 03:35:46 +02:00
|
|
|
return match_number (node, token);
|
2016-07-22 21:04:16 +02:00
|
|
|
case VARIABLE_GN:
|
2016-07-27 03:35:46 +02:00
|
|
|
return match_variable (node, token);
|
|
|
|
case END_GN:
|
2016-07-22 21:04:16 +02:00
|
|
|
default:
|
|
|
|
return no_match;
|
|
|
|
}
|
2016-07-19 23:14:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#define IPV4_ADDR_STR "0123456789."
|
|
|
|
#define IPV4_PREFIX_STR "0123456789./"
|
|
|
|
|
2016-07-22 21:04:16 +02:00
|
|
|
static enum match_type
|
2016-07-27 03:35:46 +02:00
|
|
|
match_ipv4 (const char *str)
|
2016-07-19 23:14:27 +02:00
|
|
|
{
|
2016-07-29 20:27:59 +02:00
|
|
|
const char *sp;
|
|
|
|
int dots = 0, nums = 0;
|
|
|
|
char buf[4];
|
2016-07-19 23:14:27 +02:00
|
|
|
|
|
|
|
if (str == NULL)
|
|
|
|
return partly_match;
|
|
|
|
|
2016-07-29 20:27:59 +02:00
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
memset (buf, 0, sizeof (buf));
|
|
|
|
sp = str;
|
|
|
|
while (*str != '\0')
|
2016-07-29 20:34:10 +02:00
|
|
|
{
|
|
|
|
if (*str == '.')
|
|
|
|
{
|
|
|
|
if (dots >= 3)
|
|
|
|
return no_match;
|
2016-07-19 23:14:27 +02:00
|
|
|
|
2016-07-29 20:34:10 +02:00
|
|
|
if (*(str + 1) == '.')
|
|
|
|
return no_match;
|
2016-07-29 20:27:59 +02:00
|
|
|
|
2016-07-29 20:34:10 +02:00
|
|
|
if (*(str + 1) == '\0')
|
|
|
|
return partly_match;
|
2016-07-29 20:27:59 +02:00
|
|
|
|
2016-07-29 20:34:10 +02:00
|
|
|
dots++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!isdigit ((int) *str))
|
|
|
|
return no_match;
|
2016-07-29 20:27:59 +02:00
|
|
|
|
2016-07-29 20:34:10 +02:00
|
|
|
str++;
|
|
|
|
}
|
2016-07-29 20:27:59 +02:00
|
|
|
|
|
|
|
if (str - sp > 3)
|
2016-07-29 20:34:10 +02:00
|
|
|
return no_match;
|
2016-07-29 20:27:59 +02:00
|
|
|
|
|
|
|
strncpy (buf, sp, str - sp);
|
|
|
|
if (atoi (buf) > 255)
|
2016-07-29 20:34:10 +02:00
|
|
|
return no_match;
|
2016-07-29 20:27:59 +02:00
|
|
|
|
|
|
|
nums++;
|
|
|
|
|
|
|
|
if (*str == '\0')
|
2016-07-29 20:34:10 +02:00
|
|
|
break;
|
2016-07-29 20:27:59 +02:00
|
|
|
|
|
|
|
str++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nums < 4)
|
|
|
|
return partly_match;
|
2016-07-19 23:14:27 +02:00
|
|
|
|
|
|
|
return exact_match;
|
|
|
|
}
|
|
|
|
|
2016-07-22 21:04:16 +02:00
|
|
|
static enum match_type
|
2016-07-27 03:35:46 +02:00
|
|
|
match_ipv4_prefix (const char *str)
|
2016-07-19 23:14:27 +02:00
|
|
|
{
|
2016-07-29 20:27:59 +02:00
|
|
|
const char *sp;
|
|
|
|
int dots = 0;
|
|
|
|
char buf[4];
|
2016-07-19 23:14:27 +02:00
|
|
|
|
|
|
|
if (str == NULL)
|
|
|
|
return partly_match;
|
|
|
|
|
2016-07-29 20:27:59 +02:00
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
memset (buf, 0, sizeof (buf));
|
|
|
|
sp = str;
|
|
|
|
while (*str != '\0' && *str != '/')
|
2016-07-29 20:34:10 +02:00
|
|
|
{
|
|
|
|
if (*str == '.')
|
|
|
|
{
|
|
|
|
if (dots == 3)
|
|
|
|
return no_match;
|
2016-07-29 20:27:59 +02:00
|
|
|
|
2016-07-29 20:34:10 +02:00
|
|
|
if (*(str + 1) == '.' || *(str + 1) == '/')
|
|
|
|
return no_match;
|
2016-07-29 20:27:59 +02:00
|
|
|
|
2016-07-29 20:34:10 +02:00
|
|
|
if (*(str + 1) == '\0')
|
|
|
|
return partly_match;
|
2016-07-29 20:27:59 +02:00
|
|
|
|
2016-07-29 20:34:10 +02:00
|
|
|
dots++;
|
|
|
|
break;
|
|
|
|
}
|
2016-07-29 20:27:59 +02:00
|
|
|
|
2016-07-29 20:34:10 +02:00
|
|
|
if (!isdigit ((int) *str))
|
|
|
|
return no_match;
|
2016-07-29 20:27:59 +02:00
|
|
|
|
2016-07-29 20:34:10 +02:00
|
|
|
str++;
|
|
|
|
}
|
2016-07-29 20:27:59 +02:00
|
|
|
|
|
|
|
if (str - sp > 3)
|
2016-07-29 20:34:10 +02:00
|
|
|
return no_match;
|
2016-07-29 20:27:59 +02:00
|
|
|
|
|
|
|
strncpy (buf, sp, str - sp);
|
|
|
|
if (atoi (buf) > 255)
|
2016-07-29 20:34:10 +02:00
|
|
|
return no_match;
|
2016-07-29 20:27:59 +02:00
|
|
|
|
|
|
|
if (dots == 3)
|
2016-07-29 20:34:10 +02:00
|
|
|
{
|
|
|
|
if (*str == '/')
|
|
|
|
{
|
|
|
|
if (*(str + 1) == '\0')
|
|
|
|
return partly_match;
|
|
|
|
|
|
|
|
str++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (*str == '\0')
|
|
|
|
return partly_match;
|
|
|
|
}
|
2016-07-29 20:27:59 +02:00
|
|
|
|
|
|
|
if (*str == '\0')
|
2016-07-29 20:34:10 +02:00
|
|
|
return partly_match;
|
2016-07-29 20:27:59 +02:00
|
|
|
|
|
|
|
str++;
|
|
|
|
}
|
2016-07-19 23:14:27 +02:00
|
|
|
|
2016-07-29 20:27:59 +02:00
|
|
|
sp = str;
|
|
|
|
while (*str != '\0')
|
|
|
|
{
|
|
|
|
if (!isdigit ((int) *str))
|
2016-07-29 20:34:10 +02:00
|
|
|
return no_match;
|
2016-07-19 23:14:27 +02:00
|
|
|
|
2016-07-29 20:27:59 +02:00
|
|
|
str++;
|
|
|
|
}
|
2016-07-19 23:14:27 +02:00
|
|
|
|
2016-07-29 20:27:59 +02:00
|
|
|
if (atoi (sp) > 32)
|
2016-07-19 23:14:27 +02:00
|
|
|
return no_match;
|
|
|
|
|
|
|
|
return exact_match;
|
|
|
|
}
|
|
|
|
|
2016-07-26 16:02:36 +02:00
|
|
|
#ifdef HAVE_IPV6
|
2016-07-19 23:14:27 +02:00
|
|
|
#define IPV6_ADDR_STR "0123456789abcdefABCDEF:."
|
|
|
|
#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:./"
|
|
|
|
|
2016-07-22 21:04:16 +02:00
|
|
|
static enum match_type
|
2016-07-27 03:35:46 +02:00
|
|
|
match_ipv6 (const char *str)
|
2016-07-19 23:14:27 +02:00
|
|
|
{
|
|
|
|
struct sockaddr_in6 sin6_dummy;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (str == NULL)
|
|
|
|
return partly_match;
|
|
|
|
|
|
|
|
if (strspn (str, IPV6_ADDR_STR) != strlen (str))
|
|
|
|
return no_match;
|
|
|
|
|
|
|
|
ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
|
|
|
|
|
|
|
|
if (ret == 1)
|
|
|
|
return exact_match;
|
|
|
|
|
|
|
|
return no_match;
|
|
|
|
}
|
|
|
|
|
2016-07-22 21:04:16 +02:00
|
|
|
static enum match_type
|
2016-07-27 03:35:46 +02:00
|
|
|
match_ipv6_prefix (const char *str)
|
2016-07-19 23:14:27 +02:00
|
|
|
{
|
|
|
|
struct sockaddr_in6 sin6_dummy;
|
|
|
|
const char *delim = "/\0";
|
|
|
|
char *dupe, *prefix, *mask, *context, *endptr;
|
|
|
|
int nmask = -1;
|
|
|
|
|
|
|
|
if (str == NULL)
|
|
|
|
return partly_match;
|
|
|
|
|
|
|
|
if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
|
|
|
|
return no_match;
|
|
|
|
|
|
|
|
/* tokenize to address + mask */
|
|
|
|
dupe = XMALLOC(MTYPE_TMP, strlen(str)+1);
|
|
|
|
strncpy(dupe, str, strlen(str)+1);
|
|
|
|
prefix = strtok_r(dupe, delim, &context);
|
|
|
|
mask = strtok_r(NULL, delim, &context);
|
|
|
|
|
|
|
|
if (!mask)
|
|
|
|
return partly_match;
|
|
|
|
|
|
|
|
/* validate prefix */
|
|
|
|
if (inet_pton(AF_INET6, prefix, &sin6_dummy.sin6_addr) != 1)
|
|
|
|
return no_match;
|
|
|
|
|
|
|
|
/* validate mask */
|
2016-08-04 18:18:31 +02:00
|
|
|
nmask = strtoimax (mask, &endptr, 10);
|
2016-07-19 23:14:27 +02:00
|
|
|
if (*endptr != '\0' || nmask < 0 || nmask > 128)
|
|
|
|
return no_match;
|
|
|
|
|
|
|
|
XFREE(MTYPE_TMP, dupe);
|
|
|
|
|
|
|
|
return exact_match;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-07-22 21:04:16 +02:00
|
|
|
static enum match_type
|
2016-07-27 03:35:46 +02:00
|
|
|
match_range (struct graph_node *rangenode, const char *str)
|
2016-07-19 23:14:27 +02:00
|
|
|
{
|
|
|
|
char *endptr = NULL;
|
2016-08-04 18:18:31 +02:00
|
|
|
signed int val;
|
2016-07-19 23:14:27 +02:00
|
|
|
|
|
|
|
if (str == NULL)
|
|
|
|
return 1;
|
|
|
|
|
2016-08-04 18:18:31 +02:00
|
|
|
val = strtoimax (str, &endptr, 10);
|
2016-07-19 23:14:27 +02:00
|
|
|
if (*endptr != '\0')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (val < rangenode->min || val > rangenode->max)
|
|
|
|
return no_match;
|
|
|
|
else
|
|
|
|
return exact_match;
|
|
|
|
}
|
|
|
|
|
2016-07-22 21:04:16 +02:00
|
|
|
static enum match_type
|
2016-08-01 22:30:14 +02:00
|
|
|
match_word(struct graph_node *wordnode, const char *word)
|
2016-07-19 23:14:27 +02:00
|
|
|
{
|
2016-08-01 22:30:14 +02:00
|
|
|
// if the passed token is null or 0 length, partly match
|
|
|
|
if (!word || !strlen(word))
|
|
|
|
return partly_match;
|
|
|
|
|
|
|
|
// if the passed token is strictly a prefix of the full word, partly match
|
|
|
|
if (strlen(word) < strlen(wordnode->text))
|
|
|
|
return !strncmp(wordnode->text, word, strlen(word)) ? partly_match : no_match;
|
|
|
|
|
|
|
|
// if they are the same length and exactly equal, exact match
|
|
|
|
else if (strlen(word) == strlen(wordnode->text))
|
|
|
|
return !strncmp(wordnode->text, word, strlen(word)) ? exact_match : no_match;
|
|
|
|
|
|
|
|
return no_match;
|
2016-07-26 16:02:36 +02:00
|
|
|
}
|
2016-07-19 23:14:27 +02:00
|
|
|
|
2016-07-27 03:35:46 +02:00
|
|
|
static enum match_type
|
|
|
|
match_number(struct graph_node *numnode, const char *word)
|
2016-07-26 16:02:36 +02:00
|
|
|
{
|
|
|
|
if (!strcmp("\0", word)) return no_match;
|
|
|
|
char *endptr;
|
2016-08-04 18:18:31 +02:00
|
|
|
int num = strtoimax(word, &endptr, 10);
|
2016-07-26 16:02:36 +02:00
|
|
|
if (endptr != '\0') return no_match;
|
|
|
|
return num == numnode->value ? exact_match : no_match;
|
|
|
|
}
|
|
|
|
|
2016-08-01 20:36:30 +02:00
|
|
|
#define VARIABLE_ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890:"
|
2016-07-26 16:02:36 +02:00
|
|
|
|
2016-07-27 03:35:46 +02:00
|
|
|
static enum match_type
|
|
|
|
match_variable(struct graph_node *varnode, const char *word)
|
2016-07-26 16:02:36 +02:00
|
|
|
{
|
2016-08-01 20:36:30 +02:00
|
|
|
return strlen(word) == strspn(word, VARIABLE_ALPHABET) ?
|
2016-07-27 03:35:46 +02:00
|
|
|
exact_match : no_match;
|
2016-07-19 23:14:27 +02:00
|
|
|
}
|