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