1/*- 2 * Copyright (c) 2011-2012 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by Christos Zoulas. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30/* 31 * NPF variables are used to build the intermediate representation (IR) 32 * of the configuration grammar. They represent primitive types (strings, 33 * numbers, etc) as well as complex types (address and mask, table, etc). 34 */ 35 36#include <sys/cdefs.h> 37__RCSID("$NetBSD: npf_var.c,v 1.13 2020/05/30 14:16:56 rmind Exp $"); 38 39#include <stdlib.h> 40#include <string.h> 41#include <unistd.h> 42 43#define _NPFVAR_PRIVATE 44#include "npfctl.h" 45 46typedef struct npf_element { 47 void * e_data; 48 unsigned e_type; 49 struct npf_element *e_next; 50} npf_element_t; 51 52struct npfvar { 53 char * v_key; 54 npf_element_t * v_elements; 55 npf_element_t * v_last; 56 size_t v_count; 57 void * v_next; 58}; 59 60static npfvar_t * var_list = NULL; 61static size_t var_num = 0; 62 63npfvar_t * 64npfvar_create(void) 65{ 66 npfvar_t *vp = ecalloc(1, sizeof(*vp)); 67 var_num++; 68 return vp; 69} 70 71npfvar_t * 72npfvar_lookup(const char *key) 73{ 74 for (npfvar_t *it = var_list; it != NULL; it = it->v_next) 75 if (strcmp(it->v_key, key) == 0) 76 return it; 77 return NULL; 78} 79 80const char * 81npfvar_type(size_t t) 82{ 83 if (t >= __arraycount(npfvar_types)) { 84 return "unknown"; 85 } 86 return npfvar_types[t]; 87} 88 89void 90npfvar_add(npfvar_t *vp, const char *name) 91{ 92 vp->v_key = estrdup(name); 93 vp->v_next = var_list; 94 var_list = vp; 95} 96 97npfvar_t * 98npfvar_create_element(unsigned type, const void *data, size_t len) 99{ 100 npfvar_t *vp = npfvar_create(); 101 return npfvar_add_element(vp, type, data, len); 102} 103 104npfvar_t * 105npfvar_create_from_string(unsigned type, const char *string) 106{ 107 return npfvar_create_element(type, string, strlen(string) + 1); 108} 109 110npfvar_t * 111npfvar_add_element(npfvar_t *vp, unsigned type, const void *data, size_t len) 112{ 113 npf_element_t *el; 114 115 el = ecalloc(1, sizeof(*el)); 116 el->e_data = ecalloc(1, len); 117 el->e_type = type; 118 memcpy(el->e_data, data, len); 119 120 /* Preserve the order of insertion. */ 121 if (vp->v_elements == NULL) { 122 vp->v_elements = el; 123 } else { 124 vp->v_last->e_next = el; 125 } 126 vp->v_last = el; 127 vp->v_count++; 128 return vp; 129} 130 131npfvar_t * 132npfvar_add_elements(npfvar_t *vp, npfvar_t *vp2) 133{ 134 if (vp2 == NULL) 135 return vp; 136 if (vp == NULL) 137 return vp2; 138 139 if (vp->v_elements == NULL) { 140 if (vp2->v_elements) { 141 vp->v_elements = vp2->v_elements; 142 } 143 } else if (vp2->v_elements) { 144 vp->v_last->e_next = vp2->v_elements; 145 } 146 if (vp2->v_elements) { 147 vp->v_last = vp2->v_last; 148 vp->v_count += vp2->v_count; 149 vp2->v_elements = NULL; 150 vp2->v_count = 0; 151 vp2->v_last = NULL; 152 } 153 npfvar_destroy(vp2); 154 return vp; 155} 156 157static void 158npfvar_free_elements(npf_element_t *el) 159{ 160 if (el == NULL) 161 return; 162 npfvar_free_elements(el->e_next); 163 free(el->e_data); 164 free(el); 165} 166 167void 168npfvar_destroy(npfvar_t *vp) 169{ 170 npfvar_free_elements(vp->v_elements); 171 free(vp->v_key); 172 free(vp); 173 var_num--; 174} 175 176char * 177npfvar_expand_string(const npfvar_t *vp) 178{ 179 if (npfvar_get_count(vp) != 1) { 180 yyerror("variable '%s' has multiple elements", vp->v_key); 181 return NULL; 182 } 183 return npfvar_get_data(vp, NPFVAR_STRING, 0); 184} 185 186size_t 187npfvar_get_count(const npfvar_t *vp) 188{ 189 return vp ? vp->v_count : 0; 190} 191 192static npf_element_t * 193npfvar_get_element(const npfvar_t *vp, size_t idx, size_t level) 194{ 195 npf_element_t *el; 196 197 /* 198 * Verify the parameters. 199 */ 200 if (vp == NULL) { 201 return NULL; 202 } 203 if (level >= var_num) { 204 yyerror("circular dependency for variable '%s'", vp->v_key); 205 return NULL; 206 } 207 if (vp->v_count <= idx) { 208 yyerror("variable '%s' has only %zu elements, requested %zu", 209 vp->v_key, vp->v_count, idx); 210 return NULL; 211 } 212 213 /* 214 * Get the element at the given index. 215 */ 216 el = vp->v_elements; 217 while (idx--) { 218 el = el->e_next; 219 } 220 221 /* 222 * Resolve if it is a reference to another variable. 223 */ 224 if (el->e_type == NPFVAR_VAR_ID) { 225 const npfvar_t *rvp = npfvar_lookup(el->e_data); 226 return npfvar_get_element(rvp, 0, level + 1); 227 } 228 return el; 229} 230 231int 232npfvar_get_type(const npfvar_t *vp, size_t idx) 233{ 234 npf_element_t *el = npfvar_get_element(vp, idx, 0); 235 return el ? (int)el->e_type : -1; 236} 237 238void * 239npfvar_get_data(const npfvar_t *vp, unsigned type, size_t idx) 240{ 241 npf_element_t *el = npfvar_get_element(vp, idx, 0); 242 243 if (el && NPFVAR_TYPE(el->e_type) != NPFVAR_TYPE(type)) { 244 yyerror("variable '%s' element %zu " 245 "is of type '%s' rather than '%s'", vp->v_key, 246 idx, npfvar_type(el->e_type), npfvar_type(type)); 247 return NULL; 248 } 249 return el->e_data; 250} 251