srputil.c revision 1.1 1 /* srputil.c
2 *
3 * Copyright (c) 2020-2024 Apple Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * SRP Advertising Proxy utility program, allows:
18 * start/stop advertising proxy
19 * get/track list of service types
20 * get/track list of services of a particular type
21 * get/track list of hosts
22 * get/track information about a particular host
23 */
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <fcntl.h>
34 #include <time.h>
35 #include <dns_sd.h>
36 #include <net/if.h>
37 #include <inttypes.h>
38
39 void *main_queue = NULL;
40
41 #include "srp.h"
42 #include "dns-msg.h"
43 #include "ioloop.h"
44 #include "advertising_proxy_services.h"
45 #include "route-tracker.h"
46 #include "state-machine.h"
47 #include "thread-service.h"
48 #include "service-tracker.h"
49 #include "probe-srp.h"
50 #include "cti-services.h"
51 #include "adv-ctl-server.h"
52
53
54 static void
55 flushed_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
56 {
57 INFO("flushed: cref %p response %p err %d.", cref, result, err);
58 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
59 exit(1);
60 }
61 // We don't need to wait around after flushing.
62 exit(0);
63 }
64
65 static void
66 block_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
67 {
68 INFO("blocked: cref %p response %p err %d.", cref, result, err);
69 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
70 exit(1);
71 }
72 // We don't need to wait around after blocking.
73 exit(0);
74 }
75
76 static void
77 unblock_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
78 {
79 INFO("unblocked: cref %p response %p err %d.", cref, result, err);
80 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
81 exit(1);
82 }
83 // We don't need to wait around after unblocking.
84 exit(0);
85 }
86
87 static void
88 regenerate_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
89 {
90 INFO("regenerated ula: cref %p response %p err %d.", cref, result, err);
91 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
92 exit(1);
93 }
94 // We don't need to wait around after unblocking.
95 exit(0);
96 }
97
98 static void
99 prefix_advertise_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
100 {
101 INFO("advertise prefix: cref %p response %p err %d.", cref, result, err);
102 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
103 exit(1);
104 }
105 // We don't need to wait around after advertising prefix.
106 exit(0);
107 }
108
109 static void
110 add_prefix_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
111 {
112 INFO("add prefix: cref %p response %p err %d.", cref, result, err);
113 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
114 exit(1);
115 }
116 // We don't need to wait around after advertising prefix.
117 exit(0);
118 }
119
120 static void
121 remove_prefix_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
122 {
123 INFO("remove prefix: cref %p response %p err %d.", cref, result, err);
124 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
125 exit(1);
126 }
127 // We don't need to wait around after advertising prefix.
128 exit(0);
129 }
130
131 static void
132 add_nat64_prefix_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
133 {
134 INFO("add nat64 prefix: cref %p response %p err %d.", cref, result, err);
135 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
136 exit(1);
137 }
138 exit(0);
139 }
140
141 static void
142 remove_nat64_prefix_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
143 {
144 INFO("remove nat64 prefix: cref %p response %p err %d.", cref, result, err);
145 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
146 exit(1);
147 }
148 exit(0);
149 }
150
151 static void
152 stop_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
153 {
154 INFO("stopped: cref %p response %p err %d.", cref, result, err);
155 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
156 exit(1);
157 }
158 // We don't need to wait around after stopping.
159 exit(0);
160 }
161
162 static const char *
163 print_address(advertising_proxy_host_address_t *address, char *addrbuf, size_t addrbuf_size)
164 {
165 if (address->rrtype == 0) {
166 return (char *)address->rdata;
167 } else if (address->rrtype == dns_rrtype_a && address->rdlen == 4) {
168 inet_ntop(AF_INET, address->rdata, addrbuf, (socklen_t)addrbuf_size);
169 return addrbuf;
170 } else if (address->rrtype == dns_rrtype_aaaa && address->rdlen == 16) {
171 inet_ntop(AF_INET6, address->rdata, addrbuf, (socklen_t)addrbuf_size);
172 return addrbuf;
173 } else {
174 sprintf(addrbuf, "Family-%d", address->rrtype);
175 return addrbuf;
176 }
177 }
178
179 static void
180 services_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
181 {
182 int i;
183 int64_t lease, hours, minutes, seconds;
184 advertising_proxy_host_t *host = result;
185 const char *address = "<no address>";
186 char *addrbuf = NULL;
187 size_t addrbuflen;
188 uint64_t ula;
189
190 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
191 INFO("services: cref %p response %p err %d.", cref, result, err);
192 exit(1);
193 }
194 if (result == NULL) {
195 INFO("services: cref %p response %p err %d.", cref, result, err);
196 exit(0);
197 }
198
199 if (host->num_instances == 0) {
200 i = -1;
201 } else {
202 i = 0;
203 }
204 for (; i < host->num_instances; i++) {
205 const char *instance_name, *service_type, *reg_type;
206 char port[6]; // uint16_t as ascii
207
208 if (i == -1 || host->instances[i].instance_name == NULL) {
209 instance_name = "<no instances>";
210 service_type = "";
211 port[0] = 0;
212 reg_type = "";
213 } else {
214 instance_name = host->instances[i].instance_name;
215 service_type = host->instances[i].service_type;
216 snprintf(port, sizeof(port), "%u", host->instances[i].port);
217 reg_type = host->instances[i].reg_type;
218 }
219
220 if (host->num_addresses > 0) {
221 addrbuflen = host->num_addresses * (INET6_ADDRSTRLEN + 1);
222 addrbuf = malloc(addrbuflen);
223 if (addrbuf == NULL) {
224 address = "<no memory for address buffer>";
225 } else {
226 char *ap = addrbuf;
227 for (int j = 0; j < host->num_addresses; j++) {
228 *ap++ = ' ';
229 address = print_address(&host->addresses[j], ap, addrbuflen - (ap - addrbuf));
230 size_t len = strlen(address);
231 if (address != ap) {
232 if (len + ap + 1 > addrbuf + addrbuflen) {
233 len = addrbuflen - (ap - addrbuf) - 1;
234 }
235 memcpy(ap, address, len + 1); // Includes NUL
236 }
237 ap += len;
238 }
239 address = addrbuf;
240 }
241 }
242 lease = host->lease_time;
243 hours = lease / 3600 / 1000;
244 lease -= hours * 3600 * 1000;
245 minutes = lease / 60 / 1000;
246 lease -= minutes * 60 * 1000;
247 seconds = lease / 1000;
248 lease -= seconds * 1000;
249
250 // Our implementation of the stable server ID uses the server ULA, so just copy out those 48 bits,
251 // which are in network byte order.
252 ula = 0;
253 for (int j = 1; j < 6; j++) {
254 ula = ula << 8 | (((uint8_t *)&host->server_id)[j]);
255 }
256 printf("\"%s\" \"%s\" %s %s %s %" PRIu64 ":%" PRIu64 ":%" PRIu64 ".%" PRIu64 " \"%s\" \"%s\" %s %" PRIx64 "\n",
257 host->regname, instance_name, service_type, port,
258 address == NULL ? "" : address, hours, minutes, seconds, lease, host->hostname,
259 reg_type, host->removed ? "invalid" : "valid", ula);
260 if (addrbuf != NULL) {
261 free(addrbuf);
262 }
263 }
264 }
265
266 static void
267 ula_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
268 {
269 INFO("get_ula: cref %p response %p err %d.", cref, result, err);
270 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
271 fprintf(stderr, "ULA get failed: %d\n", err);
272 exit(1);
273 }
274 uint64_t ula = *((uint64_t *)result);
275 printf("ULA: %" PRIx64 "\n", ula);
276 exit(0);
277 }
278
279 static void
280 disable_srp_replication_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
281 {
282 INFO("disable_srp_replication: cref %p response %p err %d.", cref, result, err);
283 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
284 exit(1);
285 }
286 // We don't need to wait around after SRP replication disabled.
287 exit(0);
288 }
289
290 static void
291 drop_srpl_connection_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
292 {
293 INFO("drop_srpl_connection: cref %p response %p err %d.", cref, result, err);
294 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
295 exit(1);
296 }
297 exit(0);
298 }
299
300 static void
301 undrop_srpl_connection_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
302 {
303 INFO("undrop_srpl_connection: cref %p response %p err %d.", cref, result, err);
304 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
305 exit(1);
306 }
307 exit(0);
308 }
309
310 static void
311 drop_srpl_advertisement_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
312 {
313 INFO("drop_srpl_advertisement: cref %p response %p err %d.", cref, result, err);
314 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
315 exit(1);
316 }
317 exit(0);
318 }
319
320 static void
321 undrop_srpl_advertisement_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
322 {
323 INFO("undrop_srpl_advertisement: cref %p response %p err %d.", cref, result, err);
324 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
325 exit(1);
326 }
327 exit(0);
328 }
329
330 static void
331 start_dropping_push_connections_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
332 {
333 INFO("start_dropping_push_connections: cref %p response %p err %d.", cref, result, err);
334 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
335 exit(1);
336 }
337 exit(0);
338 }
339
340 static void
341 start_breaking_time_validation_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
342 {
343 INFO("start_breaking_time_validation: cref %p response %p err %d.", cref, result, err);
344 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
345 exit(1);
346 }
347 exit(0);
348 }
349
350 static void
351 block_anycast_service_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
352 {
353 INFO("block_anycast_service: cref %p response %p err %d.", cref, result, err);
354 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
355 exit(1);
356 }
357 exit(0);
358 }
359
360 static void
361 unblock_anycast_service_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
362 {
363 INFO("unblock_anycast_service: cref %p response %p err %d.", cref, result, err);
364 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
365 exit(1);
366 }
367 exit(0);
368 }
369
370 static void
371 start_thread_shutdown_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
372 {
373 INFO("cref %p response %p err %d.", cref, result, err);
374 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
375 exit(1);
376 }
377 exit(0);
378 }
379
380 typedef struct variable variable_t;
381 struct variable {
382 variable_t *next;
383 const char *name, *value;
384 };
385
386 static void
387 set_variable_callback(advertising_proxy_conn_ref cref, void *context, void *result, advertising_proxy_error_type err)
388 {
389 variable_t *variable = context;
390 INFO("set_variable: cref %p response %p err %d, variable name %s, value %s.",
391 cref, result, err, variable->name, variable->value);
392 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
393 if (variable->next == NULL) {
394 exit(1);
395 }
396 }
397 if (variable->next == NULL) {
398 exit(0);
399 }
400 }
401
402 static comm_t *tcp_connection;
403 bool do_tcp_zero_test = false;
404 bool do_tcp_fin_length = false;
405 bool do_tcp_fin_payload = false;
406
407 service_tracker_t *tracker;
408
409 // Dummy functions required to use service tracker here
410 void
411 adv_ctl_thread_shutdown_status_check(srp_server_t *UNUSED server_state) {
412 }
413
414 static void
415 service_done_callback(void *context, cti_status_t status)
416 {
417 const char *action = context;
418 if (status != kCTIStatus_NoError) {
419 fprintf(stderr, PUB_S_SRP " failed, status %d", action, status);
420 exit(1);
421 } else {
422 fprintf(stderr, PUB_S_SRP " done", action);
423 exit(0);
424 }
425 }
426
427 static void
428 service_set_changed(bool unicast)
429 {
430 thread_service_t *winner = NULL;
431 for (thread_service_t *service = service_tracker_services_get(tracker); service != NULL; service = service->next) {
432 if (service->ignore) {
433 continue;
434 }
435 if (unicast && service->service_type == unicast_service) {
436 if (winner == NULL || in6addr_compare(&service->u.unicast.address, &winner->u.unicast.address) < 0) {
437 winner = service;
438 }
439 }
440 }
441 if (winner == NULL) {
442 fprintf(stderr, "no services present!");
443 exit(1);
444 }
445 winner->u.unicast.address.s6_addr[15] = 0;
446 uint8_t service_data[1] = { THREAD_SRP_SERVER_OPTION };
447 uint8_t server_data[18];
448 memcpy(server_data, &winner->u.unicast.address, 15);
449 server_data[15] = 0;
450 server_data[16] = winner->u.unicast.port[0];
451 server_data[17] = winner->u.unicast.port[1];
452 int ret = cti_add_service(NULL, "add", service_done_callback, NULL,
453 THREAD_ENTERPRISE_NUMBER, service_data, 1, server_data, 18);
454 if (ret != kCTIStatus_NoError) {
455 fprintf(stderr, "add_service failed: %d", ret);
456 exit(1);
457 }
458 }
459
460 static void
461 unicast_service_set_changed(void *UNUSED context)
462 {
463 service_set_changed(true);
464 }
465
466 static void
467 start_advertising_winning_unicast_service(void)
468 {
469 tracker = service_tracker_create(NULL);
470 if (tracker != NULL) {
471 service_tracker_callback_add(tracker, unicast_service_set_changed, NULL, NULL);
472 service_tracker_start(tracker);
473 } else {
474 fprintf(stderr, "unable to allocate tracker");
475 exit(1);
476 }
477 }
478
479 static void
480 start_removing_unicast_service(void)
481 {
482 uint8_t service_data[1] = { THREAD_SRP_SERVER_OPTION };
483 int ret = cti_remove_service(NULL, "remove", service_done_callback, NULL, THREAD_ENTERPRISE_NUMBER, service_data, 1);
484 if (ret != kCTIStatus_NoError) {
485 fprintf(stderr, "remove_service failed: %d", ret);
486 exit(1);
487 }
488 }
489
490 static void
491 tcp_datagram_callback(comm_t *NONNULL comm, message_t *NONNULL message, void *NULLABLE context)
492 {
493 (void)comm;
494 (void)context;
495 fprintf(stderr, "tcp datagram received, length %d", message->length);
496 }
497
498 static void
499 tcp_connect_callback(comm_t *NONNULL connection, void *NULLABLE context)
500 {
501 fprintf(stderr, "tcp connection succeeded...\n");
502 uint8_t length[2];
503 struct iovec iov[2];
504 char databuf[128];
505 memset(databuf, 0, sizeof(databuf));
506 memset(iov, 0, sizeof(iov));
507
508 (void)context;
509
510 if (do_tcp_zero_test) {
511 memset(length, 0, sizeof(length));
512 iov[0].iov_len = 2;
513 iov[0].iov_base = length;
514 ioloop_send_data(connection, NULL, iov, 1);
515 } else if (do_tcp_fin_length) {
516 memset(length, 0, sizeof(length));
517 iov[0].iov_len = 1;
518 iov[0].iov_base = &length;
519 ioloop_send_final_data(connection, NULL, iov, 1);
520 } else if (do_tcp_fin_payload) {
521 length[0] = 0;
522 length[1] = 255;
523 iov[0].iov_len = 2;
524 iov[0].iov_base = length;
525 iov[1].iov_len = 128;
526 iov[1].iov_base = databuf;
527 ioloop_send_final_data(connection, NULL, iov, 2);
528 }
529 }
530
531 static void
532 tcp_disconnect_callback(comm_t *NONNULL comm, void *NULLABLE context, int error)
533 {
534 (void)comm;
535 (void)context;
536 (void)error;
537 fprintf(stderr, "tcp remote close.\n");
538 exit(0);
539 }
540
541 static int
542 start_tcp_test(void)
543 {
544 addr_t address;
545 memset(&address, 0, sizeof(address));
546 address.sa.sa_family = AF_INET;
547 address.sin.sin_addr.s_addr = htonl(0x7f000001);
548 address.sin.sin_port = htons(53);
549 #ifndef NOT_HAVE_SA_LEN
550 address.sin.sin_len = sizeof(address.sin);
551 #endif
552 tcp_connection = ioloop_connection_create(&address, false, true, false, false, tcp_datagram_callback,
553 tcp_connect_callback, tcp_disconnect_callback, NULL, NULL);
554 if (tcp_connection == NULL) {
555 return kDNSSDAdvertisingProxyStatus_NoMemory;
556 }
557 return kDNSSDAdvertisingProxyStatus_NoError;
558 }
559
560 const char *need_name, *need_service;
561 bool needed_flag;
562
563 advertising_proxy_conn_ref service_sub;
564
565 static void
566 service_callback(advertising_proxy_conn_ref UNUSED sub, void *UNUSED context, advertising_proxy_error_type error)
567 {
568 fprintf(stderr, "service callback: %d\n", error);
569 exit(0);
570 }
571
572 static void
573 start_needing_service(void)
574 {
575 advertising_proxy_error_type ret = advertising_proxy_set_service_needed(&service_sub, dispatch_get_main_queue(),
576 service_callback, NULL, NULL, need_service,
577 needed_flag);
578 if (ret != kDNSSDAdvertisingProxyStatus_NoError) {
579 fprintf(stderr, "advertising_proxy_service_create failed: %d\n", ret);
580 exit(1);
581 }
582 }
583
584 advertising_proxy_conn_ref instance_sub;
585
586 static void
587 instance_callback(advertising_proxy_conn_ref UNUSED sub, void *UNUSED context, advertising_proxy_error_type error)
588 {
589 fprintf(stderr, "instance callback: %d\n", error);
590 exit(0);
591 }
592
593 static void
594 start_needing_instance(void)
595 {
596 advertising_proxy_error_type ret = advertising_proxy_set_service_needed(&instance_sub, dispatch_get_main_queue(),
597 instance_callback, NULL, need_name,
598 need_service, needed_flag);
599 if (ret != kDNSSDAdvertisingProxyStatus_NoError) {
600 fprintf(stderr, "advertising_proxy_set_service_needed failed: %d\n", ret);
601 exit(1);
602 }
603 }
604
605 const char *browse_service, *resolve_name, *resolve_service;
606
607 advertising_proxy_subscription_t *browse_sub;
608
609 static void
610 browse_callback(advertising_proxy_subscription_t *UNUSED sub, advertising_proxy_error_type error, uint32_t interface_index,
611 bool add, const char *instance_name, const char *service_type, void *UNUSED context)
612 {
613 if (error != kDNSSDAdvertisingProxyStatus_NoError) {
614 fprintf(stderr, "browse_callback: %d\n", error);
615 exit(1);
616 }
617
618 fprintf(stderr, "browse: %d %s %s %s\n", interface_index, add ? "add" : "rmv", instance_name, service_type);
619 }
620
621 static void
622 start_browsing_service(void)
623 {
624 advertising_proxy_error_type ret = advertising_proxy_browse_create(&browse_sub, dispatch_get_main_queue(),
625 browse_service, browse_callback, NULL);
626 if (ret != kDNSSDAdvertisingProxyStatus_NoError) {
627 fprintf(stderr, "advertising_proxy_browse_create failed: %d\n", ret);
628 exit(1);
629 }
630 }
631
632 advertising_proxy_subscription_t *resolve_sub;
633
634 static void
635 resolve_callback(advertising_proxy_subscription_t *UNUSED sub, advertising_proxy_error_type error,
636 uint32_t interface_index, bool add, const char *fullname, const char *hostname, uint16_t port,
637 uint16_t txt_length, const uint8_t *UNUSED txt_record, void *UNUSED context)
638 {
639 if (error != kDNSSDAdvertisingProxyStatus_NoError) {
640 fprintf(stderr, "resolve_create callback: %d\n", error);
641 exit(1);
642 }
643
644 fprintf(stderr, "resolved: %d %s %s %s %d %d\n", interface_index, add ? "add" : "rmv",
645 fullname, hostname, port, txt_length);
646 }
647
648 static void
649 start_resolving_service(void)
650 {
651 advertising_proxy_error_type ret = advertising_proxy_resolve_create(&resolve_sub, dispatch_get_main_queue(),
652 resolve_name, resolve_service, NULL,
653 resolve_callback, NULL);
654 if (ret != kDNSSDAdvertisingProxyStatus_NoError) {
655 fprintf(stderr, "advertising_proxy_resolve_create failed: %d\n", ret);
656 exit(1);
657 }
658 }
659
660 advertising_proxy_subscription_t *registrar_sub;
661
662 static void
663 registrar_callback(advertising_proxy_subscription_t *UNUSED sub,
664 advertising_proxy_error_type error, void *UNUSED context)
665 {
666 if (error != kDNSSDAdvertisingProxyStatus_NoError) {
667 fprintf(stderr, "registrar_callback: %d\n", error);
668 exit(1);
669 }
670
671 INFO("SRP registrar is enabled.");
672 }
673
674 static void
675 start_registrar(void)
676 {
677 advertising_proxy_error_type ret = advertising_proxy_registrar_create(®istrar_sub, dispatch_get_main_queue(),
678 registrar_callback, NULL);
679 if (ret != kDNSSDAdvertisingProxyStatus_NoError) {
680 fprintf(stderr, "advertising_proxy_registrar_create failed: %d", ret);
681 exit(1);
682 }
683 }
684
685
686 static void
687 usage(void)
688 {
689 fprintf(stderr, "srputil start [if1 .. ifN -] -- start the SRP MDNS Proxy through launchd\n");
690 fprintf(stderr, " tcp-zero -- connect to port 53, send a DNS message that's got a zero-length payload\n");
691 fprintf(stderr, " tcp-fin-length -- connect to port 53, send a DNS message that ends before length is complete\n");
692 fprintf(stderr, " tcp-fin-payload -- connect to port 53, send a DNS message that ends before payload is complete\n");
693 fprintf(stderr, " services -- get the list of services currently being advertised\n");
694 fprintf(stderr, " block -- block the SRP listener\n");
695 fprintf(stderr, " unblock -- unblock the SRP listener\n");
696 fprintf(stderr, " regenerate-ula -- generate a new ULA and restart the network\n");
697 fprintf(stderr, " adv-prefix-high -- advertise high-priority prefix to thread network\n");
698 fprintf(stderr, " adv-prefix -- advertise prefix to thread network\n");
699 fprintf(stderr, " stop -- stop advertising as SRP server\n");
700 fprintf(stderr, " get-ula -- fetch the current ULA prefix configured on the SRP server\n");
701 fprintf(stderr, " disable-srpl -- disable SRP replication\n");
702 fprintf(stderr, " add-prefix <ipv6 prefix> -- add an OMR prefix\n");
703 fprintf(stderr, " remove-prefix <ipv6 prefix -- remove an OMR prefix\n");
704 fprintf(stderr, " add-nat64-prefix <nat64 prefix> -- add an nat64 prefix\n");
705 fprintf(stderr, " remove-nat64-prefix <nat64 prefix> -- remove an nat64 prefix\n");
706 fprintf(stderr, " drop-srpl-connection -- drop existing srp replication connections\n");
707 fprintf(stderr, " undrop-srpl-connection -- restart srp replication connections that were dropped \n");
708 fprintf(stderr, " drop-srpl-advertisement -- stop advertising srpl service (but keep it around)\n");
709 fprintf(stderr, " undrop-srpl-advertisement -- resume advertising srpl service\n");
710 fprintf(stderr, " start-dropping-push -- start repeatedly dropping any active push connections after 90 seconds\n");
711 fprintf(stderr, " start-breaking-time -- start breaking time validation on replicated SRP registrations\n");
712 fprintf(stderr, " set [variable] [value] -- set the value of variable to value (e.g. set min-lease-time 100)\n");
713 fprintf(stderr, " block-anycast-service -- block advertising anycast service\n");
714 fprintf(stderr, " unblock-anycast-service -- unblock advertising anycast service\n");
715 fprintf(stderr, " start-thread-shutdown -- start thread network shutdown\n");
716 fprintf(stderr, " advertise-winning-unicast-service -- advertise a unicast service that wins over the current service\n");
717 fprintf(stderr, " browse <service> -- start an advertising_proxy_browse on the specified service\n");
718 fprintf(stderr, " resolve <name> <service> -- start an advertising_proxy_resolve on the specified service instance\n");
719 fprintf(stderr, " need-service <service> <flag> -- signal to srp-mdns-proxy that we need to discover a service\n");
720 fprintf(stderr, " need-instance <name> <service> <flag> -- signal to srp-mdns-proxy that we need to discover a service\n");
721 fprintf(stderr, " start-srp -- on thread device, enable srp registration\n");
722 #ifdef NOTYET
723 fprintf(stderr, " flush -- flush all entries from the SRP proxy (for testing only)\n");
724 #endif
725 }
726
727 bool start_proxy = false;
728 bool flush_entries = false;
729 bool list_services = false;
730 bool block = false;
731 bool unblock = false;
732 bool regenerate_ula = false;
733 bool adv_prefix = false;
734 bool adv_prefix_high = false;
735 bool stop_proxy = false;
736 bool dump_stdin = false;
737 bool get_ula = false;
738 bool disable_srp_replication = false;
739 bool dso_test = false;
740 bool drop_srpl_connection;
741 bool undrop_srpl_connection;
742 bool drop_srpl_advertisement;
743 bool undrop_srpl_advertisement;
744 bool start_dropping_push_connections;
745 bool add_thread_prefix = false;
746 bool remove_thread_prefix = false;
747 bool add_nat64_prefix = false;
748 bool remove_nat64_prefix = false;
749 bool start_breaking_time_validation = false;
750 bool test_route_tracker = false;
751 bool block_anycast_service = false;
752 bool unblock_anycast_service = false;
753 bool start_thread_shutdown = false;
754 bool advertise_winning_unicast_service = false;
755 bool remove_unicast_service = false;
756 bool start_srp = false;
757 uint8_t prefix_buf[16];
758 #ifdef NOTYET
759 bool watch = false;
760 bool get = false;
761 #endif
762 variable_t *variables;
763 int num_permitted_interfaces;
764 char **permitted_interfaces;
765
766 static void
767 start_activities(void *context)
768 {
769 advertising_proxy_error_type err = kDNSSDAdvertisingProxyStatus_NoError;;
770 advertising_proxy_conn_ref cref = NULL;
771 (void)context;
772
773 if (err == kDNSSDAdvertisingProxyStatus_NoError && flush_entries) {
774 err = advertising_proxy_flush_entries(&cref, main_queue, flushed_callback);
775 }
776 if (err == kDNSSDAdvertisingProxyStatus_NoError && (do_tcp_zero_test ||
777 do_tcp_fin_length || do_tcp_fin_payload)) {
778 err = start_tcp_test();
779 }
780 if (err == kDNSSDAdvertisingProxyStatus_NoError && list_services) {
781 err = advertising_proxy_get_service_list(&cref, main_queue, services_callback);
782 }
783 if (err == kDNSSDAdvertisingProxyStatus_NoError && block) {
784 err = advertising_proxy_block_service(&cref, main_queue, block_callback);
785 }
786 if (err == kDNSSDAdvertisingProxyStatus_NoError && unblock) {
787 err = advertising_proxy_unblock_service(&cref, main_queue, unblock_callback);
788 }
789 if (err == kDNSSDAdvertisingProxyStatus_NoError && regenerate_ula) {
790 err = advertising_proxy_regenerate_ula(&cref, main_queue, regenerate_callback);
791 }
792 if (err == kDNSSDAdvertisingProxyStatus_NoError && adv_prefix) {
793 err = advertising_proxy_advertise_prefix(&cref, adv_prefix_high, main_queue, prefix_advertise_callback);
794 }
795 if (err == kDNSSDAdvertisingProxyStatus_NoError && stop_proxy) {
796 err = advertising_proxy_stop(&cref, main_queue, stop_callback);
797 }
798 if (err == kDNSSDAdvertisingProxyStatus_NoError && get_ula) {
799 err = advertising_proxy_get_ula(&cref, main_queue, ula_callback);
800 }
801 if (err == kDNSSDAdvertisingProxyStatus_NoError && disable_srp_replication) {
802 err = advertising_proxy_disable_srp_replication(&cref, main_queue, disable_srp_replication_callback);
803 }
804 if (err == kDNSSDAdvertisingProxyStatus_NoError && add_thread_prefix) {
805 err = advertising_proxy_add_prefix(&cref, main_queue, add_prefix_callback, prefix_buf, sizeof(prefix_buf));
806 }
807 if (err == kDNSSDAdvertisingProxyStatus_NoError && remove_thread_prefix) {
808 err = advertising_proxy_remove_prefix(&cref, main_queue, remove_prefix_callback, prefix_buf, sizeof(prefix_buf));
809 }
810 if (err == kDNSSDAdvertisingProxyStatus_NoError && add_nat64_prefix) {
811 err = advertising_proxy_add_nat64_prefix(&cref, main_queue, add_nat64_prefix_callback, prefix_buf, sizeof(prefix_buf));
812 }
813 if (err == kDNSSDAdvertisingProxyStatus_NoError && remove_nat64_prefix) {
814 err = advertising_proxy_remove_nat64_prefix(&cref, main_queue, remove_nat64_prefix_callback, prefix_buf, sizeof(prefix_buf));
815 }
816 if (err == kDNSSDAdvertisingProxyStatus_NoError && drop_srpl_connection) {
817 err = advertising_proxy_drop_srpl_connection(&cref, main_queue, drop_srpl_connection_callback);
818 }
819 if (err == kDNSSDAdvertisingProxyStatus_NoError && undrop_srpl_connection) {
820 err = advertising_proxy_undrop_srpl_connection(&cref, main_queue, undrop_srpl_connection_callback);
821 }
822 if (err == kDNSSDAdvertisingProxyStatus_NoError && drop_srpl_advertisement) {
823 err = advertising_proxy_drop_srpl_advertisement(&cref, main_queue, drop_srpl_advertisement_callback);
824 }
825 if (err == kDNSSDAdvertisingProxyStatus_NoError && undrop_srpl_advertisement) {
826 err = advertising_proxy_undrop_srpl_advertisement(&cref, main_queue, undrop_srpl_advertisement_callback);
827 }
828 if (err == kDNSSDAdvertisingProxyStatus_NoError && start_dropping_push_connections) {
829 err = advertising_proxy_start_dropping_push_connections(&cref, main_queue, start_dropping_push_connections_callback);
830 }
831 if (err == kDNSSDAdvertisingProxyStatus_NoError && start_breaking_time_validation) {
832 err = advertising_proxy_start_breaking_time_validation(&cref, main_queue, start_breaking_time_validation_callback);
833 }
834 if (err == kDNSSDAdvertisingProxyStatus_NoError && block_anycast_service) {
835 err = advertising_proxy_block_anycast_service(&cref, main_queue, block_anycast_service_callback);
836 }
837 if (err == kDNSSDAdvertisingProxyStatus_NoError && unblock_anycast_service) {
838 err = advertising_proxy_unblock_anycast_service(&cref, main_queue, unblock_anycast_service_callback);
839 }
840 if (err == kDNSSDAdvertisingProxyStatus_NoError && start_thread_shutdown) {
841 err = advertising_proxy_start_thread_shutdown(&cref, main_queue, start_thread_shutdown_callback);
842 }
843 if (err == kDNSSDAdvertisingProxyStatus_NoError && test_route_tracker) {
844 route_tracker_test_start(1000);
845 }
846 if (err == kDNSSDAdvertisingProxyStatus_NoError && advertise_winning_unicast_service) {
847 start_advertising_winning_unicast_service();
848 }
849 if (err == kDNSSDAdvertisingProxyStatus_NoError && remove_unicast_service) {
850 start_removing_unicast_service();
851 }
852 if (err == kDNSSDAdvertisingProxyStatus_NoError && advertise_winning_unicast_service) {
853 start_advertising_winning_unicast_service();
854 }
855 if (err == kDNSSDAdvertisingProxyStatus_NoError && browse_service != NULL) {
856 start_browsing_service();
857 }
858 if (err == kDNSSDAdvertisingProxyStatus_NoError && resolve_service != NULL) {
859 start_resolving_service();
860 }
861 if (err == kDNSSDAdvertisingProxyStatus_NoError && need_service != NULL && need_name == NULL) {
862 start_needing_service();
863 }
864 if (err == kDNSSDAdvertisingProxyStatus_NoError && need_service != NULL && need_name != NULL) {
865 start_needing_instance();
866 }
867 if (err == kDNSSDAdvertisingProxyStatus_NoError && start_srp) {
868 start_registrar();
869 }
870 if (err == kDNSSDAdvertisingProxyStatus_NoError && variables != NULL) {
871 for (variable_t *variable = variables; variable != NULL; variable = variable->next) {
872 err = advertising_proxy_set_variable(&cref, main_queue, set_variable_callback, variable, variable->name, variable->value);
873 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
874 break;
875 }
876 }
877 }
878 if (err != kDNSSDAdvertisingProxyStatus_NoError) {
879 exit(1);
880 }
881 }
882
883 static void
884 dump_packet(void)
885 {
886 ssize_t len;
887 dns_message_t *message = NULL;
888 dns_wire_t wire;
889
890 len = read(0, &wire, sizeof(wire));
891 if (len < 0) {
892 ERROR("stdin: %s", strerror(errno));
893 return;
894 }
895 if (len < DNS_HEADER_SIZE) {
896 ERROR("stdin: too short: %zd bytes", len);
897 return;
898 }
899 if (!dns_wire_parse(&message, &wire, (unsigned)len, true)) {
900 fprintf(stderr, "DNS message parse failed\n");
901 return;
902 }
903 }
904
905 int
906 main(int argc, char **argv)
907 {
908 int i;
909 bool something = false;
910 bool log_stderr = false;
911
912 for (i = 1; i < argc; i++) {
913 if (!strcmp(argv[i], "start")) {
914 start_proxy = true;
915 something = true;
916 int j;
917 for (j = i + 1; j < argc; j++) {
918 if (!strcmp(argv[j], "-")) {
919 break;
920 }
921 }
922 num_permitted_interfaces = j - i - 1;
923 permitted_interfaces = argv + i + 1;
924 i = j;
925 } else if (!strcmp(argv[i], "tcp-zero")) {
926 do_tcp_zero_test = true;
927 something = true;
928 } else if (!strcmp(argv[i], "tcp-fin-length")) {
929 do_tcp_fin_length = true;
930 something = true;
931 } else if (!strcmp(argv[i], "tcp-fin-payload")) {
932 do_tcp_fin_payload = true;
933 something = true;
934 } else if (!strcmp(argv[i], "flush")) {
935 flush_entries = true;
936 something = true;
937 } else if (!strcmp(argv[i], "services")) {
938 list_services = true;
939 something = true;
940 } else if (!strcmp(argv[i], "block")) {
941 block = true;
942 something = true;
943 } else if (!strcmp(argv[i], "unblock")) {
944 unblock = true;
945 something = true;
946 } else if (!strcmp(argv[i], "regenerate-ula")) {
947 regenerate_ula = true;
948 something = true;
949 } else if (!strcmp(argv[i], "adv-prefix")) {
950 adv_prefix = true;
951 something = true;
952 } else if (!strcmp(argv[i], "adv-prefix-high")) {
953 adv_prefix = true;
954 adv_prefix_high = true;
955 something = true;
956 } else if (!strcmp(argv[i], "stop")) {
957 stop_proxy = true;
958 something = true;
959 } else if (!strcmp(argv[i], "dump")) {
960 dump_packet();
961 exit(0);
962 } else if (!strcmp(argv[i], "get-ula")) {
963 get_ula = true;
964 something = true;
965 } else if (!strcmp(argv[i], "disable-srpl")) {
966 disable_srp_replication = true;
967 something = true;
968 } else if (!strcmp(argv[i], "add-prefix")) {
969 if (i + 1 >= argc) {
970 usage();
971 }
972 if (inet_pton(AF_INET6, argv[i + 1], prefix_buf) < 1) {
973 fprintf(stderr, "Invalid ipv6 prefix %s.\n", argv[i + 1]);
974 usage();
975 } else {
976 add_thread_prefix = true;
977 something = true;
978 i++;
979 }
980 } else if (!strcmp(argv[i], "remove-prefix")) {
981 if (i + 1 >= argc) {
982 usage();
983 }
984 if (inet_pton(AF_INET6, argv[i + 1], prefix_buf) < 1) {
985 fprintf(stderr, "Invalid ipv6 prefix %s.\n", argv[i + 1]);
986 usage();
987 } else {
988 remove_thread_prefix = true;
989 something = true;
990 i++;
991 }
992 } else if (!strcmp(argv[i], "add-nat64-prefix")) {
993 if (i + 1 >= argc) {
994 usage();
995 }
996 if (inet_pton(AF_INET6, argv[i + 1], prefix_buf) < 1) {
997 fprintf(stderr, "Invalid ipv6 prefix %s.\n", argv[i + 1]);
998 usage();
999 } else {
1000 add_nat64_prefix = true;
1001 something = true;
1002 i++;
1003 }
1004 } else if (!strcmp(argv[i], "remove-nat64-prefix")) {
1005 if (i + 1 >= argc) {
1006 usage();
1007 }
1008 if (inet_pton(AF_INET6, argv[i + 1], prefix_buf) < 1) {
1009 fprintf(stderr, "Invalid ipv6 prefix %s.\n", argv[i + 1]);
1010 usage();
1011 } else {
1012 remove_nat64_prefix = true;
1013 something = true;
1014 i++;
1015 }
1016 } else if (!strcmp(argv[i], "browse")) {
1017 if (i + 1 >= argc) {
1018 usage();
1019 }
1020 browse_service = argv[i + 1];
1021 i++;
1022 something = true;
1023 } else if (!strcmp(argv[i], "resolve")) {
1024 if (i + 2 >= argc) {
1025 usage();
1026 }
1027 resolve_name = argv[i + 1];
1028 resolve_service = argv[i + 2];
1029 i += 2;
1030 something = true;
1031 } else if (!strcmp(argv[i], "need-service")) {
1032 if (i + 2 >= argc) {
1033 usage();
1034 }
1035 need_service = argv[i + 1];
1036 if (!strcmp(argv[i + 2], "true")) {
1037 needed_flag = true;
1038 } else {
1039 needed_flag = false;
1040 }
1041 i += 2;
1042 something = true;
1043 } else if (!strcmp(argv[i], "need-instance")) {
1044 if (i + 3 >= argc) {
1045 usage();
1046 }
1047 need_name = argv[i + 1];
1048 need_service = argv[i + 2];
1049 if (!strcmp(argv[i + 3], "true")) {
1050 needed_flag = true;
1051 } else {
1052 needed_flag = false;
1053 }
1054 i += 3;
1055 something = true;
1056 } else if (!strcmp(argv[i], "start-srp")) {
1057 start_srp = true;
1058 something = true;
1059 } else if (!strcmp(argv[i], "drop-srpl-connection")) {
1060 drop_srpl_connection = true;
1061 something = true;
1062 } else if (!strcmp(argv[i], "undrop-srpl-connection")) {
1063 undrop_srpl_connection = true;
1064 something = true;
1065 } else if (!strcmp(argv[i], "drop-srpl-advertisement")) {
1066 drop_srpl_advertisement = true;
1067 something = true;
1068 } else if (!strcmp(argv[i], "undrop-srpl-advertisement")) {
1069 undrop_srpl_advertisement = true;
1070 something = true;
1071 } else if (!strcmp(argv[i], "start-dropping-push")) {
1072 start_dropping_push_connections = true;
1073 something = true;
1074 } else if (!strcmp(argv[i], "start-breaking-time")) {
1075 start_breaking_time_validation = true;
1076 something = true;
1077 } else if (!strcmp(argv[i], "block-anycast-service")) {
1078 block_anycast_service = true;
1079 something = true;
1080 } else if (!strcmp(argv[i], "unblock-anycast-service")) {
1081 unblock_anycast_service = true;
1082 something = true;
1083 } else if (!strcmp(argv[i], "test-route-tracker")) {
1084 test_route_tracker = true;
1085 something = true;
1086 } else if (!strcmp(argv[i], "start-thread-shutdown")) {
1087 start_thread_shutdown = true;
1088 something = true;
1089 } else if (!strcmp(argv[i], "advertise-winning-unicast-service")) {
1090 advertise_winning_unicast_service = true;
1091 something = true;
1092 } else if (!strcmp(argv[i], "remove-unicast-service")) {
1093 remove_unicast_service = true;
1094 something = true;
1095 } else if (!strcmp(argv[i], "set")) {
1096 if (i + 2 >= argc) {
1097 usage();
1098 }
1099 variable_t *variable = calloc(1, sizeof(*variable));
1100 if (variable == NULL) {
1101 fprintf(stderr, "no memory for variable %s", argv[i + 1]);
1102 exit(1);
1103 }
1104 variable->name = argv[i + 1];
1105 variable->value = argv[i + 2];
1106 variable->next = variables;
1107 variables = variable;
1108 i += 2;
1109 something = true;
1110 #ifdef NOTYET
1111 } else if (!strcmp(argv[i], "watch")) {
1112 fprintf(stderr, "Watching not implemented yet.\n");
1113 exit(1);
1114 } else if (!strcmp(argv[i], "get")) {
1115 fprintf(stderr, "Getting not implemented yet.\n");
1116 exit(1);
1117 #endif
1118 } else if (!strcmp(argv[i], "--debug")) {
1119 OPENLOG("srputil", true);
1120 log_stderr = true;
1121 } else {
1122 usage();
1123 exit(1);
1124 }
1125 }
1126
1127 if (!something) {
1128 usage();
1129 exit(1);
1130 }
1131
1132 if (log_stderr == false) {
1133 OPENLOG("srputil", log_stderr);
1134 }
1135
1136 ioloop_init();
1137 // Start the queue, //then// do the work
1138 ioloop_run_async(start_activities, NULL);
1139 ioloop();
1140 }
1141
1142 // Local Variables:
1143 // mode: C
1144 // tab-width: 4
1145 // c-file-style: "bsd"
1146 // c-basic-offset: 4
1147 // fill-column: 108
1148 // indent-tabs-mode: nil
1149 // End:
1150