1 /* dhcpctl.c
2 
3    Subroutines providing general support for objects. */
4 
5 /*
6  * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
7  * Copyright (c) 1999-2003 by Internet Software Consortium
8  *
9  * This Source Code Form is subject to the terms of the Mozilla Public
10  * License, v. 2.0. If a copy of the MPL was not distributed with this
11  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  *
21  *   Internet Systems Consortium, Inc.
22  *   950 Charter Street
23  *   Redwood City, CA 94063
24  *   <info@isc.org>
25  *   https://www.isc.org/
26  *
27  */
28 
29 #include "dhcpd.h"
30 #include <omapip/omapip_p.h>
31 #include "dhcpctl.h"
32 
33 omapi_object_type_t *dhcpctl_callback_type;
34 omapi_object_type_t *dhcpctl_remote_type;
35 
36 /* dhcpctl_initialize ()
37 
38    Must be called before any other dhcpctl function. */
39 
dhcpctl_initialize()40 dhcpctl_status dhcpctl_initialize ()
41 {
42 	isc_result_t status;
43 
44 	/* Set up the isc and dns library managers */
45 	status = dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB,
46 				     NULL, NULL);
47 	if (status != ISC_R_SUCCESS)
48 		return status;
49 
50 	status = omapi_init();
51 	if (status != ISC_R_SUCCESS)
52 		return status;
53 
54 	status = omapi_object_type_register (&dhcpctl_callback_type,
55 					     "dhcpctl-callback",
56 					     dhcpctl_callback_set_value,
57 					     dhcpctl_callback_get_value,
58 					     dhcpctl_callback_destroy,
59 					     dhcpctl_callback_signal_handler,
60 					     dhcpctl_callback_stuff_values,
61 					     0, 0, 0, 0, 0, 0,
62 					     sizeof
63 					     (dhcpctl_callback_object_t), 0,
64 					     RC_MISC);
65 	if (status != ISC_R_SUCCESS)
66 		return status;
67 
68 	status = omapi_object_type_register (&dhcpctl_remote_type,
69 					     "dhcpctl-remote",
70 					     dhcpctl_remote_set_value,
71 					     dhcpctl_remote_get_value,
72 					     dhcpctl_remote_destroy,
73 					     dhcpctl_remote_signal_handler,
74 					     dhcpctl_remote_stuff_values,
75 					     0, 0, 0, 0, 0, 0,
76 					     sizeof (dhcpctl_remote_object_t),
77 					     0, RC_MISC);
78 	if (status != ISC_R_SUCCESS)
79 		return status;
80 
81 	return ISC_R_SUCCESS;
82 }
83 
84 /* dhcpctl_connect
85 
86    synchronous
87    returns nonzero status code if it didn't connect, zero otherwise
88    stores connection handle through connection, which can be used
89    for subsequent access to the specified server.
90    server_name is the name of the server, and port is the TCP
91    port on which it is listening.
92    authinfo is the handle to an object containing authentication
93    information. */
94 
dhcpctl_connect(dhcpctl_handle * connection,const char * server_name,int port,dhcpctl_handle authinfo)95 dhcpctl_status dhcpctl_connect (dhcpctl_handle *connection,
96 				const char *server_name, int port,
97 				dhcpctl_handle authinfo)
98 {
99 	isc_result_t status;
100 
101 	status = omapi_generic_new (connection, MDL);
102 	if (status != ISC_R_SUCCESS) {
103 		return status;
104 	}
105 
106 	status = omapi_protocol_connect (*connection, server_name,
107 					 (unsigned)port, authinfo);
108 	if (status == ISC_R_SUCCESS)
109 		return status;
110 	if (status != DHCP_R_INCOMPLETE) {
111 		omapi_object_dereference (connection, MDL);
112 		return status;
113 	}
114 
115 	status = omapi_wait_for_completion (*connection, 0);
116 	if (status != ISC_R_SUCCESS) {
117 		omapi_object_dereference (connection, MDL);
118 		return status;
119 	}
120 
121 	return status;
122 }
123 
124 /* dhcpctl_wait_for_completion
125 
126    synchronous
127    returns zero if the callback completes, a nonzero status if
128    there was some problem relating to the wait operation.   The
129    status of the queued request will be stored through s, and
130    will also be either zero for success or nonzero for some kind
131    of failure.    Never returns until completion or until the
132    connection to the server is lost.   This performs the same
133    function as dhcpctl_set_callback and the subsequent callback,
134    for programs that want to do inline execution instead of using
135    callbacks. */
136 
dhcpctl_wait_for_completion(dhcpctl_handle h,dhcpctl_status * s)137 dhcpctl_status dhcpctl_wait_for_completion (dhcpctl_handle h,
138 					    dhcpctl_status *s)
139 {
140 	isc_result_t status;
141 	status = omapi_wait_for_completion (h, 0);
142 	if (status != ISC_R_SUCCESS)
143 		return status;
144 	if (h -> type == dhcpctl_remote_type)
145 		*s = ((dhcpctl_remote_object_t *)h) -> waitstatus;
146 	return ISC_R_SUCCESS;
147 }
148 
149 /* dhcpctl_get_value
150 
151    synchronous
152    returns zero if the call succeeded, a nonzero status code if
153    it didn't.
154    result is the address of an empty data string (initialized
155    with bzero or cleared with data_string_forget).   On
156    successful completion, the addressed data string will contain
157    the value that was fetched.
158    dhcpctl_handle refers to some dhcpctl item
159    value_name refers to some value related to that item - e.g.,
160    for a handle associated with a completed host lookup, value
161    could be one of "hardware-address", "dhcp-client-identifier",
162    "known" or "client-hostname". */
163 
dhcpctl_get_value(dhcpctl_data_string * result,dhcpctl_handle h,const char * value_name)164 dhcpctl_status dhcpctl_get_value (dhcpctl_data_string *result,
165 				  dhcpctl_handle h, const char *value_name)
166 {
167 	isc_result_t status;
168 	omapi_value_t *tv = (omapi_value_t *)0;
169 	unsigned len;
170 	int ip;
171 
172 	status = omapi_get_value_str (h, (omapi_object_t *)0, value_name, &tv);
173 	if (status != ISC_R_SUCCESS)
174 		return status;
175 
176 	switch (tv -> value -> type) {
177 	      case omapi_datatype_int:
178 		len = sizeof (int);
179 		break;
180 
181 	      case omapi_datatype_string:
182 	      case omapi_datatype_data:
183 		len = tv -> value -> u.buffer.len;
184 		break;
185 
186 	      case omapi_datatype_object:
187 		len = sizeof (omapi_handle_t);
188 		break;
189 
190 	      default:
191 		omapi_typed_data_dereference (&tv -> value, MDL);
192 		return ISC_R_UNEXPECTED;
193 	}
194 
195 	status = omapi_data_string_new (result, len, MDL);
196 	if (status != ISC_R_SUCCESS) {
197 		omapi_typed_data_dereference (&tv -> value, MDL);
198 		return status;
199 	}
200 
201 	switch (tv -> value -> type) {
202 	      case omapi_datatype_int:
203 		ip = htonl (tv -> value -> u.integer);
204 		memcpy ((*result) -> value, &ip, sizeof ip);
205 		break;
206 
207 	      case omapi_datatype_string:
208 	      case omapi_datatype_data:
209 		memcpy ((*result) -> value,
210 			tv -> value -> u.buffer.value,
211 			tv -> value -> u.buffer.len);
212 		break;
213 
214 	      case omapi_datatype_object:
215 		ip = htonl (tv -> value -> u.object -> handle);
216 		memcpy ((*result) -> value, &ip, sizeof ip);
217 		break;
218 	}
219 
220 	omapi_value_dereference (&tv, MDL);
221 	return ISC_R_SUCCESS;
222 }
223 
224 /* dhcpctl_get_boolean
225 
226    like dhcpctl_get_value, but more convenient for boolean
227    values, since no data_string needs to be dealt with. */
228 
dhcpctl_get_boolean(int * result,dhcpctl_handle h,const char * value_name)229 dhcpctl_status dhcpctl_get_boolean (int *result,
230 				    dhcpctl_handle h, const char *value_name)
231 {
232 	isc_result_t status;
233 	dhcpctl_data_string data = (dhcpctl_data_string)0;
234 	int rv;
235 
236 	status = dhcpctl_get_value (&data, h, value_name);
237 	if (status != ISC_R_SUCCESS)
238 		return status;
239 	if (data -> len != sizeof rv) {
240 		omapi_data_string_dereference (&data, MDL);
241 		return ISC_R_UNEXPECTED;
242 	}
243 	memcpy (&rv, data -> value, sizeof rv);
244 	*result = ntohl (rv);
245 	omapi_data_string_dereference (&data, MDL);
246 	return ISC_R_SUCCESS;
247 }
248 
249 /* dhcpctl_set_value
250 
251    Sets a value on an object referred to by a dhcpctl_handle.
252    The opposite of dhcpctl_get_value.   Does not update the
253    server - just sets the value on the handle. */
254 
dhcpctl_set_value(dhcpctl_handle h,dhcpctl_data_string value,const char * value_name)255 dhcpctl_status dhcpctl_set_value (dhcpctl_handle h, dhcpctl_data_string value,
256 				  const char *value_name)
257 {
258 	isc_result_t status;
259 	omapi_typed_data_t *tv = (omapi_typed_data_t *)0;
260 	omapi_data_string_t *name = (omapi_data_string_t *)0;
261 
262 	status = omapi_data_string_new (&name, strlen (value_name), MDL);
263 	if (status != ISC_R_SUCCESS)
264 		return status;
265 	memcpy (name -> value, value_name, strlen (value_name));
266 
267 	status = omapi_typed_data_new (MDL, &tv, omapi_datatype_data,
268 				       value -> len);
269 	if (status != ISC_R_SUCCESS) {
270 		omapi_data_string_dereference (&name, MDL);
271 		return status;
272 	}
273 	memcpy (tv -> u.buffer.value, value -> value, value -> len);
274 
275 	status = omapi_set_value (h, (omapi_object_t *)0, name, tv);
276 	omapi_data_string_dereference (&name, MDL);
277 	omapi_typed_data_dereference (&tv, MDL);
278 	return status;
279 }
280 
281 /* dhcpctl_set_string_value
282 
283    Sets a NUL-terminated ASCII value on an object referred to by
284    a dhcpctl_handle.   like dhcpctl_set_value, but saves the
285    trouble of creating a data_string for a NUL-terminated string.
286    Does not update the server - just sets the value on the handle. */
287 
dhcpctl_set_string_value(dhcpctl_handle h,const char * value,const char * value_name)288 dhcpctl_status dhcpctl_set_string_value (dhcpctl_handle h, const char *value,
289 					 const char *value_name)
290 {
291 	isc_result_t status;
292 	omapi_typed_data_t *tv = (omapi_typed_data_t *)0;
293 	omapi_data_string_t *name = (omapi_data_string_t *)0;
294 
295 	status = omapi_data_string_new (&name, strlen (value_name), MDL);
296 	if (status != ISC_R_SUCCESS)
297 		return status;
298 	memcpy (name -> value, value_name, strlen (value_name));
299 
300 	status = omapi_typed_data_new (MDL, &tv, omapi_datatype_string, value);
301 	if (status != ISC_R_SUCCESS) {
302 		omapi_data_string_dereference (&name, MDL);
303 		return status;
304 	}
305 
306 	status = omapi_set_value (h, (omapi_object_t *)0, name, tv);
307 	omapi_data_string_dereference (&name, MDL);
308 	omapi_typed_data_dereference (&tv, MDL);
309 	return status;
310 }
311 
312 /* dhcpctl_set_buffer_value
313 
314    Sets a value on an object referred to by a dhcpctl_handle.  like
315    dhcpctl_set_value, but saves the trouble of creating a data_string
316    for string for which we have a buffer and length.  Does not update
317    the server - just sets the value on the handle. */
318 
dhcpctl_set_data_value(dhcpctl_handle h,const char * value,unsigned len,const char * value_name)319 dhcpctl_status dhcpctl_set_data_value (dhcpctl_handle h,
320 				       const char *value, unsigned len,
321 				       const char *value_name)
322 {
323 	isc_result_t status;
324 	omapi_typed_data_t *tv = (omapi_typed_data_t *)0;
325 	omapi_data_string_t *name = (omapi_data_string_t *)0;
326 	unsigned ll;
327 
328 	ll = strlen (value_name);
329 	status = omapi_data_string_new (&name, ll, MDL);
330 	if (status != ISC_R_SUCCESS)
331 		return status;
332 	memcpy (name -> value, value_name, ll);
333 
334 	status = omapi_typed_data_new (MDL, &tv,
335 				       omapi_datatype_data, len, value);
336 	if (status != ISC_R_SUCCESS) {
337 		omapi_data_string_dereference (&name, MDL);
338 		return status;
339 	}
340 	memcpy (tv -> u.buffer.value, value, len);
341 
342 	status = omapi_set_value (h, (omapi_object_t *)0, name, tv);
343 	omapi_data_string_dereference (&name, MDL);
344 	omapi_typed_data_dereference (&tv, MDL);
345 	return status;
346 }
347 
348 /* dhcpctl_set_null_value
349 
350    Sets a null value on an object referred to by a dhcpctl_handle. */
351 
dhcpctl_set_null_value(dhcpctl_handle h,const char * value_name)352 dhcpctl_status dhcpctl_set_null_value (dhcpctl_handle h,
353 				       const char *value_name)
354 {
355 	isc_result_t status;
356 	omapi_data_string_t *name = (omapi_data_string_t *)0;
357 	unsigned ll;
358 
359 	ll = strlen (value_name);
360 	status = omapi_data_string_new (&name, ll, MDL);
361 	if (status != ISC_R_SUCCESS)
362 		return status;
363 	memcpy (name -> value, value_name, ll);
364 
365 	status = omapi_set_value (h, (omapi_object_t *)0, name,
366 				  (omapi_typed_data_t *)0);
367 	omapi_data_string_dereference (&name, MDL);
368 	return status;
369 }
370 
371 /* dhcpctl_set_boolean_value
372 
373    Sets a boolean value on an object - like dhcpctl_set_value,
374    only more convenient for booleans. */
375 
dhcpctl_set_boolean_value(dhcpctl_handle h,int value,const char * value_name)376 dhcpctl_status dhcpctl_set_boolean_value (dhcpctl_handle h, int value,
377 					  const char *value_name)
378 {
379 	isc_result_t status;
380 	omapi_typed_data_t *tv = (omapi_typed_data_t *)0;
381 	omapi_data_string_t *name = (omapi_data_string_t *)0;
382 
383 	status = omapi_data_string_new (&name, strlen (value_name), MDL);
384 	if (status != ISC_R_SUCCESS)
385 		return status;
386 	memcpy (name -> value, value_name, strlen (value_name));
387 
388 	status = omapi_typed_data_new (MDL, &tv, omapi_datatype_int, value);
389 	if (status != ISC_R_SUCCESS) {
390 		omapi_data_string_dereference (&name, MDL);
391 		return status;
392 	}
393 
394 	status = omapi_set_value (h, (omapi_object_t *)0, name, tv);
395 	omapi_data_string_dereference (&name, MDL);
396 	omapi_typed_data_dereference (&tv, MDL);
397 	return status;
398 }
399 
400 /* dhcpctl_set_int_value
401 
402    Sets a boolean value on an object - like dhcpctl_set_value,
403    only more convenient for booleans. */
404 
dhcpctl_set_int_value(dhcpctl_handle h,int value,const char * value_name)405 dhcpctl_status dhcpctl_set_int_value (dhcpctl_handle h, int value,
406 				      const char *value_name)
407 {
408 	isc_result_t status;
409 	omapi_typed_data_t *tv = (omapi_typed_data_t *)0;
410 	omapi_data_string_t *name = (omapi_data_string_t *)0;
411 
412 	status = omapi_data_string_new (&name, strlen (value_name), MDL);
413 	if (status != ISC_R_SUCCESS)
414 		return status;
415 	memcpy (name -> value, value_name, strlen (value_name));
416 
417 	status = omapi_typed_data_new (MDL, &tv, omapi_datatype_int, value);
418 	if (status != ISC_R_SUCCESS) {
419 		omapi_data_string_dereference (&name, MDL);
420 		return status;
421 	}
422 
423 	status = omapi_set_value (h, (omapi_object_t *)0, name, tv);
424 	omapi_data_string_dereference (&name, MDL);
425 	omapi_typed_data_dereference (&tv, MDL);
426 	return status;
427 }
428 
429 /* dhcpctl_object_update
430 
431    Queues an update on the object referenced by the handle (there
432    can't be any other work in progress on the handle).   An
433    update means local parameters will be sent to the server. */
434 
dhcpctl_object_update(dhcpctl_handle connection,dhcpctl_handle h)435 dhcpctl_status dhcpctl_object_update (dhcpctl_handle connection,
436 				      dhcpctl_handle h)
437 {
438 	isc_result_t status;
439 	omapi_object_t *message = (omapi_object_t *)0;
440 	dhcpctl_remote_object_t *ro;
441 
442 	if (h -> type != dhcpctl_remote_type)
443 		return DHCP_R_INVALIDARG;
444 	ro = (dhcpctl_remote_object_t *)h;
445 
446 	status = omapi_message_new (&message, MDL);
447 	if (status != ISC_R_SUCCESS) {
448 		omapi_object_dereference (&message, MDL);
449 		return status;
450 	}
451 	status = omapi_set_int_value (message, (omapi_object_t *)0,
452 				      "op", OMAPI_OP_UPDATE);
453 	if (status != ISC_R_SUCCESS) {
454 		omapi_object_dereference (&message, MDL);
455 		return status;
456 	}
457 
458 	status = omapi_set_object_value (message, (omapi_object_t *)0,
459 					 "object", h);
460 	if (status != ISC_R_SUCCESS) {
461 		omapi_object_dereference (&message, MDL);
462 		return status;
463 	}
464 
465 	status = omapi_set_int_value (message, (omapi_object_t *)0, "handle",
466 				      (int)(ro -> remote_handle));
467 	if (status != ISC_R_SUCCESS) {
468 		omapi_object_dereference (&message, MDL);
469 		return status;
470 	}
471 
472 	omapi_message_register (message);
473 	status = omapi_protocol_send_message (connection -> outer,
474 					      (omapi_object_t *)0,
475 					      message, (omapi_object_t *)0);
476 	omapi_object_dereference (&message, MDL);
477 	return status;
478 }
479 
480 /* Requests a refresh on the object referenced by the handle (there
481    can't be any other work in progress on the handle).   A
482    refresh means local parameters are updated from the server. */
483 
dhcpctl_object_refresh(dhcpctl_handle connection,dhcpctl_handle h)484 dhcpctl_status dhcpctl_object_refresh (dhcpctl_handle connection,
485 				       dhcpctl_handle h)
486 {
487 	isc_result_t status;
488 	omapi_object_t *message = (omapi_object_t *)0;
489 	dhcpctl_remote_object_t *ro;
490 
491 	if (h -> type != dhcpctl_remote_type)
492 		return DHCP_R_INVALIDARG;
493 	ro = (dhcpctl_remote_object_t *)h;
494 
495 	status = omapi_message_new (&message, MDL);
496 	if (status != ISC_R_SUCCESS) {
497 		omapi_object_dereference (&message, MDL);
498 		return status;
499 	}
500 	status = omapi_set_int_value (message, (omapi_object_t *)0,
501 				      "op", OMAPI_OP_REFRESH);
502 	if (status != ISC_R_SUCCESS) {
503 		omapi_object_dereference (&message, MDL);
504 		return status;
505 	}
506 	status = omapi_set_int_value (message, (omapi_object_t *)0,
507 				      "handle", (int)(ro -> remote_handle));
508 	if (status != ISC_R_SUCCESS) {
509 		omapi_object_dereference (&message, MDL);
510 		return status;
511 	}
512 
513 	omapi_message_register (message);
514 	status = omapi_protocol_send_message (connection -> outer,
515 					      (omapi_object_t *)0,
516 					      message, (omapi_object_t *)0);
517 
518 	/* We don't want to send the contents of the object down the
519 	   wire, but we do need to reference it so that we know what
520 	   to do with the update. */
521 	status = omapi_set_object_value (message, (omapi_object_t *)0,
522 					 "object", h);
523 	if (status != ISC_R_SUCCESS) {
524 		omapi_object_dereference (&message, MDL);
525 		return status;
526 	}
527 
528 	omapi_object_dereference (&message, MDL);
529 	return status;
530 }
531 
532 /* Requests the removal of the object referenced by the handle (there
533    can't be any other work in progress on the handle).   A
534    removal means that all searchable references to the object on the
535    server are deleted. */
536 
dhcpctl_object_remove(dhcpctl_handle connection,dhcpctl_handle h)537 dhcpctl_status dhcpctl_object_remove (dhcpctl_handle connection,
538 				      dhcpctl_handle h)
539 {
540 	isc_result_t status;
541 	omapi_object_t *message = (omapi_object_t *)0;
542 	dhcpctl_remote_object_t *ro;
543 
544 	if (h -> type != dhcpctl_remote_type)
545 		return DHCP_R_INVALIDARG;
546 	ro = (dhcpctl_remote_object_t *)h;
547 
548 	status = omapi_message_new (&message, MDL);
549 	if (status != ISC_R_SUCCESS) {
550 		omapi_object_dereference (&message, MDL);
551 		return status;
552 	}
553 	status = omapi_set_int_value (message, (omapi_object_t *)0,
554 				      "op", OMAPI_OP_DELETE);
555 	if (status != ISC_R_SUCCESS) {
556 		omapi_object_dereference (&message, MDL);
557 		return status;
558 	}
559 
560 	status = omapi_set_int_value (message, (omapi_object_t *)0, "handle",
561 				      (int)(ro -> remote_handle));
562 	if (status != ISC_R_SUCCESS) {
563 		omapi_object_dereference (&message, MDL);
564 		return status;
565 	}
566 
567 	status = omapi_set_object_value (message, (omapi_object_t *)0,
568 					 "notify-object", h);
569 	if (status != ISC_R_SUCCESS) {
570 		omapi_object_dereference (&message, MDL);
571 		return status;
572 	}
573 
574 	omapi_message_register (message);
575 	status = omapi_protocol_send_message (connection -> outer,
576 					      (omapi_object_t *)0,
577 					      message, (omapi_object_t *)0);
578 	omapi_object_dereference (&message, MDL);
579 	return status;
580 }
581 
dhcpctl_data_string_dereference(dhcpctl_data_string * vp,const char * file,int line)582 isc_result_t dhcpctl_data_string_dereference (dhcpctl_data_string *vp,
583 					      const char *file, int line)
584 {
585 	return omapi_data_string_dereference (vp, file, line);
586 }
587