1 /* $NetBSD: dict_pipe.c,v 1.3 2026/05/09 18:49:22 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* dict_pipe 3 6 /* SUMMARY 7 /* dictionary manager interface for pipelined tables 8 /* SYNOPSIS 9 /* #include <dict_pipe.h> 10 /* 11 /* DICT *dict_pipe_open(name, open_flags, dict_flags) 12 /* const char *name; 13 /* int open_flags; 14 /* int dict_flags; 15 /* DESCRIPTION 16 /* dict_pipe_open() opens a pipeline of one or more tables. 17 /* Example: "\fBpipemap:{\fItype_1:name_1, ..., type_n:name_n\fR}". 18 /* 19 /* Each "pipemap:" query is given to the first table. Each 20 /* lookup result becomes the query for the next table in the 21 /* pipeline, and the last table produces the final result. 22 /* When any table lookup produces no result, the pipeline 23 /* produces no result. 24 /* 25 /* The first and last characters of the "pipemap:" table name 26 /* must be '{' and '}'. Within these, individual maps are 27 /* separated with comma or whitespace. 28 /* 29 /* The open_flags and dict_flags arguments are passed on to 30 /* the underlying dictionaries. 31 /* SEE ALSO 32 /* dict(3) generic dictionary manager 33 /* LICENSE 34 /* .ad 35 /* .fi 36 /* The Secure Mailer license must be distributed with this software. 37 /* AUTHOR(S) 38 /* Wietse Venema 39 /* IBM T.J. Watson Research 40 /* P.O. Box 704 41 /* Yorktown Heights, NY 10598, USA 42 /*--*/ 43 44 /* System library. */ 45 46 #include <sys_defs.h> 47 #include <string.h> 48 49 /* Utility library. */ 50 51 #include <msg.h> 52 #include "mymalloc.h" 53 #include "htable.h" 54 #include "dict.h" 55 #include "dict_pipe.h" 56 #include "stringops.h" 57 #include "vstring.h" 58 59 /* Application-specific. */ 60 61 typedef struct { 62 DICT dict; /* generic members */ 63 ARGV *map_pipe; /* pipelined tables */ 64 VSTRING *qr_buf; /* query/reply buffer */ 65 } DICT_PIPE; 66 67 #define STR(x) vstring_str(x) 68 69 /* dict_pipe_lookup - search pipelined tables */ 70 71 static const char *dict_pipe_lookup(DICT *dict, const char *query) 72 { 73 static const char myname[] = "dict_pipe_lookup"; 74 DICT_PIPE *dict_pipe = (DICT_PIPE *) dict; 75 DICT *map; 76 char **cpp; 77 char *reg_name; 78 const char *result = 0; 79 80 vstring_strcpy(dict_pipe->qr_buf, query); 81 for (cpp = dict_pipe->map_pipe->argv; (reg_name = *cpp) != 0; cpp++) { 82 if ((map = dict_handle(reg_name)) == 0) 83 msg_panic("%s: dictionary \"%s\" not found", myname, reg_name); 84 if ((result = dict_get(map, STR(dict_pipe->qr_buf))) == 0) 85 DICT_ERR_VAL_RETURN(dict, map->error, result); 86 vstring_strcpy(dict_pipe->qr_buf, result); 87 } 88 DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, STR(dict_pipe->qr_buf)); 89 } 90 91 /* dict_pipe_close - disassociate from pipelined tables */ 92 93 static void dict_pipe_close(DICT *dict) 94 { 95 static const char myname[] = "dict_pipe_close"; 96 DICT_PIPE *dict_pipe = (DICT_PIPE *) dict; 97 DICT *map; 98 char **cpp; 99 char *reg_name; 100 101 for (cpp = dict_pipe->map_pipe->argv; (reg_name = *cpp) != 0; cpp++) { 102 if ((map = dict_handle(reg_name)) == 0) 103 msg_panic("%s: dictionary \"%s\" not found", myname, reg_name); 104 dict_close(map); 105 } 106 argv_free(dict_pipe->map_pipe); 107 vstring_free(dict_pipe->qr_buf); 108 dict_free(dict); 109 } 110 111 /* dict_pipe_open - open pipelined tables */ 112 113 DICT *dict_pipe_open(const char *name, int open_flags, int dict_flags) 114 { 115 static const char myname[] = "dict_pipe_open"; 116 DICT_PIPE *dict_pipe; 117 char *saved_name = 0; 118 char *dict_type_name; 119 ARGV *argv = 0; 120 char **cpp; 121 DICT *dict; 122 int match_flags = 0; 123 struct DICT_OWNER aggr_owner; 124 size_t len; 125 126 /* 127 * Clarity first. Let the optimizer worry about redundant code. 128 */ 129 #define DICT_PIPE_RETURN(x) do { \ 130 if (saved_name != 0) \ 131 myfree(saved_name); \ 132 if (argv != 0) \ 133 argv_free(argv); \ 134 return (x); \ 135 } while (0) 136 137 /* 138 * Sanity checks. 139 */ 140 if (open_flags != O_RDONLY) 141 DICT_PIPE_RETURN(dict_surrogate(DICT_TYPE_PIPE, name, 142 open_flags, dict_flags, 143 "%s:%s map requires O_RDONLY access mode", 144 DICT_TYPE_PIPE, name)); 145 146 /* 147 * Split the table name into its constituent parts. 148 */ 149 if ((len = balpar(name, CHARS_BRACE)) == 0 || name[len] != 0 150 || *(saved_name = mystrndup(name + 1, len - 2)) == 0 151 || ((argv = argv_splitq(saved_name, CHARS_COMMA_SP, CHARS_BRACE)), 152 (argv->argc == 0))) 153 DICT_PIPE_RETURN(dict_surrogate(DICT_TYPE_PIPE, name, 154 open_flags, dict_flags, 155 "bad syntax: \"%s:%s\"; " 156 "need \"%s:{type:name...}\"", 157 DICT_TYPE_PIPE, name, 158 DICT_TYPE_PIPE)); 159 160 /* 161 * Check all underlying table specs before registering any of them to 162 * avoid leaking refcounts if one of them is bad. 163 */ 164 for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) { 165 if (strchr(dict_type_name, ':') == 0) 166 DICT_PIPE_RETURN(dict_surrogate(DICT_TYPE_PIPE, name, 167 open_flags, dict_flags, 168 "bad syntax: \"%s:%s\"; " 169 "need \"%s:{type:name...}\"", 170 DICT_TYPE_PIPE, name, 171 DICT_TYPE_PIPE)); 172 } 173 174 /* 175 * The least-trusted table in the pipeline determines the over-all trust 176 * level. The first table determines the pattern-matching flags. 177 */ 178 DICT_OWNER_AGGREGATE_INIT(aggr_owner); 179 for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) { 180 if (msg_verbose) 181 msg_info("%s: %s", myname, dict_type_name); 182 dict = dict_open(dict_type_name, open_flags, dict_flags); 183 argv_replace_one(argv, cpp - argv->argv, dict->reg_name); 184 DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner); 185 if (cpp == argv->argv) 186 match_flags = dict->flags & (DICT_FLAG_FIXED | DICT_FLAG_PATTERN); 187 } 188 189 /* 190 * Bundle up the result. 191 */ 192 dict_pipe = 193 (DICT_PIPE *) dict_alloc(DICT_TYPE_PIPE, name, sizeof(*dict_pipe)); 194 dict_pipe->dict.lookup = dict_pipe_lookup; 195 dict_pipe->dict.close = dict_pipe_close; 196 dict_pipe->dict.flags = dict_flags | match_flags; 197 dict_pipe->dict.owner = aggr_owner; 198 dict_pipe->qr_buf = vstring_alloc(100); 199 dict_pipe->map_pipe = argv; 200 argv = 0; 201 DICT_PIPE_RETURN(&dict_pipe->dict); 202 } 203