omshell.c revision 1.2.2.2 1 /* $NetBSD: omshell.c,v 1.2.2.2 2018/04/16 01:59:47 pgoyette Exp $ */
2
3 /* omshell.c
4
5 Examine and modify omapi objects. */
6
7 /*
8 * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
9 * Copyright (c) 2001-2003 by Internet Software Consortium
10 *
11 * This Source Code Form is subject to the terms of the Mozilla Public
12 * License, v. 2.0. If a copy of the MPL was not distributed with this
13 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 *
23 * Internet Systems Consortium, Inc.
24 * 950 Charter Street
25 * Redwood City, CA 94063
26 * <info (at) isc.org>
27 * https://www.isc.org/
28 *
29 */
30
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: omshell.c,v 1.2.2.2 2018/04/16 01:59:47 pgoyette Exp $");
33
34 #include "config.h"
35
36 #include <time.h>
37 #include <sys/time.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <stdarg.h>
41 #include <string.h>
42 //#include "result.h"
43 #include <syslog.h>
44 #include "dhcpctl.h"
45 #include "dhcpd.h"
46 #include <isc/file.h>
47
48 uint16_t local_port = 0;
49 uint16_t remote_port = 0;
50 libdhcp_callbacks_t omshell_callbacks = {
51 &local_port,
52 &remote_port,
53 classify,
54 check_collection,
55 dhcp,
56 #ifdef DHCPv6
57 dhcpv6,
58 #endif /* DHCPv6 */
59 bootp,
60 find_class,
61 parse_allow_deny,
62 dhcp_set_control_state,
63 };
64
65 /* Fixups */
66 isc_result_t find_class (struct class **c, const char *n, const char *f, int l)
67 {
68 return 0;
69 }
70 int parse_allow_deny (struct option_cache **oc, struct parse *cfile, int flag)
71 {
72 return 0;
73 }
74 void dhcp (struct packet *packet) { }
75 void bootp (struct packet *packet) { }
76
77 #ifdef DHCPv6
78 /* XXX: should we warn or something here? */
79 void dhcpv6(struct packet *packet) { }
80 #ifdef DHCP4o6
81 isc_result_t dhcpv4o6_handler(omapi_object_t *h)
82 {
83 return ISC_R_NOTIMPLEMENTED;
84 }
85 #endif /* DHCP4o6 */
86 #endif /* DHCPv6 */
87
88 int check_collection (struct packet *p, struct lease *l, struct collection *c)
89 {
90 return 0;
91 }
92 void classify (struct packet *packet, struct class *class) { }
93
94 static void usage (const char *s) {
95 fprintf (stderr, "Usage: %s\n", s);
96 exit (1);
97 }
98
99 static void check (isc_result_t status, const char *func) {
100 if (status != ISC_R_SUCCESS) {
101 fprintf (stderr, "%s: %s\n", func, isc_result_totext (status));
102 exit (1);
103 }
104 }
105
106 int
107 main(int argc, char **argv) {
108 isc_result_t status, waitstatus;
109 dhcpctl_handle connection;
110 dhcpctl_handle authenticator;
111 dhcpctl_handle oh;
112 struct data_string secret;
113 const char *name = 0, *algorithm = "hmac-md5";
114 int i;
115 int port = 7911;
116 const char *server = "127.0.0.1";
117 struct parse *cfile;
118 enum dhcp_token token;
119 const char *val;
120 char *s;
121 char buf[1024];
122 char s1[1024];
123 int connected = 0;
124 char hex_buf[1025];
125 char *progname;
126
127 #ifdef OLD_LOG_NAME
128 progname = "omshell";
129 #else
130 progname = argv[0];
131 #endif
132
133 libdhcp_callbacks_register(&omshell_callbacks);
134
135 for (i = 1; i < argc; i++) {
136 usage(isc_file_basename(progname));
137 }
138
139 /* Initially, log errors to stderr as well as to syslogd. */
140 openlog (isc_file_basename(progname),
141 DHCP_LOG_OPTIONS, DHCPD_LOG_FACILITY);
142 status = dhcpctl_initialize ();
143 if (status != ISC_R_SUCCESS) {
144 fprintf (stderr, "dhcpctl_initialize: %s\n",
145 isc_result_totext (status));
146 exit (1);
147 }
148
149 memset (&oh, 0, sizeof oh);
150
151 do {
152 if (!connected) {
153 } else if (oh == NULL) {
154 printf ("obj: <null>\n");
155 } else {
156 dhcpctl_remote_object_t *r = (dhcpctl_remote_object_t *)oh;
157 omapi_generic_object_t *g =
158 (omapi_generic_object_t *)(r -> inner);
159
160 printf ("obj: ");
161
162 if (r -> rtype -> type != omapi_datatype_string) {
163 printf ("?\n");
164 } else {
165 printf ("%.*s\n",
166 (int)(r -> rtype -> u . buffer . len),
167 r -> rtype -> u . buffer . value);
168 }
169
170 for (i = 0; i < g -> nvalues; i++) {
171 omapi_value_t *v = g -> values [i];
172
173 if (!g -> values [i])
174 continue;
175
176 printf ("%.*s = ", (int)v -> name -> len,
177 v -> name -> value);
178
179 if (!v -> value) {
180 printf ("<null>\n");
181 continue;
182 }
183 switch (v -> value -> type) {
184 case omapi_datatype_int:
185 printf ("%d\n",
186 v -> value -> u . integer);
187 break;
188
189 case omapi_datatype_string:
190 printf ("\"%.*s\"\n",
191 (int) v -> value -> u.buffer.len,
192 v -> value -> u.buffer.value);
193 break;
194
195 case omapi_datatype_data:
196 print_hex_or_string(v->value->u.buffer.len,
197 v->value->u.buffer.value,
198 sizeof(hex_buf), hex_buf);
199 printf("%s\n", hex_buf);
200 break;
201
202 case omapi_datatype_object:
203 printf ("<obj>\n");
204 break;
205 }
206 }
207 }
208
209 fputs ("> ", stdout);
210 fflush (stdout);
211 if (fgets (buf, sizeof(buf), stdin) == NULL)
212 break;
213
214 status = new_parse (&cfile, -1, buf, strlen(buf), "<STDIN>", 1);
215 check(status, "new_parse()");
216
217 token = next_token (&val, (unsigned *)0, cfile);
218 switch (token) {
219 default:
220 parse_warn (cfile, "unknown token: %s", val);
221 skip_to_semi (cfile);
222 break;
223
224 case END_OF_FILE:
225 case ENDOFLINE: /* EOL: */
226 break;
227
228 case TOKEN_HELP:
229 case QUESTIONMARK: /* '?': */
230 printf ("Commands:\n");
231 printf (" port <server omapi port>\n");
232 printf (" server <server address>\n");
233 printf (" key <key name> <key value>\n");
234 printf (" connect\n");
235 printf (" new <object-type>\n");
236 printf (" set <name> = <value>\n");
237 printf (" create\n");
238 printf (" open\n");
239 printf (" update\n");
240 printf (" unset <name>\n");
241 printf (" refresh\n");
242 printf (" remove\n");
243 skip_to_semi (cfile);
244 break;
245
246 case PORT:
247 token = next_token (&val, (unsigned *)0, cfile);
248 if (is_identifier (token)) {
249 struct servent *se;
250 se = getservbyname (val, "tcp");
251 if (se)
252 port = ntohs (se -> s_port);
253 else {
254 printf ("unknown service name: %s\n", val);
255 break;
256 }
257 } else if (token == NUMBER) {
258 port = atoi (val);
259 } else {
260 skip_to_semi (cfile);
261 printf ("usage: port <port>\n");
262 break;
263 }
264 token = next_token (&val, (unsigned *)0, cfile);
265 if (token != END_OF_FILE && token != EOL) {
266 printf ("usage: port <server>\n");
267 skip_to_semi (cfile);
268 break;
269 }
270 break;
271
272 case TOKEN_SERVER:
273 token = next_token (&val, (unsigned *)0, cfile);
274 if (token == NUMBER) {
275 int alen = (sizeof buf) - 1;
276 int len;
277
278 s = &buf [0];
279 len = strlen (val);
280 if (len + 1 > alen) {
281 baddq:
282 printf ("usage: server <server>\n");
283 skip_to_semi (cfile);
284 break;
285 } strcpy (buf, val);
286 s += len;
287 token = next_token (&val, (unsigned *)0, cfile);
288 if (token != DOT)
289 goto baddq;
290 *s++ = '.';
291 token = next_token (&val, (unsigned *)0, cfile);
292 if (token != NUMBER)
293 goto baddq;
294 len = strlen (val);
295 if (len + 1 > alen)
296 goto baddq;
297 strcpy (s, val);
298 s += len;
299 token = next_token (&val, (unsigned *)0, cfile);
300 if (token != DOT)
301 goto baddq;
302 *s++ = '.';
303 token = next_token (&val, (unsigned *)0, cfile);
304 if (token != NUMBER)
305 goto baddq;
306 len = strlen (val);
307 if (len + 1 > alen)
308 goto baddq;
309 strcpy (s, val);
310 s += len;
311 token = next_token (&val, (unsigned *)0, cfile);
312 if (token != DOT)
313 goto baddq;
314 *s++ = '.';
315 token = next_token (&val, (unsigned *)0, cfile);
316 if (token != NUMBER)
317 goto baddq;
318 len = strlen (val);
319 if (len + 1 > alen)
320 goto baddq;
321 strcpy (s, val);
322 val = &buf [0];
323 } else if (is_identifier (token)) {
324 /* Use val directly. */
325 } else {
326 printf ("usage: server <server>\n");
327 skip_to_semi (cfile);
328 break;
329 }
330
331 s = dmalloc (strlen (val) + 1, MDL);
332 if (!server) {
333 printf ("no memory to store server name.\n");
334 skip_to_semi (cfile);
335 break;
336 }
337 strcpy (s, val);
338 server = s;
339
340 token = next_token (&val, (unsigned *)0, cfile);
341 if (token != END_OF_FILE && token != EOL) {
342 printf ("usage: server <server>\n");
343 skip_to_semi (cfile);
344 break;
345 }
346 break;
347
348 case KEY_ALGORITHM:
349 /* Algorithm is optional */
350 token = next_token (&val, (unsigned *)0, cfile);
351 if (token != NAME || !is_identifier(token)) {
352 printf ("missing or invalid algorithm name\n");
353 printf ("usage: key-algoritm <algorithm name>\n");
354 skip_to_semi (cfile);
355 break;
356 }
357
358 s = dmalloc (strlen (val) + 1, MDL);
359 if (!s) {
360 printf ("no memory for algorithm name.\n");
361 skip_to_semi (cfile);
362 break;
363 }
364
365 strcpy (s, val);
366 algorithm = s;
367
368 token = next_token (&val, (unsigned *)0, cfile);
369 if (token != END_OF_FILE && token != EOL) {
370 printf ("extra information after %s\n", algorithm);
371 printf ("usage: key-algorithm <algorithm name>\n");
372 skip_to_semi (cfile);
373 break;
374 }
375
376 break;
377
378 case KEY:
379 token = peek_token(&val, (unsigned *)0, cfile);
380 if (token == STRING) {
381 token = next_token (&val, (unsigned *)0, cfile);
382 if (!is_identifier (token)) {
383 printf ("usage: key <name> <value>\n");
384 skip_to_semi (cfile);
385 break;
386 }
387 s = dmalloc (strlen (val) + 1, MDL);
388 if (!s) {
389 printf ("no memory for key name.\n");
390 skip_to_semi (cfile);
391 break;
392 }
393 strcpy (s, val);
394 } else {
395 s = parse_host_name(cfile);
396 if (s == NULL) {
397 printf ("usage: key <name> <value>\n");
398 skip_to_semi(cfile);
399 break;
400 }
401 }
402 name = s;
403
404 memset (&secret, 0, sizeof secret);
405 if (!parse_base64 (&secret, cfile)) {
406 skip_to_semi (cfile);
407 break;
408 }
409
410 token = next_token (&val, (unsigned *)0, cfile);
411 if (token != END_OF_FILE && token != EOL) {
412 printf ("usage: key <name> <value>\n");
413 skip_to_semi (cfile);
414 break;
415 }
416
417 break;
418
419 case CONNECT:
420 token = next_token (&val, (unsigned *)0, cfile);
421 if (token != END_OF_FILE && token != EOL) {
422 printf ("usage: connect\n");
423 skip_to_semi (cfile);
424 break;
425 }
426
427 authenticator = dhcpctl_null_handle;
428
429 if (name) {
430 status = dhcpctl_new_authenticator (&authenticator,
431 name, algorithm,
432 secret.data,
433 secret.len);
434
435 if (status != ISC_R_SUCCESS) {
436 fprintf (stderr,
437 "Cannot create authenticator: %s\n",
438 isc_result_totext (status));
439 break;
440 }
441 }
442
443 memset (&connection, 0, sizeof connection);
444 status = dhcpctl_connect (&connection,
445 server, port, authenticator);
446 if (status != ISC_R_SUCCESS) {
447 fprintf (stderr, "dhcpctl_connect: %s\n",
448 isc_result_totext (status));
449 break;
450 }
451 connected = 1;
452 break;
453
454 case TOKEN_NEW:
455 token = next_token (&val, (unsigned *)0, cfile);
456 if ((!is_identifier (token) && token != STRING)) {
457 printf ("usage: new <object-type>\n");
458 break;
459 }
460
461 if (oh) {
462 printf ("an object is already open.\n");
463 skip_to_semi (cfile);
464 break;
465 }
466
467 if (!connected) {
468 printf ("not connected.\n");
469 skip_to_semi (cfile);
470 break;
471 }
472
473 status = dhcpctl_new_object (&oh, connection, val);
474 if (status != ISC_R_SUCCESS) {
475 printf ("can't create object: %s\n",
476 isc_result_totext (status));
477 break;
478 }
479
480 token = next_token (&val, (unsigned *)0, cfile);
481 if (token != END_OF_FILE && token != EOL) {
482 printf ("usage: new <object-type>\n");
483 skip_to_semi (cfile);
484 break;
485 }
486 break;
487
488 case TOKEN_CLOSE:
489 token = next_token (&val, (unsigned *)0, cfile);
490 if (token != END_OF_FILE && token != EOL) {
491 printf ("usage: close\n");
492 skip_to_semi (cfile);
493 break;
494 }
495
496 if (!connected) {
497 printf ("not connected.\n");
498 skip_to_semi (cfile);
499 break;
500 }
501
502 if (!oh) {
503 printf ("not open.\n");
504 skip_to_semi (cfile);
505 break;
506 }
507 omapi_object_dereference (&oh, MDL);
508
509 break;
510
511 case TOKEN_SET:
512 token = next_token (&val, (unsigned *)0, cfile);
513
514 if ((!is_identifier (token) && token != STRING)) {
515 set_usage:
516 printf ("usage: set <name> = <value>\n");
517 skip_to_semi (cfile);
518 break;
519 }
520
521 if (oh == NULL) {
522 printf ("no open object.\n");
523 skip_to_semi (cfile);
524 break;
525 }
526
527 if (!connected) {
528 printf ("not connected.\n");
529 skip_to_semi (cfile);
530 break;
531 }
532
533 #ifdef HAVE_STRLCPY
534 strlcpy (s1, val, sizeof(s1));
535 #else
536 s1[0] = 0;
537 strncat (s1, val, sizeof(s1)-strlen(s1)-1);
538 #endif
539
540 token = next_token (&val, (unsigned *)0, cfile);
541 if (token != EQUAL)
542 goto set_usage;
543
544 token = next_token (&val, (unsigned *)0, cfile);
545 switch (token) {
546 case STRING:
547 dhcpctl_set_string_value (oh, val, s1);
548 token = next_token (&val, (unsigned *)0, cfile);
549 break;
550
551 case NUMBER:
552 strcpy (buf, val);
553 token = peek_token (&val, (unsigned *)0, cfile);
554 /* Colon-separated hex list? */
555 if (token == COLON)
556 goto cshl;
557 else if (token == DOT) {
558 s = buf;
559 val = buf;
560 do {
561 int intval = atoi (val);
562 if (intval > 255) {
563 parse_warn (cfile,
564 "dotted octet > 255: %s",
565 val);
566 skip_to_semi (cfile);
567 goto badnum;
568 }
569 *s++ = intval;
570 token = next_token (&val,
571 (unsigned *)0, cfile);
572 if (token != DOT)
573 break;
574 /* DOT is zero. */
575 while ((token = next_token (&val,
576 (unsigned *)0, cfile)) == DOT)
577 *s++ = 0;
578 } while (token == NUMBER);
579 dhcpctl_set_data_value (oh, buf,
580 (unsigned)(s - buf),
581 s1);
582 break;
583 }
584 dhcpctl_set_int_value (oh, atoi (buf), s1);
585 token = next_token (&val, (unsigned *)0, cfile);
586 badnum:
587 break;
588
589 case NUMBER_OR_NAME:
590 strcpy (buf, val);
591 cshl:
592 s = buf;
593 val = buf;
594 do {
595 convert_num (cfile, (unsigned char *)s,
596 val, 16, 8);
597 ++s;
598 token = next_token (&val,
599 (unsigned *)0, cfile);
600 if (token != COLON)
601 break;
602 token = next_token (&val,
603 (unsigned *)0, cfile);
604 } while (token == NUMBER ||
605 token == NUMBER_OR_NAME);
606 dhcpctl_set_data_value (oh, buf,
607 (unsigned)(s - buf), s1);
608 break;
609
610 default:
611 printf ("invalid value.\n");
612 skip_to_semi (cfile);
613 }
614
615 if (token != END_OF_FILE && token != EOL)
616 goto set_usage;
617 break;
618
619 case UNSET:
620 token = next_token (&val, (unsigned *)0, cfile);
621
622 if ((!is_identifier (token) && token != STRING)) {
623 unset_usage:
624 printf ("usage: unset <name>\n");
625 skip_to_semi (cfile);
626 break;
627 }
628
629 if (!oh) {
630 printf ("no open object.\n");
631 skip_to_semi (cfile);
632 break;
633 }
634
635 if (!connected) {
636 printf ("not connected.\n");
637 skip_to_semi (cfile);
638 break;
639 }
640
641 #if HAVE_STRLCPY
642 strlcpy (s1, val, sizeof(s1));
643 #else
644 s1[0] = 0;
645 strncat (s1, val, sizeof(s1)-strlen(s1)-1);
646 #endif
647
648 token = next_token (&val, (unsigned *)0, cfile);
649 if (token != END_OF_FILE && token != EOL)
650 goto unset_usage;
651
652 dhcpctl_set_null_value (oh, s1);
653 break;
654
655
656 case TOKEN_CREATE:
657 case TOKEN_OPEN:
658 i = token;
659 token = next_token (&val, (unsigned *)0, cfile);
660 if (token != END_OF_FILE && token != EOL) {
661 printf ("usage: %s\n", val);
662 skip_to_semi (cfile);
663 break;
664 }
665
666 if (!connected) {
667 printf ("not connected.\n");
668 skip_to_semi (cfile);
669 break;
670 }
671
672 if (!oh) {
673 printf ("you must make a new object first!\n");
674 skip_to_semi (cfile);
675 break;
676 }
677
678 if (i == TOKEN_CREATE)
679 i = DHCPCTL_CREATE | DHCPCTL_EXCL;
680 else
681 i = 0;
682
683 status = dhcpctl_open_object (oh, connection, i);
684 if (status == ISC_R_SUCCESS)
685 status = dhcpctl_wait_for_completion
686 (oh, &waitstatus);
687 if (status == ISC_R_SUCCESS)
688 status = waitstatus;
689 if (status != ISC_R_SUCCESS) {
690 printf ("can't open object: %s\n",
691 isc_result_totext (status));
692 break;
693 }
694
695 break;
696
697 case UPDATE:
698 token = next_token (&val, (unsigned *)0, cfile);
699 if (token != END_OF_FILE && token != EOL) {
700 printf ("usage: %s\n", val);
701 skip_to_semi (cfile);
702 break;
703 }
704
705 if (!connected) {
706 printf ("not connected.\n");
707 skip_to_semi (cfile);
708 break;
709 }
710
711 if (!oh) {
712 printf ("you haven't opened an object yet!\n");
713 skip_to_semi (cfile);
714 break;
715 }
716
717 status = dhcpctl_object_update(connection, oh);
718 if (status == ISC_R_SUCCESS)
719 status = dhcpctl_wait_for_completion
720 (oh, &waitstatus);
721 if (status == ISC_R_SUCCESS)
722 status = waitstatus;
723 if (status != ISC_R_SUCCESS) {
724 printf ("can't update object: %s\n",
725 isc_result_totext (status));
726 break;
727 }
728
729 break;
730
731 case REMOVE:
732 token = next_token (&val, (unsigned *)0, cfile);
733 if (token != END_OF_FILE && token != EOL) {
734 printf ("usage: remove\n");
735 skip_to_semi (cfile);
736 break;
737 }
738
739 if (!connected) {
740 printf ("not connected.\n");
741 break;
742 }
743
744 if (!oh) {
745 printf ("no object.\n");
746 break;
747 }
748
749 status = dhcpctl_object_remove(connection, oh);
750 if (status == ISC_R_SUCCESS)
751 status = dhcpctl_wait_for_completion
752 (oh, &waitstatus);
753 if (status == ISC_R_SUCCESS)
754 status = waitstatus;
755 if (status != ISC_R_SUCCESS) {
756 printf ("can't destroy object: %s\n",
757 isc_result_totext (status));
758 break;
759 }
760 omapi_object_dereference (&oh, MDL);
761 break;
762
763 case REFRESH:
764 token = next_token (&val, (unsigned *)0, cfile);
765 if (token != END_OF_FILE && token != EOL) {
766 printf ("usage: refresh\n");
767 skip_to_semi (cfile);
768 break;
769 }
770
771 if (!connected) {
772 printf ("not connected.\n");
773 break;
774 }
775
776 if (!oh) {
777 printf ("no object.\n");
778 break;
779 }
780
781 status = dhcpctl_object_refresh(connection, oh);
782 if (status == ISC_R_SUCCESS)
783 status = dhcpctl_wait_for_completion
784 (oh, &waitstatus);
785 if (status == ISC_R_SUCCESS)
786 status = waitstatus;
787 if (status != ISC_R_SUCCESS) {
788 printf ("can't refresh object: %s\n",
789 isc_result_totext (status));
790 break;
791 }
792
793 break;
794 }
795 end_parse (&cfile);
796 } while (1);
797
798 exit (0);
799 }
800
801 /* Sigh */
802 isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
803 control_object_state_t newstate)
804 {
805 if (newstate != server_shutdown)
806 return ISC_R_SUCCESS;
807 exit (0);
808 }
809