3 * Copyright (c) 2019 Apple Computer, Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 * srp host API implementation for Posix using ioloop primitives.
21 #include <arpa/inet.h>
32 #include "srp-crypto.h"
35 #include "cti-services.h"
36 #if !defined(OPEN_SOURCE) && defined(TARGET_OS_TV)
37 #define LIVE_TRANSACTION_CLEANUP 1
38 #include "advertising_proxy_services.h"
41 static int lease_time
= 0;
42 static bool random_leases
= false;
43 static bool delete_registrations
= false;
44 static bool use_thread_services
= false;
45 #ifdef LIVE_TRANSACTION_CLEANUP
46 static bool live_transaction_cleanup
= false;
47 static int live_transaction_cleanup_time
;
48 static wakeup_t
*live_transaction_wakeup
;
49 static advertising_proxy_conn_ref live_transaction_cref
;
51 static int num_clients
= 1;
52 static int bogusify_signatures
= false;
54 const uint64_t thread_enterprise_number
= 52627;
56 cti_connection_t thread_service_context
;
58 #define SRP_IO_CONTEXT_MAGIC 0xFEEDFACEFADEBEEFULL // BEES! Everybody gets BEES!
59 typedef struct io_context
{
60 uint64_t magic_cookie1
;
62 void *NONNULL srp_context
;
63 comm_t
*NULLABLE connection
;
64 srp_wakeup_callback_t wakeup_callback
;
65 srp_datagram_callback_t datagram_callback
;
66 uint64_t magic_cookie2
;
68 wakeup_t
*remove_wakeup
;
70 typedef struct srp_client
{
78 validate_io_context(io_context_t
**dest
, void *src
)
80 io_context_t
*context
= src
;
81 if (context
->magic_cookie1
== SRP_IO_CONTEXT_MAGIC
&&
82 context
->magic_cookie2
== SRP_IO_CONTEXT_MAGIC
)
85 return kDNSServiceErr_NoError
;
87 return kDNSServiceErr_BadState
;
91 datagram_callback(comm_t
*connection
, message_t
*message
, void *context
)
94 io_context_t
*io_context
= context
;
95 io_context
->datagram_callback(io_context
->srp_context
,
96 &message
->wire
, message
->length
);
100 wakeup_callback(void *context
)
102 io_context_t
*io_context
;
103 if (validate_io_context(&io_context
, context
) == kDNSServiceErr_NoError
) {
104 INFO("wakeup on context %p srp_context %p", io_context
, io_context
->srp_context
);
105 io_context
->wakeup_callback(io_context
->srp_context
);
107 INFO("wakeup with invalid context: %p", context
);
112 srp_deactivate_udp_context(void *host_context
, void *in_context
)
114 io_context_t
*io_context
;
118 err
= validate_io_context(&io_context
, in_context
);
119 if (err
== kDNSServiceErr_NoError
) {
120 if (io_context
->connection
) {
121 ioloop_comm_release(io_context
->connection
);
129 srp_disconnect_udp(void *context
)
131 io_context_t
*io_context
;
134 err
= validate_io_context(&io_context
, context
);
135 if (err
== kDNSServiceErr_NoError
) {
136 if (io_context
->connection
) {
137 ioloop_comm_cancel(io_context
->connection
);
138 ioloop_comm_release(io_context
->connection
);
139 io_context
->connection
= NULL
;
146 srp_connect_udp(void *context
, const uint8_t *port
, uint16_t address_type
, const uint8_t *address
, uint16_t addrlen
)
148 io_context_t
*io_context
;
152 err
= validate_io_context(&io_context
, context
);
153 if (err
== kDNSServiceErr_NoError
) {
154 if (io_context
->connection
) {
155 ERROR("srp_connect_udp called with non-null I/O context.");
156 return kDNSServiceErr_Invalid
;
159 if (address_type
== dns_rrtype_a
) {
161 return kDNSServiceErr_Invalid
;
163 remote
.sa
.sa_family
= AF_INET
;
164 memcpy(&remote
.sin
.sin_addr
, address
, addrlen
);
165 #ifndef NOT_HAVE_SA_LEN
166 remote
.sa
.sa_len
= sizeof remote
.sin
;
168 memcpy(&remote
.sin
.sin_port
, port
, 2);
171 return kDNSServiceErr_Invalid
;
173 remote
.sa
.sa_family
= AF_INET6
;
174 memcpy(&remote
.sin6
.sin6_addr
, address
, addrlen
);
175 #ifndef NOT_HAVE_SA_LEN
176 remote
.sa
.sa_len
= sizeof remote
.sin
;
178 memcpy(&remote
.sin6
.sin6_port
, port
, 2);
181 io_context
->connection
= ioloop_connection_create(&remote
, false, false, datagram_callback
,
182 NULL
, NULL
, NULL
, io_context
);
183 if (io_context
->connection
== NULL
) {
184 return kDNSServiceErr_NoMemory
;
191 srp_make_udp_context(void *host_context
, void **p_context
, srp_datagram_callback_t callback
, void *context
)
195 io_context_t
*io_context
= calloc(1, sizeof *io_context
);
196 if (io_context
== NULL
) {
197 return kDNSServiceErr_NoMemory
;
199 io_context
->magic_cookie1
= io_context
->magic_cookie2
= SRP_IO_CONTEXT_MAGIC
;
200 io_context
->datagram_callback
= callback
;
201 io_context
->srp_context
= context
;
203 io_context
->wakeup
= ioloop_wakeup_create();
204 if (io_context
->wakeup
== NULL
) {
206 return kDNSServiceErr_NoMemory
;
209 *p_context
= io_context
;
210 return kDNSServiceErr_NoError
;
214 srp_set_wakeup(void *host_context
, void *context
, int milliseconds
, srp_wakeup_callback_t callback
)
217 io_context_t
*io_context
;
220 err
= validate_io_context(&io_context
, context
);
221 if (err
== kDNSServiceErr_NoError
) {
222 io_context
->wakeup_callback
= callback
;
223 INFO("srp_set_wakeup on context %p, srp_context %p", io_context
, io_context
->srp_context
);
224 ioloop_add_wake_event(io_context
->wakeup
, io_context
, wakeup_callback
, NULL
, milliseconds
);
230 srp_cancel_wakeup(void *host_context
, void *context
)
233 io_context_t
*io_context
;
236 err
= validate_io_context(&io_context
, context
);
237 if (err
== kDNSServiceErr_NoError
) {
238 ioloop_cancel_wake_event(io_context
->wakeup
);
244 srp_send_datagram(void *host_context
, void *context
, void *message
, size_t message_length
)
248 io_context_t
*io_context
;
251 memset(&iov
, 0, sizeof iov
);
252 iov
.iov_base
= message
;
253 iov
.iov_len
= message_length
;
255 if (bogusify_signatures
) {
256 ((uint8_t *)message
)[message_length
- 10] = ~(((uint8_t *)message
)[message_length
- 10]);
259 err
= validate_io_context(&io_context
, context
);
260 if (err
== kDNSServiceErr_NoError
) {
261 if (!ioloop_send_message(io_context
->connection
, message
, &iov
, 1)) {
262 return kDNSServiceErr_Unknown
;
269 srp_load_file_data(void *host_context
, const char *filename
, uint8_t *buffer
, uint16_t *length
, uint16_t buffer_size
)
276 file
= open(filename
, O_RDONLY
);
278 ERROR("srp_load_file_data: %s: open: %s", filename
, strerror(errno
));
282 // Get the length of the file.
283 flen
= lseek(file
, 0, SEEK_END
);
284 lseek(file
, 0, SEEK_SET
);
285 if (flen
> buffer_size
) {
286 ERROR("srp_load_file_data: %s: lseek: %s", filename
, strerror(errno
));
290 len
= read(file
, buffer
, flen
);
291 if (len
< 0 || len
!= flen
) {
293 ERROR("srp_load_file_data: %s: read: %s", filename
, strerror(errno
));
295 ERROR("srp_load_file_data: %s: short read %d out of %d", filename
, (int)len
, (int)flen
);
301 *length
= (uint16_t)len
;
306 srp_store_file_data(void *host_context
, const char *filename
, uint8_t *buffer
, uint16_t length
)
311 file
= open(filename
, O_WRONLY
| O_CREAT
, 0600);
313 ERROR("srp_store_file_data: %s: %s", filename
, strerror(errno
));
316 len
= write(file
, buffer
, length
);
317 if (len
< 0 || len
!= length
) {
319 ERROR("srp_store_file_data: " PUB_S_SRP
": " PUB_S_SRP
, filename
, strerror(errno
));
321 ERROR("srp_store_file_data: short write %d out of %d on file %s", (int)len
, (int)length
, filename
);
325 return kDNSServiceErr_Unknown
;
328 return kDNSServiceErr_NoError
;
333 srp_get_last_server(uint16_t *NONNULL rrtype
, uint8_t *NONNULL rdata
, uint16_t rdlim
,
334 uint8_t *NONNULL port
, void *NULLABLE host_context
)
341 if (!srp_load_file_data(host_context
, "/var/run/srp-last-server", buffer
, &length
, sizeof(buffer
))) {
344 if (length
< 10) { // rrtype + rdlength + ipv4 address + port
345 ERROR("srp_get_last_server: stored server data is too short: %d", length
);
348 *rrtype
= (((uint16_t)buffer
[offset
]) << 8) | buffer
[offset
+ 1];
350 rdlength
= (((uint16_t)buffer
[offset
]) << 8) | buffer
[offset
+ 1];
352 if ((*rrtype
== dns_rrtype_a
&& rdlength
!= 4) || (*rrtype
== dns_rrtype_aaaa
&& rdlength
!= 16)) {
353 ERROR("srp_get_last_server: invalid rdlength %d for %s record",
354 rdlength
, *rrtype
== dns_rrtype_a
? "A" : "AAAA");
357 if (length
< rdlength
+ 6) { // rrtype + rdlength + address + port
358 ERROR("srp_get_last_server: stored server data length %d is too short", length
);
361 if (rdlength
> rdlim
) {
362 ERROR("srp_get_last_server: no space for %s data in provided buffer size %d",
363 *rrtype
== dns_rrtype_a
? "A" : "AAAA", rdlim
);
366 memcpy(rdata
, &buffer
[offset
], rdlength
);
368 memcpy(port
, &buffer
[offset
], 2);
373 srp_save_last_server(uint16_t rrtype
, uint8_t *NONNULL rdata
, uint16_t rdlength
,
374 uint8_t *NONNULL port
, void *NULLABLE host_context
)
376 dns_towire_state_t towire
;
379 memset(&towire
, 0, sizeof(towire
));
381 towire
.lim
= towire
.p
+ sizeof(buffer
);
383 if (rdlength
!= 4 && rdlength
!= 16) {
384 ERROR("srp_save_last_server: invalid IP address length %d", rdlength
);
387 dns_u16_to_wire(&towire
, rrtype
);
388 dns_u16_to_wire(&towire
, rdlength
);
389 dns_rdata_raw_data_to_wire(&towire
, rdata
, rdlength
);
390 dns_rdata_raw_data_to_wire(&towire
, port
, 2);
393 ERROR("srp_save_last_server: " PUB_S_SRP
" at %d (%p:%p:%p) while constructing output buffer",
394 strerror(towire
.error
), towire
.line
, towire
.p
, towire
.lim
, buffer
);
398 length
= towire
.p
- buffer
;
399 if (!srp_store_file_data(host_context
, "/var/run/srp-last-server", buffer
, length
)) {
407 srp_load_key_data(void *host_context
, const char *key_name
, uint8_t *buffer
, uint16_t *length
, uint16_t buffer_size
)
409 if (srp_load_file_data(key_name
, buffer
, length
, buffer_size
)) {
410 return kDNSServiceErr_NoError
;
412 return kDNSServiceErr_Unknown
;
416 srp_store_key_data(void *host_context
, const char *key_name
, uint8_t *buffer
, uint16_t length
)
418 if (!srp_store_file_data(host_context
, key_name
, buffer
, length
)) {
419 return kDNSServiceErr_Unknown
;
420 return kDNSServiceErr_NoError
;
422 #endif // NO_KEYCHAIN
425 interface_callback(void *context
, const char *NONNULL name
,
426 const addr_t
*NONNULL address
, const addr_t
*NONNULL netmask
,
427 uint32_t flags
, enum interface_address_change event_type
)
433 cti_service_vec_t
*cti_services
= context
;
439 if (address
->sa
.sa_family
== AF_INET
) {
440 rrtype
= dns_rrtype_a
;
441 rdata
= (uint8_t *)&address
->sin
.sin_addr
;
444 // Should use IN_LINKLOCAL and IN_LOOPBACK macros here, but for some reason they are not present on
446 if (rdata
[0] == 127) {
448 } else if (rdata
[0] == 169 && rdata
[1] == 254) {
451 } else if (address
->sa
.sa_family
== AF_INET6
) {
452 rrtype
= dns_rrtype_aaaa
;
453 rdata
= (uint8_t *)&address
->sin6
.sin6_addr
;
455 if (IN6_IS_ADDR_LOOPBACK(&address
->sin6
.sin6_addr
)) {
457 } else if (IN6_IS_ADDR_LINKLOCAL(&address
->sin6
.sin6_addr
)) {
464 if (address
->sa
.sa_family
== AF_INET
) {
465 IPv4_ADDR_GEN_SRP(rdata
, ipv4_rdata_buf
);
466 DEBUG("interface_callback: ignoring " PUB_S_SRP
" " PRI_IPv4_ADDR_SRP
, name
,
467 IPv4_ADDR_PARAM_SRP(rdata
, ipv4_rdata_buf
));
468 } else if (address
->sa
.sa_family
== AF_INET6
) {
469 SEGMENTED_IPv6_ADDR_GEN_SRP(rdata
, ipv6_rdata_buf
);
470 DEBUG("interface_callback: ignoring " PUB_S_SRP
" " PRI_SEGMENTED_IPv6_ADDR_SRP
, name
,
471 SEGMENTED_IPv6_ADDR_PARAM_SRP(rdata
, ipv6_rdata_buf
));
473 INFO("interface_callback: ignoring with non-v4/v6 address" PUB_S_SRP
, name
);
478 if (address
->sa
.sa_family
== AF_INET
) {
479 IPv4_ADDR_GEN_SRP(rdata
, ipv4_rdata_buf
);
480 DEBUG("interface_callback: " PUB_S_SRP
" " PRI_IPv4_ADDR_SRP
" %x", name
,
481 IPv4_ADDR_PARAM_SRP(rdata
, ipv4_rdata_buf
), flags
);
482 } else if (address
->sa
.sa_family
== AF_INET6
) {
483 SEGMENTED_IPv6_ADDR_GEN_SRP(rdata
, ipv6_rdata_buf
);
484 DEBUG("interface_callback: " PUB_S_SRP
" " PRI_SEGMENTED_IPv6_ADDR_SRP
" %x", name
,
485 SEGMENTED_IPv6_ADDR_PARAM_SRP(rdata
, ipv6_rdata_buf
), flags
);
487 DEBUG("interface_callback: " PUB_S_SRP
"<none IPv4/IPv6 address> %x", name
, flags
);
490 // This is a workaround for a bug in the utun0 code, where packets sent to the IP address of the local
491 // thread interface are dropped and do not reach the SRP server. To address this, if we find a service
492 // that is on a local IPv6 address, we replace the address with ::1.
493 if (cti_services
!= NULL
&& rrtype
== dns_rrtype_aaaa
) {
495 for (i
= 0; i
< cti_services
->num
; i
++) {
496 cti_service_t
*cti_service
= cti_services
->services
[i
];
497 // Look for SRP service advertisements only.
498 if (IS_SRP_SERVICE(cti_service
)) {
500 if (!memcmp(cti_service
->server
, rdata
, 16)) {
502 memset(cti_service
->server
, 0, 15);
504 cti_service
->server
[15] = 1;
510 srp_add_interface_address(rrtype
, rdata
, rdlen
);
514 remove_callback(void *context
)
516 srp_client_t
*client
= context
;
517 srp_deregister(client
);
521 register_callback(DNSServiceRef sdRef
, DNSServiceFlags flags
, DNSServiceErrorType errorCode
,
522 const char *name
, const char *regtype
, const char *domain
, void *context
)
524 srp_client_t
*client
= context
;
532 INFO("Register Reply for %s: %d", client
->name
, errorCode
);
534 if (errorCode
== kDNSServiceErr_NoError
&& delete_registrations
) {
535 client
->wakeup
= ioloop_wakeup_create();
536 if (client
->wakeup
== NULL
) {
537 ERROR("Unable to allocate a wakeup for %s.", client
->name
);
541 // Do a remove in five seconds.
542 ioloop_add_wake_event(client
->wakeup
, client
, remove_callback
, NULL
, 5000);
550 "srp-client [--lease-time <seconds>] [--client-count <client count>] [--server <address>%%<port>]\n"
551 #ifdef LIVE_TRANSACTION_CLEANUP
552 " [--live-transaction-cleanup <milliseconds>]\n"
554 " [--random-leases] [--delete-registrations] [--use-thread-services] [--bogusify-signatures]\n");
558 #ifdef LIVE_TRANSACTION_CLEANUP
560 flushed_callback(advertising_proxy_conn_ref cref
, xpc_object_t response
, advertising_proxy_error_type err
)
562 INFO("flushed: cref %p response %p err %d.", cref
, response
, err
);
563 if (err
!= kDNSSDAdvertisingProxyStatus_NoError
) {
566 // We don't need to wait around after flushing.
571 live_transaction_wakeup_callback(void *__unused context
)
573 int err
= advertising_proxy_flush_entries(&live_transaction_cref
, dispatch_get_main_queue(), flushed_callback
);
574 if (err
!= kDNSSDAdvertisingProxyStatus_NoError
) {
575 ERROR("live_transaction_wakeup_callback: advertising_proxy_flush_entries failed: %d", err
);
579 // This is a test to see if, if we have an update pending, we can safely remove all hosts without there being a
582 maybe_schedule_live_transaction_cleanup(void)
584 if (live_transaction_cleanup
) {
585 live_transaction_wakeup
= ioloop_wakeup_create();
586 if (live_transaction_wakeup
== NULL
) {
587 ERROR("maybe_schedule_live_transaction_cleanup: unable to allocate wakeup!");
590 // Schedule the wakeup for 100ms in the future.
591 ioloop_add_wake_event(live_transaction_wakeup
, NULL
, live_transaction_wakeup_callback
, NULL
, live_transaction_cleanup_time
);
597 cti_service_list_callback(void *__unused context
, cti_service_vec_t
*services
, cti_status_t status
)
601 if (status
== kCTIStatus_Disconnected
|| status
== kCTIStatus_DaemonNotRunning
) {
602 INFO("cti_get_service_list_callback: disconnected");
606 srp_start_address_refresh();
607 ioloop_map_interface_addresses(services
, interface_callback
);
608 for (i
= 0; i
< services
->num
; i
++) {
609 cti_service_t
*cti_service
= services
->services
[i
];
610 // Look for SRP service advertisements only.
611 if (IS_SRP_SERVICE(cti_service
)) {
612 srp_add_server_address(&cti_service
->server
[16], dns_rrtype_aaaa
, cti_service
->server
, 16);
615 srp_finish_address_refresh();
616 srp_network_state_stable();
617 #ifdef LIVE_TRANSACTION_CLEANUP
618 maybe_schedule_live_transaction_cleanup();
623 main(int argc
, char **argv
)
626 uint8_t server_address
[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1 };
627 uint8_t bogus_address
[16] = { 0xfc,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1 };
628 // { 0x26, 0x20, 0x01, 0x49, 0x00, 0x0f, 0x1a, 0x4d, 0x04, 0xff, 0x61, 0x5a, 0xa2, 0x2a, 0xab, 0xe8 };
638 bool have_server_address
= false;
642 for (i
= 1; i
< argc
; i
++) {
643 if (!strcmp(argv
[i
], "--lease-time")) {
649 *nump
= (uint32_t)strtoul(argv
[i
+ 1], &end
, 10);
650 if (end
== argv
[i
+ 1] || end
[0] != 0) {
654 } else if (!strcmp(argv
[i
], "--client-count")) {
657 } else if (!strcmp(argv
[i
], "--server")) {
661 uint16_t addrtype
= dns_rrtype_aaaa
;
667 percent
= strchr(argv
[i
+ 1], '%');
668 if (percent
== NULL
|| percent
[1] == 0) {
674 server_port
= (uint32_t)strtoul(percent
, &end
, 10);
675 if (end
== percent
|| end
[0] != 0) {
678 port
[0] = server_port
>> 8;
679 port
[1] = server_port
& 255;
681 if (inet_pton(AF_INET6
, argv
[i
+ 1], addrbuf
) < 1) {
682 if (inet_pton(AF_INET
, argv
[i
+ 1], addrbuf
) < 1) {
685 addrtype
= dns_rrtype_a
;
689 srp_add_server_address(port
, addrtype
, addrbuf
, addrlen
);
690 have_server_address
= true;
692 } else if (!strcmp(argv
[i
], "--random-leases")) {
693 random_leases
= true;
694 } else if (!strcmp(argv
[i
], "--delete-registrations")) {
695 delete_registrations
= true;
696 } else if (!strcmp(argv
[i
], "--use-thread-services")) {
697 use_thread_services
= true;
698 } else if (!strcmp(argv
[i
], "--bogusify-signatures")) {
699 bogusify_signatures
= true;
700 #ifdef LIVE_TRANSACTION_CLEANUP
701 } else if (!strcmp(argv
[i
], "--live-transaction-cleanup")) {
702 nump
= &live_transaction_cleanup_time
;
703 live_transaction_cleanup
= true;
711 if (!use_thread_services
) {
712 ioloop_map_interface_addresses(NULL
, interface_callback
);
715 if (!have_server_address
&& !use_thread_services
) {
718 srp_add_server_address(port
, dns_rrtype_aaaa
, bogus_address
, 16);
719 srp_add_server_address(port
, dns_rrtype_aaaa
, server_address
, 16);
722 for (i
= 0; i
< num_clients
; i
++) {
723 srp_client_t
*client
;
726 client
= calloc(1, sizeof(*client
));
727 if (client
== NULL
) {
728 ERROR("no memory for client %d", i
);
732 if (num_clients
== 1) {
733 strcpy(hnbuf
, "srp-api-test");
735 snprintf(hnbuf
, sizeof(hnbuf
), "srp-api-test-%d", i
);
737 client
->name
= strdup(hnbuf
);
738 if (client
->name
== NULL
) {
739 ERROR("No memory for client name %s", hnbuf
);
744 srp_host_init(client
);
745 srp_set_hostname(hnbuf
, NULL
);
748 int random_lease_time
= 30 + srp_random16() % 1800; // random
749 INFO("Client %d, lease time = %d", i
, random_lease_time
);
750 srp_set_lease_times(random_lease_time
, 7 * 24 * 3600); // random host lease, 7 day key lease
751 } else if (lease_time
> 0) {
752 srp_set_lease_times(lease_time
, 7 * 24 * 3600); // specified host lease, 7 day key lease
755 memcpy(&iport
, port
, 2);
756 err
= DNSServiceRegister(&sdref
, 0, 0, hnbuf
, "_ipps._tcp",
757 NULL
, NULL
, iport
, 0, NULL
, register_callback
, client
);
758 if (err
!= kDNSServiceErr_NoError
) {
759 ERROR("DNSServiceRegister failed: %d", err
);
764 if (use_thread_services
) {
765 cti_get_service_list(&thread_service_context
, NULL
, cti_service_list_callback
, dispatch_get_main_queue());
767 srp_network_state_stable();
768 #ifdef LIVE_TRANSACTION_CLEANUP
769 maybe_schedule_live_transaction_cleanup();
778 // c-file-style: "bsd"
781 // indent-tabs-mode: nil