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 #include <sys/cdefs.h> 31 __RCSID("$NetBSD: npf_var.c,v 1.12 2019/01/19 21:19:32 rmind Exp $"); 32 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 37 #define _NPFVAR_PRIVATE 38 #include "npfctl.h" 39 40 typedef struct npf_element { 41 void * e_data; 42 int e_type; 43 struct npf_element *e_next; 44 } npf_element_t; 45 46 struct npfvar { 47 char * v_key; 48 npf_element_t * v_elements; 49 npf_element_t * v_last; 50 int v_type; 51 size_t v_count; 52 void * v_next; 53 }; 54 55 static npfvar_t * var_list = NULL; 56 static size_t var_num = 0; 57 58 npfvar_t * 59 npfvar_create(void) 60 { 61 npfvar_t *vp = ecalloc(1, sizeof(*vp)); 62 var_num++; 63 return vp; 64 } 65 66 npfvar_t * 67 npfvar_lookup(const char *key) 68 { 69 for (npfvar_t *it = var_list; it != NULL; it = it->v_next) 70 if (strcmp(it->v_key, key) == 0) 71 return it; 72 return NULL; 73 } 74 75 const char * 76 npfvar_type(size_t t) 77 { 78 if (t >= __arraycount(npfvar_types)) { 79 return "unknown"; 80 } 81 return npfvar_types[t]; 82 } 83 84 void 85 npfvar_add(npfvar_t *vp, const char *name) 86 { 87 vp->v_key = estrdup(name); 88 vp->v_next = var_list; 89 var_list = vp; 90 } 91 92 npfvar_t * 93 npfvar_create_element(int type, const void *data, size_t len) 94 { 95 npfvar_t *vp = npfvar_create(); 96 return npfvar_add_element(vp, type, data, len); 97 } 98 99 npfvar_t * 100 npfvar_create_from_string(int type, const char *string) 101 { 102 return npfvar_create_element(type, string, strlen(string) + 1); 103 } 104 105 npfvar_t * 106 npfvar_add_element(npfvar_t *vp, int type, const void *data, size_t len) 107 { 108 npf_element_t *el; 109 110 if (vp->v_count == 0) { 111 vp->v_type = type; 112 } else if (NPFVAR_TYPE(vp->v_type) != NPFVAR_TYPE(type)) { 113 yyerror("element type '%s' does not match variable type '%s'", 114 npfvar_type(type), npfvar_type(vp->v_type)); 115 return NULL; 116 } 117 vp->v_count++; 118 el = ecalloc(1, sizeof(*el)); 119 el->e_data = ecalloc(1, len); 120 el->e_type = type; 121 memcpy(el->e_data, data, len); 122 123 /* Preserve order of insertion. */ 124 if (vp->v_elements == NULL) { 125 vp->v_elements = el; 126 } else { 127 vp->v_last->e_next = el; 128 } 129 vp->v_last = el; 130 return vp; 131 } 132 133 npfvar_t * 134 npfvar_add_elements(npfvar_t *vp, npfvar_t *vp2) 135 { 136 if (vp2 == NULL) 137 return vp; 138 if (vp == NULL) 139 return vp2; 140 141 if (vp->v_elements == NULL) { 142 if (vp2->v_elements) { 143 vp->v_type = vp2->v_type; 144 vp->v_elements = vp2->v_elements; 145 } 146 } else if (vp2->v_elements) { 147 if (NPFVAR_TYPE(vp->v_type) != NPFVAR_TYPE(vp2->v_type)) { 148 yyerror("variable '%s' type '%s' does not match " 149 "variable '%s' type '%s'", vp->v_key, 150 npfvar_type(vp->v_type), 151 vp2->v_key, npfvar_type(vp2->v_type)); 152 return NULL; 153 } 154 vp->v_last->e_next = vp2->v_elements; 155 } 156 if (vp2->v_elements) { 157 vp->v_last = vp2->v_last; 158 vp->v_count += vp2->v_count; 159 vp2->v_elements = NULL; 160 vp2->v_count = 0; 161 vp2->v_last = NULL; 162 } 163 npfvar_destroy(vp2); 164 return vp; 165 } 166 167 static void 168 npfvar_free_elements(npf_element_t *el) 169 { 170 if (el == NULL) 171 return; 172 npfvar_free_elements(el->e_next); 173 free(el->e_data); 174 free(el); 175 } 176 177 void 178 npfvar_destroy(npfvar_t *vp) 179 { 180 npfvar_free_elements(vp->v_elements); 181 free(vp->v_key); 182 free(vp); 183 var_num--; 184 } 185 186 char * 187 npfvar_expand_string(const npfvar_t *vp) 188 { 189 if (npfvar_get_count(vp) != 1) { 190 yyerror("variable '%s' type '%s' has %zu elements", vp->v_key, 191 npfvar_type(vp->v_type), npfvar_get_count(vp)); 192 } 193 return npfvar_get_data(vp, NPFVAR_STRING, 0); 194 } 195 196 size_t 197 npfvar_get_count(const npfvar_t *vp) 198 { 199 return vp ? vp->v_count : 0; 200 } 201 202 static void * 203 npfvar_get_data1(const npfvar_t *vp, int type, size_t idx, size_t level) 204 { 205 npf_element_t *el; 206 207 if (level >= var_num) { 208 yyerror("variable loop for '%s'", vp->v_key); 209 return NULL; 210 } 211 212 if (vp == NULL) 213 return NULL; 214 215 if (NPFVAR_TYPE(vp->v_type) != NPFVAR_TYPE(type)) { 216 yyerror("variable '%s' is of type '%s' not '%s'", vp->v_key, 217 npfvar_type(vp->v_type), npfvar_type(type)); 218 return NULL; 219 } 220 221 if (vp->v_count <= idx) { 222 yyerror("variable '%s' has only %zu elements, requested %zu", 223 vp->v_key, vp->v_count, idx); 224 return NULL; 225 } 226 227 el = vp->v_elements; 228 while (idx--) { 229 el = el->e_next; 230 } 231 232 if (vp->v_type == NPFVAR_VAR_ID) { 233 npfvar_t *rvp = npfvar_lookup(el->e_data); 234 return npfvar_get_data1(rvp, type, 0, level + 1); 235 } 236 return el->e_data; 237 } 238 239 static int 240 npfvar_get_type1(const npfvar_t *vp, size_t idx, size_t level) 241 { 242 npf_element_t *el; 243 244 if (vp == NULL) 245 return -1; 246 247 if (level >= var_num) { 248 yyerror("variable loop for '%s'", vp->v_key); 249 return -1; 250 } 251 252 if (vp->v_count <= idx) { 253 yyerror("variable '%s' has only %zu elements, requested %zu", 254 vp->v_key, vp->v_count, idx); 255 return -1; 256 } 257 258 el = vp->v_elements; 259 while (idx--) { 260 el = el->e_next; 261 } 262 263 if (vp->v_type == NPFVAR_VAR_ID) { 264 npfvar_t *rvp = npfvar_lookup(el->e_data); 265 return npfvar_get_type1(rvp, 0, level + 1); 266 } 267 return el->e_type; 268 } 269 270 int 271 npfvar_get_type(const npfvar_t *vp, size_t idx) 272 { 273 return npfvar_get_type1(vp, idx, 0); 274 } 275 276 void * 277 npfvar_get_data(const npfvar_t *vp, int type, size_t idx) 278 { 279 return npfvar_get_data1(vp, type, idx, 0); 280 } 281