xref: /freebsd/contrib/unbound/util/module.c (revision 389e4940069316fe667ffa263fa7d6390d0a960f)
1 /*
2  * util/module.c - module interface
3  *
4  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5  *
6  * This software is open source.
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  *
12  * Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * Neither the name of the NLNET LABS nor the names of its contributors may
20  * be used to endorse or promote products derived from this software without
21  * specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 /**
36  * \file
37  * Implementation of module.h.
38  */
39 
40 #include "config.h"
41 #include "util/module.h"
42 #include "sldns/wire2str.h"
43 
44 const char*
45 strextstate(enum module_ext_state s)
46 {
47 	switch(s) {
48 	case module_state_initial: return "module_state_initial";
49 	case module_wait_reply: return "module_wait_reply";
50 	case module_wait_module: return "module_wait_module";
51 	case module_restart_next: return "module_restart_next";
52 	case module_wait_subquery: return "module_wait_subquery";
53 	case module_error: return "module_error";
54 	case module_finished: return "module_finished";
55 	}
56 	return "bad_extstate_value";
57 }
58 
59 const char*
60 strmodulevent(enum module_ev e)
61 {
62 	switch(e) {
63 	case module_event_new: return "module_event_new";
64 	case module_event_pass: return "module_event_pass";
65 	case module_event_reply: return "module_event_reply";
66 	case module_event_noreply: return "module_event_noreply";
67 	case module_event_capsfail: return "module_event_capsfail";
68 	case module_event_moddone: return "module_event_moddone";
69 	case module_event_error: return "module_event_error";
70 	}
71 	return "bad_event_value";
72 }
73 
74 int
75 edns_known_options_init(struct module_env* env)
76 {
77 	env->edns_known_options_num = 0;
78 	env->edns_known_options = (struct edns_known_option*)calloc(
79 		MAX_KNOWN_EDNS_OPTS, sizeof(struct edns_known_option));
80 	if(!env->edns_known_options) return 0;
81 	return 1;
82 }
83 
84 void
85 edns_known_options_delete(struct module_env* env)
86 {
87 	free(env->edns_known_options);
88 	env->edns_known_options = NULL;
89 	env->edns_known_options_num = 0;
90 }
91 
92 int
93 edns_register_option(uint16_t opt_code, int bypass_cache_stage,
94 	int no_aggregation, struct module_env* env)
95 {
96 	size_t i;
97 	if(env->worker) {
98 		log_err("invalid edns registration: "
99 			"trying to register option after module init phase");
100 		return 0;
101 	}
102 
103 	/**
104 	 * Checking if we are full first is faster but it does not provide
105 	 * the option to change the flags when the array is full.
106 	 * It only impacts unbound initialization, leave it for now.
107 	 */
108 	/* Check if the option is already registered. */
109 	for(i=0; i<env->edns_known_options_num; i++)
110 		if(env->edns_known_options[i].opt_code == opt_code)
111 			break;
112 	/* If it is not yet registered check if we have space to add a new one. */
113 	if(i == env->edns_known_options_num) {
114 		if(env->edns_known_options_num >= MAX_KNOWN_EDNS_OPTS) {
115 			log_err("invalid edns registration: maximum options reached");
116 			return 0;
117 		}
118 		env->edns_known_options_num++;
119 	}
120 	env->edns_known_options[i].opt_code = opt_code;
121 	env->edns_known_options[i].bypass_cache_stage = bypass_cache_stage;
122 	env->edns_known_options[i].no_aggregation = no_aggregation;
123 	return 1;
124 }
125 
126 int
127 inplace_cb_register(void* cb, enum inplace_cb_list_type type, void* cbarg,
128 	struct module_env* env, int id)
129 {
130 	struct inplace_cb* callback;
131 	struct inplace_cb** prevp;
132 	if(env->worker) {
133 		log_err("invalid edns callback registration: "
134 			"trying to register callback after module init phase");
135 		return 0;
136 	}
137 
138 	callback = (struct inplace_cb*)calloc(1, sizeof(*callback));
139 	if(callback == NULL) {
140 		log_err("out of memory during edns callback registration.");
141 		return 0;
142 	}
143 	callback->id = id;
144 	callback->next = NULL;
145 	callback->cb = cb;
146 	callback->cb_arg = cbarg;
147 
148 	prevp = (struct inplace_cb**) &env->inplace_cb_lists[type];
149 	/* append at end of list */
150 	while(*prevp != NULL)
151 		prevp = &((*prevp)->next);
152 	*prevp = callback;
153 	return 1;
154 }
155 
156 void
157 inplace_cb_delete(struct module_env* env, enum inplace_cb_list_type type,
158 	int id)
159 {
160 	struct inplace_cb* temp = env->inplace_cb_lists[type];
161 	struct inplace_cb* prev = NULL;
162 
163 	while(temp) {
164 		if(temp->id == id) {
165 			if(!prev) {
166 				env->inplace_cb_lists[type] = temp->next;
167 				free(temp);
168 				temp = env->inplace_cb_lists[type];
169 			}
170 			else {
171 				prev->next = temp->next;
172 				free(temp);
173 				temp = prev->next;
174 			}
175 		}
176 		else {
177 			prev = temp;
178 			temp = temp->next;
179 		}
180 	}
181 }
182 
183 struct edns_known_option*
184 edns_option_is_known(uint16_t opt_code, struct module_env* env)
185 {
186 	size_t i;
187 	for(i=0; i<env->edns_known_options_num; i++)
188 		if(env->edns_known_options[i].opt_code == opt_code)
189 			return env->edns_known_options + i;
190 	return NULL;
191 }
192 
193 int
194 edns_bypass_cache_stage(struct edns_option* list, struct module_env* env)
195 {
196 	size_t i;
197 	for(; list; list=list->next)
198 		for(i=0; i<env->edns_known_options_num; i++)
199 			if(env->edns_known_options[i].opt_code == list->opt_code &&
200 				env->edns_known_options[i].bypass_cache_stage == 1)
201 					return 1;
202 	return 0;
203 }
204 
205 int
206 unique_mesh_state(struct edns_option* list, struct module_env* env)
207 {
208 	size_t i;
209 	if(env->unique_mesh)
210 		return 1;
211 	for(; list; list=list->next)
212 		for(i=0; i<env->edns_known_options_num; i++)
213 			if(env->edns_known_options[i].opt_code == list->opt_code &&
214 				env->edns_known_options[i].no_aggregation == 1)
215 					return 1;
216 	return 0;
217 }
218 
219 void
220 log_edns_known_options(enum verbosity_value level, struct module_env* env)
221 {
222 	size_t i;
223 	char str[32], *s;
224 	size_t slen;
225 	if(env->edns_known_options_num > 0 && verbosity >= level) {
226 		verbose(level, "EDNS known options:");
227 		verbose(level, "  Code:    Bypass_cache_stage: Aggregate_mesh:");
228 		for(i=0; i<env->edns_known_options_num; i++) {
229 			s = str;
230 			slen = sizeof(str);
231 			(void)sldns_wire2str_edns_option_code_print(&s, &slen,
232 				env->edns_known_options[i].opt_code);
233 			verbose(level, "  %-8.8s %-19s %-15s", str,
234 				env->edns_known_options[i].bypass_cache_stage?"YES":"NO",
235 				env->edns_known_options[i].no_aggregation?"NO":"YES");
236 		}
237 	}
238 }
239