/* srputil.c * * Copyright (c) 2020-2024 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * SRP Advertising Proxy utility program, allows: * start/stop advertising proxy * get/track list of service types * get/track list of services of a particular type * get/track list of hosts * get/track information about a particular host */ #include #include #include #include #include #include #include #include #include #include #include #include #include void *main_queue = NULL; #include "srp.h" #include "dns-msg.h" #include "ioloop.h" #include "advertising_proxy_services.h" #include "route-tracker.h" #include "state-machine.h" #include "thread-service.h" #include "service-tracker.h" #include "probe-srp.h" #include "cti-services.h" #include "adv-ctl-server.h" static void flushed_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("flushed: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } // We don't need to wait around after flushing. exit(0); } static void block_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("blocked: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } // We don't need to wait around after blocking. exit(0); } static void unblock_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("unblocked: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } // We don't need to wait around after unblocking. exit(0); } static void regenerate_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("regenerated ula: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } // We don't need to wait around after unblocking. exit(0); } static void prefix_advertise_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("advertise prefix: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } // We don't need to wait around after advertising prefix. exit(0); } static void add_prefix_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("add prefix: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } // We don't need to wait around after advertising prefix. exit(0); } static void remove_prefix_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("remove prefix: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } // We don't need to wait around after advertising prefix. exit(0); } static void add_nat64_prefix_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("add nat64 prefix: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } exit(0); } static void remove_nat64_prefix_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("remove nat64 prefix: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } exit(0); } static void stop_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("stopped: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } // We don't need to wait around after stopping. exit(0); } static const char * print_address(advertising_proxy_host_address_t *address, char *addrbuf, size_t addrbuf_size) { if (address->rrtype == 0) { return (char *)address->rdata; } else if (address->rrtype == dns_rrtype_a && address->rdlen == 4) { inet_ntop(AF_INET, address->rdata, addrbuf, (socklen_t)addrbuf_size); return addrbuf; } else if (address->rrtype == dns_rrtype_aaaa && address->rdlen == 16) { inet_ntop(AF_INET6, address->rdata, addrbuf, (socklen_t)addrbuf_size); return addrbuf; } else { sprintf(addrbuf, "Family-%d", address->rrtype); return addrbuf; } } static void services_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { int i; int64_t lease, hours, minutes, seconds; advertising_proxy_host_t *host = result; const char *address = ""; char *addrbuf = NULL; size_t addrbuflen; uint64_t ula; if (err != kDNSSDAdvertisingProxyStatus_NoError) { INFO("services: cref %p response %p err %d.", cref, result, err); exit(1); } if (result == NULL) { INFO("services: cref %p response %p err %d.", cref, result, err); exit(0); } if (host->num_instances == 0) { i = -1; } else { i = 0; } for (; i < host->num_instances; i++) { const char *instance_name, *service_type, *reg_type; char port[6]; // uint16_t as ascii if (i == -1 || host->instances[i].instance_name == NULL) { instance_name = ""; service_type = ""; port[0] = 0; reg_type = ""; } else { instance_name = host->instances[i].instance_name; service_type = host->instances[i].service_type; snprintf(port, sizeof(port), "%u", host->instances[i].port); reg_type = host->instances[i].reg_type; } if (host->num_addresses > 0) { addrbuflen = host->num_addresses * (INET6_ADDRSTRLEN + 1); addrbuf = malloc(addrbuflen); if (addrbuf == NULL) { address = ""; } else { char *ap = addrbuf; for (int j = 0; j < host->num_addresses; j++) { *ap++ = ' '; address = print_address(&host->addresses[j], ap, addrbuflen - (ap - addrbuf)); size_t len = strlen(address); if (address != ap) { if (len + ap + 1 > addrbuf + addrbuflen) { len = addrbuflen - (ap - addrbuf) - 1; } memcpy(ap, address, len + 1); // Includes NUL } ap += len; } address = addrbuf; } } lease = host->lease_time; hours = lease / 3600 / 1000; lease -= hours * 3600 * 1000; minutes = lease / 60 / 1000; lease -= minutes * 60 * 1000; seconds = lease / 1000; lease -= seconds * 1000; // Our implementation of the stable server ID uses the server ULA, so just copy out those 48 bits, // which are in network byte order. ula = 0; for (int j = 1; j < 6; j++) { ula = ula << 8 | (((uint8_t *)&host->server_id)[j]); } printf("\"%s\" \"%s\" %s %s %s %" PRIu64 ":%" PRIu64 ":%" PRIu64 ".%" PRIu64 " \"%s\" \"%s\" %s %" PRIx64 "\n", host->regname, instance_name, service_type, port, address == NULL ? "" : address, hours, minutes, seconds, lease, host->hostname, reg_type, host->removed ? "invalid" : "valid", ula); if (addrbuf != NULL) { free(addrbuf); } } } static void ula_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("get_ula: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { fprintf(stderr, "ULA get failed: %d\n", err); exit(1); } uint64_t ula = *((uint64_t *)result); printf("ULA: %" PRIx64 "\n", ula); exit(0); } static void disable_srp_replication_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("disable_srp_replication: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } // We don't need to wait around after SRP replication disabled. exit(0); } static void drop_srpl_connection_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("drop_srpl_connection: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } exit(0); } static void undrop_srpl_connection_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("undrop_srpl_connection: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } exit(0); } static void drop_srpl_advertisement_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("drop_srpl_advertisement: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } exit(0); } static void undrop_srpl_advertisement_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("undrop_srpl_advertisement: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } exit(0); } static void start_dropping_push_connections_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("start_dropping_push_connections: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } exit(0); } static void start_breaking_time_validation_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("start_breaking_time_validation: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } exit(0); } static void block_anycast_service_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("block_anycast_service: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } exit(0); } static void unblock_anycast_service_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("unblock_anycast_service: cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } exit(0); } static void start_thread_shutdown_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err) { INFO("cref %p response %p err %d.", cref, result, err); if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } exit(0); } typedef struct variable variable_t; struct variable { variable_t *next; const char *name, *value; }; static void set_variable_callback(advertising_proxy_conn_ref cref, void *context, void *result, advertising_proxy_error_type err) { variable_t *variable = context; INFO("set_variable: cref %p response %p err %d, variable name %s, value %s.", cref, result, err, variable->name, variable->value); if (err != kDNSSDAdvertisingProxyStatus_NoError) { if (variable->next == NULL) { exit(1); } } if (variable->next == NULL) { exit(0); } } static comm_t *tcp_connection; bool do_tcp_zero_test = false; bool do_tcp_fin_length = false; bool do_tcp_fin_payload = false; service_tracker_t *tracker; // Dummy functions required to use service tracker here void adv_ctl_thread_shutdown_status_check(srp_server_t *UNUSED server_state) { } static void service_done_callback(void *context, cti_status_t status) { const char *action = context; if (status != kCTIStatus_NoError) { fprintf(stderr, PUB_S_SRP " failed, status %d", action, status); exit(1); } else { fprintf(stderr, PUB_S_SRP " done", action); exit(0); } } static void service_set_changed(bool unicast) { thread_service_t *winner = NULL; for (thread_service_t *service = service_tracker_services_get(tracker); service != NULL; service = service->next) { if (service->ignore) { continue; } if (unicast && service->service_type == unicast_service) { if (winner == NULL || in6addr_compare(&service->u.unicast.address, &winner->u.unicast.address) < 0) { winner = service; } } } if (winner == NULL) { fprintf(stderr, "no services present!"); exit(1); } winner->u.unicast.address.s6_addr[15] = 0; uint8_t service_data[1] = { THREAD_SRP_SERVER_OPTION }; uint8_t server_data[18]; memcpy(server_data, &winner->u.unicast.address, 15); server_data[15] = 0; server_data[16] = winner->u.unicast.port[0]; server_data[17] = winner->u.unicast.port[1]; int ret = cti_add_service(NULL, "add", service_done_callback, NULL, THREAD_ENTERPRISE_NUMBER, service_data, 1, server_data, 18); if (ret != kCTIStatus_NoError) { fprintf(stderr, "add_service failed: %d", ret); exit(1); } } static void unicast_service_set_changed(void *UNUSED context) { service_set_changed(true); } static void start_advertising_winning_unicast_service(void) { tracker = service_tracker_create(NULL); if (tracker != NULL) { service_tracker_callback_add(tracker, unicast_service_set_changed, NULL, NULL); service_tracker_start(tracker); } else { fprintf(stderr, "unable to allocate tracker"); exit(1); } } static void start_removing_unicast_service(void) { uint8_t service_data[1] = { THREAD_SRP_SERVER_OPTION }; int ret = cti_remove_service(NULL, "remove", service_done_callback, NULL, THREAD_ENTERPRISE_NUMBER, service_data, 1); if (ret != kCTIStatus_NoError) { fprintf(stderr, "remove_service failed: %d", ret); exit(1); } } static void tcp_datagram_callback(comm_t *NONNULL comm, message_t *NONNULL message, void *NULLABLE context) { (void)comm; (void)context; fprintf(stderr, "tcp datagram received, length %d", message->length); } static void tcp_connect_callback(comm_t *NONNULL connection, void *NULLABLE context) { fprintf(stderr, "tcp connection succeeded...\n"); uint8_t length[2]; struct iovec iov[2]; char databuf[128]; memset(databuf, 0, sizeof(databuf)); memset(iov, 0, sizeof(iov)); (void)context; if (do_tcp_zero_test) { memset(length, 0, sizeof(length)); iov[0].iov_len = 2; iov[0].iov_base = length; ioloop_send_data(connection, NULL, iov, 1); } else if (do_tcp_fin_length) { memset(length, 0, sizeof(length)); iov[0].iov_len = 1; iov[0].iov_base = &length; ioloop_send_final_data(connection, NULL, iov, 1); } else if (do_tcp_fin_payload) { length[0] = 0; length[1] = 255; iov[0].iov_len = 2; iov[0].iov_base = length; iov[1].iov_len = 128; iov[1].iov_base = databuf; ioloop_send_final_data(connection, NULL, iov, 2); } } static void tcp_disconnect_callback(comm_t *NONNULL comm, void *NULLABLE context, int error) { (void)comm; (void)context; (void)error; fprintf(stderr, "tcp remote close.\n"); exit(0); } static int start_tcp_test(void) { addr_t address; memset(&address, 0, sizeof(address)); address.sa.sa_family = AF_INET; address.sin.sin_addr.s_addr = htonl(0x7f000001); address.sin.sin_port = htons(53); #ifndef NOT_HAVE_SA_LEN address.sin.sin_len = sizeof(address.sin); #endif tcp_connection = ioloop_connection_create(&address, false, true, false, false, tcp_datagram_callback, tcp_connect_callback, tcp_disconnect_callback, NULL, NULL); if (tcp_connection == NULL) { return kDNSSDAdvertisingProxyStatus_NoMemory; } return kDNSSDAdvertisingProxyStatus_NoError; } const char *need_name, *need_service; bool needed_flag; advertising_proxy_conn_ref service_sub; static void service_callback(advertising_proxy_conn_ref UNUSED sub, void *UNUSED context, advertising_proxy_error_type error) { fprintf(stderr, "service callback: %d\n", error); exit(0); } static void start_needing_service(void) { advertising_proxy_error_type ret = advertising_proxy_set_service_needed(&service_sub, dispatch_get_main_queue(), service_callback, NULL, NULL, need_service, needed_flag); if (ret != kDNSSDAdvertisingProxyStatus_NoError) { fprintf(stderr, "advertising_proxy_service_create failed: %d\n", ret); exit(1); } } advertising_proxy_conn_ref instance_sub; static void instance_callback(advertising_proxy_conn_ref UNUSED sub, void *UNUSED context, advertising_proxy_error_type error) { fprintf(stderr, "instance callback: %d\n", error); exit(0); } static void start_needing_instance(void) { advertising_proxy_error_type ret = advertising_proxy_set_service_needed(&instance_sub, dispatch_get_main_queue(), instance_callback, NULL, need_name, need_service, needed_flag); if (ret != kDNSSDAdvertisingProxyStatus_NoError) { fprintf(stderr, "advertising_proxy_set_service_needed failed: %d\n", ret); exit(1); } } const char *browse_service, *resolve_name, *resolve_service; advertising_proxy_subscription_t *browse_sub; static void browse_callback(advertising_proxy_subscription_t *UNUSED sub, advertising_proxy_error_type error, uint32_t interface_index, bool add, const char *instance_name, const char *service_type, void *UNUSED context) { if (error != kDNSSDAdvertisingProxyStatus_NoError) { fprintf(stderr, "browse_callback: %d\n", error); exit(1); } fprintf(stderr, "browse: %d %s %s %s\n", interface_index, add ? "add" : "rmv", instance_name, service_type); } static void start_browsing_service(void) { advertising_proxy_error_type ret = advertising_proxy_browse_create(&browse_sub, dispatch_get_main_queue(), browse_service, browse_callback, NULL); if (ret != kDNSSDAdvertisingProxyStatus_NoError) { fprintf(stderr, "advertising_proxy_browse_create failed: %d\n", ret); exit(1); } } advertising_proxy_subscription_t *resolve_sub; static void resolve_callback(advertising_proxy_subscription_t *UNUSED sub, advertising_proxy_error_type error, uint32_t interface_index, bool add, const char *fullname, const char *hostname, uint16_t port, uint16_t txt_length, const uint8_t *UNUSED txt_record, void *UNUSED context) { if (error != kDNSSDAdvertisingProxyStatus_NoError) { fprintf(stderr, "resolve_create callback: %d\n", error); exit(1); } fprintf(stderr, "resolved: %d %s %s %s %d %d\n", interface_index, add ? "add" : "rmv", fullname, hostname, port, txt_length); } static void start_resolving_service(void) { advertising_proxy_error_type ret = advertising_proxy_resolve_create(&resolve_sub, dispatch_get_main_queue(), resolve_name, resolve_service, NULL, resolve_callback, NULL); if (ret != kDNSSDAdvertisingProxyStatus_NoError) { fprintf(stderr, "advertising_proxy_resolve_create failed: %d\n", ret); exit(1); } } advertising_proxy_subscription_t *registrar_sub; static void registrar_callback(advertising_proxy_subscription_t *UNUSED sub, advertising_proxy_error_type error, void *UNUSED context) { if (error != kDNSSDAdvertisingProxyStatus_NoError) { fprintf(stderr, "registrar_callback: %d\n", error); exit(1); } INFO("SRP registrar is enabled."); } static void start_registrar(void) { advertising_proxy_error_type ret = advertising_proxy_registrar_create(®istrar_sub, dispatch_get_main_queue(), registrar_callback, NULL); if (ret != kDNSSDAdvertisingProxyStatus_NoError) { fprintf(stderr, "advertising_proxy_registrar_create failed: %d", ret); exit(1); } } static void usage(void) { fprintf(stderr, "srputil start [if1 .. ifN -] -- start the SRP MDNS Proxy through launchd\n"); fprintf(stderr, " tcp-zero -- connect to port 53, send a DNS message that's got a zero-length payload\n"); fprintf(stderr, " tcp-fin-length -- connect to port 53, send a DNS message that ends before length is complete\n"); fprintf(stderr, " tcp-fin-payload -- connect to port 53, send a DNS message that ends before payload is complete\n"); fprintf(stderr, " services -- get the list of services currently being advertised\n"); fprintf(stderr, " block -- block the SRP listener\n"); fprintf(stderr, " unblock -- unblock the SRP listener\n"); fprintf(stderr, " regenerate-ula -- generate a new ULA and restart the network\n"); fprintf(stderr, " adv-prefix-high -- advertise high-priority prefix to thread network\n"); fprintf(stderr, " adv-prefix -- advertise prefix to thread network\n"); fprintf(stderr, " stop -- stop advertising as SRP server\n"); fprintf(stderr, " get-ula -- fetch the current ULA prefix configured on the SRP server\n"); fprintf(stderr, " disable-srpl -- disable SRP replication\n"); fprintf(stderr, " add-prefix -- add an OMR prefix\n"); fprintf(stderr, " remove-prefix -- add an nat64 prefix\n"); fprintf(stderr, " remove-nat64-prefix -- remove an nat64 prefix\n"); fprintf(stderr, " drop-srpl-connection -- drop existing srp replication connections\n"); fprintf(stderr, " undrop-srpl-connection -- restart srp replication connections that were dropped \n"); fprintf(stderr, " drop-srpl-advertisement -- stop advertising srpl service (but keep it around)\n"); fprintf(stderr, " undrop-srpl-advertisement -- resume advertising srpl service\n"); fprintf(stderr, " start-dropping-push -- start repeatedly dropping any active push connections after 90 seconds\n"); fprintf(stderr, " start-breaking-time -- start breaking time validation on replicated SRP registrations\n"); fprintf(stderr, " set [variable] [value] -- set the value of variable to value (e.g. set min-lease-time 100)\n"); fprintf(stderr, " block-anycast-service -- block advertising anycast service\n"); fprintf(stderr, " unblock-anycast-service -- unblock advertising anycast service\n"); fprintf(stderr, " start-thread-shutdown -- start thread network shutdown\n"); fprintf(stderr, " advertise-winning-unicast-service -- advertise a unicast service that wins over the current service\n"); fprintf(stderr, " browse -- start an advertising_proxy_browse on the specified service\n"); fprintf(stderr, " resolve -- start an advertising_proxy_resolve on the specified service instance\n"); fprintf(stderr, " need-service -- signal to srp-mdns-proxy that we need to discover a service\n"); fprintf(stderr, " need-instance -- signal to srp-mdns-proxy that we need to discover a service\n"); fprintf(stderr, " start-srp -- on thread device, enable srp registration\n"); #ifdef NOTYET fprintf(stderr, " flush -- flush all entries from the SRP proxy (for testing only)\n"); #endif } bool start_proxy = false; bool flush_entries = false; bool list_services = false; bool block = false; bool unblock = false; bool regenerate_ula = false; bool adv_prefix = false; bool adv_prefix_high = false; bool stop_proxy = false; bool dump_stdin = false; bool get_ula = false; bool disable_srp_replication = false; bool dso_test = false; bool drop_srpl_connection; bool undrop_srpl_connection; bool drop_srpl_advertisement; bool undrop_srpl_advertisement; bool start_dropping_push_connections; bool add_thread_prefix = false; bool remove_thread_prefix = false; bool add_nat64_prefix = false; bool remove_nat64_prefix = false; bool start_breaking_time_validation = false; bool test_route_tracker = false; bool block_anycast_service = false; bool unblock_anycast_service = false; bool start_thread_shutdown = false; bool advertise_winning_unicast_service = false; bool remove_unicast_service = false; bool start_srp = false; uint8_t prefix_buf[16]; #ifdef NOTYET bool watch = false; bool get = false; #endif variable_t *variables; int num_permitted_interfaces; char **permitted_interfaces; static void start_activities(void *context) { advertising_proxy_error_type err = kDNSSDAdvertisingProxyStatus_NoError;; advertising_proxy_conn_ref cref = NULL; (void)context; if (err == kDNSSDAdvertisingProxyStatus_NoError && flush_entries) { err = advertising_proxy_flush_entries(&cref, main_queue, flushed_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && (do_tcp_zero_test || do_tcp_fin_length || do_tcp_fin_payload)) { err = start_tcp_test(); } if (err == kDNSSDAdvertisingProxyStatus_NoError && list_services) { err = advertising_proxy_get_service_list(&cref, main_queue, services_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && block) { err = advertising_proxy_block_service(&cref, main_queue, block_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && unblock) { err = advertising_proxy_unblock_service(&cref, main_queue, unblock_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && regenerate_ula) { err = advertising_proxy_regenerate_ula(&cref, main_queue, regenerate_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && adv_prefix) { err = advertising_proxy_advertise_prefix(&cref, adv_prefix_high, main_queue, prefix_advertise_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && stop_proxy) { err = advertising_proxy_stop(&cref, main_queue, stop_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && get_ula) { err = advertising_proxy_get_ula(&cref, main_queue, ula_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && disable_srp_replication) { err = advertising_proxy_disable_srp_replication(&cref, main_queue, disable_srp_replication_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && add_thread_prefix) { err = advertising_proxy_add_prefix(&cref, main_queue, add_prefix_callback, prefix_buf, sizeof(prefix_buf)); } if (err == kDNSSDAdvertisingProxyStatus_NoError && remove_thread_prefix) { err = advertising_proxy_remove_prefix(&cref, main_queue, remove_prefix_callback, prefix_buf, sizeof(prefix_buf)); } if (err == kDNSSDAdvertisingProxyStatus_NoError && add_nat64_prefix) { err = advertising_proxy_add_nat64_prefix(&cref, main_queue, add_nat64_prefix_callback, prefix_buf, sizeof(prefix_buf)); } if (err == kDNSSDAdvertisingProxyStatus_NoError && remove_nat64_prefix) { err = advertising_proxy_remove_nat64_prefix(&cref, main_queue, remove_nat64_prefix_callback, prefix_buf, sizeof(prefix_buf)); } if (err == kDNSSDAdvertisingProxyStatus_NoError && drop_srpl_connection) { err = advertising_proxy_drop_srpl_connection(&cref, main_queue, drop_srpl_connection_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && undrop_srpl_connection) { err = advertising_proxy_undrop_srpl_connection(&cref, main_queue, undrop_srpl_connection_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && drop_srpl_advertisement) { err = advertising_proxy_drop_srpl_advertisement(&cref, main_queue, drop_srpl_advertisement_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && undrop_srpl_advertisement) { err = advertising_proxy_undrop_srpl_advertisement(&cref, main_queue, undrop_srpl_advertisement_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && start_dropping_push_connections) { err = advertising_proxy_start_dropping_push_connections(&cref, main_queue, start_dropping_push_connections_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && start_breaking_time_validation) { err = advertising_proxy_start_breaking_time_validation(&cref, main_queue, start_breaking_time_validation_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && block_anycast_service) { err = advertising_proxy_block_anycast_service(&cref, main_queue, block_anycast_service_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && unblock_anycast_service) { err = advertising_proxy_unblock_anycast_service(&cref, main_queue, unblock_anycast_service_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && start_thread_shutdown) { err = advertising_proxy_start_thread_shutdown(&cref, main_queue, start_thread_shutdown_callback); } if (err == kDNSSDAdvertisingProxyStatus_NoError && test_route_tracker) { route_tracker_test_start(1000); } if (err == kDNSSDAdvertisingProxyStatus_NoError && advertise_winning_unicast_service) { start_advertising_winning_unicast_service(); } if (err == kDNSSDAdvertisingProxyStatus_NoError && remove_unicast_service) { start_removing_unicast_service(); } if (err == kDNSSDAdvertisingProxyStatus_NoError && advertise_winning_unicast_service) { start_advertising_winning_unicast_service(); } if (err == kDNSSDAdvertisingProxyStatus_NoError && browse_service != NULL) { start_browsing_service(); } if (err == kDNSSDAdvertisingProxyStatus_NoError && resolve_service != NULL) { start_resolving_service(); } if (err == kDNSSDAdvertisingProxyStatus_NoError && need_service != NULL && need_name == NULL) { start_needing_service(); } if (err == kDNSSDAdvertisingProxyStatus_NoError && need_service != NULL && need_name != NULL) { start_needing_instance(); } if (err == kDNSSDAdvertisingProxyStatus_NoError && start_srp) { start_registrar(); } if (err == kDNSSDAdvertisingProxyStatus_NoError && variables != NULL) { for (variable_t *variable = variables; variable != NULL; variable = variable->next) { err = advertising_proxy_set_variable(&cref, main_queue, set_variable_callback, variable, variable->name, variable->value); if (err != kDNSSDAdvertisingProxyStatus_NoError) { break; } } } if (err != kDNSSDAdvertisingProxyStatus_NoError) { exit(1); } } static void dump_packet(void) { ssize_t len; dns_message_t *message = NULL; dns_wire_t wire; len = read(0, &wire, sizeof(wire)); if (len < 0) { ERROR("stdin: %s", strerror(errno)); return; } if (len < DNS_HEADER_SIZE) { ERROR("stdin: too short: %zd bytes", len); return; } if (!dns_wire_parse(&message, &wire, (unsigned)len, true)) { fprintf(stderr, "DNS message parse failed\n"); return; } } int main(int argc, char **argv) { int i; bool something = false; bool log_stderr = false; for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "start")) { start_proxy = true; something = true; int j; for (j = i + 1; j < argc; j++) { if (!strcmp(argv[j], "-")) { break; } } num_permitted_interfaces = j - i - 1; permitted_interfaces = argv + i + 1; i = j; } else if (!strcmp(argv[i], "tcp-zero")) { do_tcp_zero_test = true; something = true; } else if (!strcmp(argv[i], "tcp-fin-length")) { do_tcp_fin_length = true; something = true; } else if (!strcmp(argv[i], "tcp-fin-payload")) { do_tcp_fin_payload = true; something = true; } else if (!strcmp(argv[i], "flush")) { flush_entries = true; something = true; } else if (!strcmp(argv[i], "services")) { list_services = true; something = true; } else if (!strcmp(argv[i], "block")) { block = true; something = true; } else if (!strcmp(argv[i], "unblock")) { unblock = true; something = true; } else if (!strcmp(argv[i], "regenerate-ula")) { regenerate_ula = true; something = true; } else if (!strcmp(argv[i], "adv-prefix")) { adv_prefix = true; something = true; } else if (!strcmp(argv[i], "adv-prefix-high")) { adv_prefix = true; adv_prefix_high = true; something = true; } else if (!strcmp(argv[i], "stop")) { stop_proxy = true; something = true; } else if (!strcmp(argv[i], "dump")) { dump_packet(); exit(0); } else if (!strcmp(argv[i], "get-ula")) { get_ula = true; something = true; } else if (!strcmp(argv[i], "disable-srpl")) { disable_srp_replication = true; something = true; } else if (!strcmp(argv[i], "add-prefix")) { if (i + 1 >= argc) { usage(); } if (inet_pton(AF_INET6, argv[i + 1], prefix_buf) < 1) { fprintf(stderr, "Invalid ipv6 prefix %s.\n", argv[i + 1]); usage(); } else { add_thread_prefix = true; something = true; i++; } } else if (!strcmp(argv[i], "remove-prefix")) { if (i + 1 >= argc) { usage(); } if (inet_pton(AF_INET6, argv[i + 1], prefix_buf) < 1) { fprintf(stderr, "Invalid ipv6 prefix %s.\n", argv[i + 1]); usage(); } else { remove_thread_prefix = true; something = true; i++; } } else if (!strcmp(argv[i], "add-nat64-prefix")) { if (i + 1 >= argc) { usage(); } if (inet_pton(AF_INET6, argv[i + 1], prefix_buf) < 1) { fprintf(stderr, "Invalid ipv6 prefix %s.\n", argv[i + 1]); usage(); } else { add_nat64_prefix = true; something = true; i++; } } else if (!strcmp(argv[i], "remove-nat64-prefix")) { if (i + 1 >= argc) { usage(); } if (inet_pton(AF_INET6, argv[i + 1], prefix_buf) < 1) { fprintf(stderr, "Invalid ipv6 prefix %s.\n", argv[i + 1]); usage(); } else { remove_nat64_prefix = true; something = true; i++; } } else if (!strcmp(argv[i], "browse")) { if (i + 1 >= argc) { usage(); } browse_service = argv[i + 1]; i++; something = true; } else if (!strcmp(argv[i], "resolve")) { if (i + 2 >= argc) { usage(); } resolve_name = argv[i + 1]; resolve_service = argv[i + 2]; i += 2; something = true; } else if (!strcmp(argv[i], "need-service")) { if (i + 2 >= argc) { usage(); } need_service = argv[i + 1]; if (!strcmp(argv[i + 2], "true")) { needed_flag = true; } else { needed_flag = false; } i += 2; something = true; } else if (!strcmp(argv[i], "need-instance")) { if (i + 3 >= argc) { usage(); } need_name = argv[i + 1]; need_service = argv[i + 2]; if (!strcmp(argv[i + 3], "true")) { needed_flag = true; } else { needed_flag = false; } i += 3; something = true; } else if (!strcmp(argv[i], "start-srp")) { start_srp = true; something = true; } else if (!strcmp(argv[i], "drop-srpl-connection")) { drop_srpl_connection = true; something = true; } else if (!strcmp(argv[i], "undrop-srpl-connection")) { undrop_srpl_connection = true; something = true; } else if (!strcmp(argv[i], "drop-srpl-advertisement")) { drop_srpl_advertisement = true; something = true; } else if (!strcmp(argv[i], "undrop-srpl-advertisement")) { undrop_srpl_advertisement = true; something = true; } else if (!strcmp(argv[i], "start-dropping-push")) { start_dropping_push_connections = true; something = true; } else if (!strcmp(argv[i], "start-breaking-time")) { start_breaking_time_validation = true; something = true; } else if (!strcmp(argv[i], "block-anycast-service")) { block_anycast_service = true; something = true; } else if (!strcmp(argv[i], "unblock-anycast-service")) { unblock_anycast_service = true; something = true; } else if (!strcmp(argv[i], "test-route-tracker")) { test_route_tracker = true; something = true; } else if (!strcmp(argv[i], "start-thread-shutdown")) { start_thread_shutdown = true; something = true; } else if (!strcmp(argv[i], "advertise-winning-unicast-service")) { advertise_winning_unicast_service = true; something = true; } else if (!strcmp(argv[i], "remove-unicast-service")) { remove_unicast_service = true; something = true; } else if (!strcmp(argv[i], "set")) { if (i + 2 >= argc) { usage(); } variable_t *variable = calloc(1, sizeof(*variable)); if (variable == NULL) { fprintf(stderr, "no memory for variable %s", argv[i + 1]); exit(1); } variable->name = argv[i + 1]; variable->value = argv[i + 2]; variable->next = variables; variables = variable; i += 2; something = true; #ifdef NOTYET } else if (!strcmp(argv[i], "watch")) { fprintf(stderr, "Watching not implemented yet.\n"); exit(1); } else if (!strcmp(argv[i], "get")) { fprintf(stderr, "Getting not implemented yet.\n"); exit(1); #endif } else if (!strcmp(argv[i], "--debug")) { OPENLOG("srputil", true); log_stderr = true; } else { usage(); exit(1); } } if (!something) { usage(); exit(1); } if (log_stderr == false) { OPENLOG("srputil", log_stderr); } ioloop_init(); // Start the queue, //then// do the work ioloop_run_async(start_activities, NULL); ioloop(); } // Local Variables: // mode: C // tab-width: 4 // c-file-style: "bsd" // c-basic-offset: 4 // fill-column: 108 // indent-tabs-mode: nil // End: