Home | History | Annotate | Line # | Download | only in ServiceRegistration
      1 /* probe-srp.c
      2  *
      3  * Copyright (c) 2023 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  * This file contains code to queue and send updates for Thread services.
     18  */
     19 
     20 #ifndef LINUX
     21 #include <netinet/in.h>
     22 #include <net/if.h>
     23 #include <netinet6/in6_var.h>
     24 #include <netinet6/nd6.h>
     25 #include <net/if_media.h>
     26 #include <sys/stat.h>
     27 #else
     28 #define _GNU_SOURCE
     29 #include <netinet/in.h>
     30 #include <fcntl.h>
     31 #include <bsd/stdlib.h>
     32 #include <net/if.h>
     33 #endif
     34 #include <sys/socket.h>
     35 #include <sys/ioctl.h>
     36 #include <net/route.h>
     37 #include <netinet/icmp6.h>
     38 #include <stdio.h>
     39 #include <unistd.h>
     40 #include <errno.h>
     41 #include <stdlib.h>
     42 #include <string.h>
     43 #include <ctype.h>
     44 #include <arpa/inet.h>
     45 #include <stdlib.h>
     46 #include <stddef.h>
     47 #include <dns_sd.h>
     48 #include <inttypes.h>
     49 #include <signal.h>
     50 
     51 #include "srp.h"
     52 #include "dns-msg.h"
     53 #include "ioloop.h"
     54 
     55 #include "srp-mdns-proxy.h"
     56 #include "state-machine.h"
     57 #include "thread-service.h"
     58 #include "service-tracker.h"
     59 #include "probe-srp.h"
     60 
     61 struct probe_state {
     62     int ref_count;
     63     comm_t *connection;
     64     wakeup_t *wakeup;
     65     thread_service_t *service;
     66     void *context;
     67     void (*callback)(thread_service_t *service, void *context, bool succeeded);
     68     void (*context_release)(void *context);
     69     route_state_t *route_state;
     70     dns_wire_t question;
     71     int num_retransmissions, retransmission_delay;
     72     uint16_t question_length;
     73 };
     74 
     75 static void
     76 probe_state_finalize(probe_state_t *probe_state)
     77 {
     78     if (probe_state->wakeup != NULL) {
     79         ioloop_wakeup_release(probe_state->wakeup);
     80     }
     81     if (probe_state->service != NULL) {
     82         thread_service_release(probe_state->service);
     83         probe_state->service = NULL;
     84     }
     85     if (probe_state->connection != NULL) {
     86         ioloop_comm_release(probe_state->connection);
     87         probe_state->connection = NULL;
     88     }
     89     if (probe_state->context_release) {
     90         probe_state->context_release(probe_state->context);
     91     }
     92     free(probe_state);
     93 }
     94 
     95 void
     96 probe_srp_service_probe_cancel(thread_service_t *service)
     97 {
     98     probe_state_t *probe_state = service->probe_state;
     99     probe_state->service = NULL;
    100     service->probe_state = NULL;
    101 
    102     if (probe_state->context_release != NULL) {
    103         probe_state->context_release(probe_state->context);
    104     }
    105     probe_state->context = NULL;
    106 
    107     thread_service_release(service); // The probe state's reference to the service
    108     if (probe_state->wakeup != NULL) {
    109         ioloop_cancel_wake_event(probe_state->wakeup);
    110     }
    111 
    112     if (probe_state->connection != NULL) {
    113         ioloop_comm_cancel(probe_state->connection); // Cancel the connection (should result in the state being released)
    114         ioloop_comm_release(probe_state->connection);
    115         probe_state->connection = NULL;
    116     }
    117     RELEASE_HERE(probe_state, probe_state); // The thread_service_t's reference to the probe state
    118 }
    119 
    120 static void
    121 probe_srp_done(void *context, bool succeeded)
    122 {
    123     probe_state_t *probe_state = context;
    124     struct in6_addr *address;
    125     int port;
    126     thread_service_t *service = probe_state->service;
    127     // Note: we should still have references both to probe_state and to service here because they each held references
    128     // to each other.
    129     probe_state->service = NULL;
    130     service->probe_state = NULL;
    131     if (service->service_type == anycast_service) {
    132         address = &service->u.anycast.address;
    133         port = 53;
    134     } else {
    135         address = &service->u.unicast.address;
    136         // If the anycast service is present, we can use port 53, which we need to prefer because pre-2024 Apple BRs
    137         // will not answer DNS queries on the SRP service port.
    138         if (service->u.unicast.anycast_also_present) {
    139             port = 53;
    140         } else {
    141             port = (service->u.unicast.port[0] << 8) | service->u.unicast.port[1];
    142         }
    143     }
    144     SEGMENTED_IPv6_ADDR_GEN_SRP(address->s6_addr, addr_buf);
    145     if (!succeeded) {
    146         INFO("service " PRI_SEGMENTED_IPv6_ADDR_SRP " not responding on port %d",
    147              SEGMENTED_IPv6_ADDR_PARAM_SRP(address->s6_addr, addr_buf), port);
    148         service->checking = false;
    149         service->ignore = true;  // Don't consider this service when deciding what to advertise
    150         service->responding = false;
    151     } else {
    152         INFO("service " PRI_SEGMENTED_IPv6_ADDR_SRP " responded on port %d",
    153              SEGMENTED_IPv6_ADDR_PARAM_SRP(address->s6_addr, addr_buf), port);
    154         service->checking = false;
    155         service->ignore = false;
    156         service->checked = true;
    157     }
    158     service->responding = true;
    159 
    160     if (probe_state->callback != NULL) {
    161         probe_state->callback(probe_state->service, probe_state->context, succeeded);
    162         probe_state->callback = NULL;
    163     }
    164     if (probe_state->context_release != NULL) {
    165         probe_state->context_release(probe_state->context);
    166     }
    167     probe_state->context = NULL;
    168 
    169     thread_service_release(service); // The probe state's reference to the service
    170     if (probe_state->wakeup != NULL) {
    171         ioloop_cancel_wake_event(probe_state->wakeup);
    172     }
    173     RELEASE_HERE(probe_state, probe_state); // The thread_service_t's reference to the probe state
    174 }
    175 
    176 static void
    177 probe_srp_datagram(comm_t *connection, message_t *message, void *context)
    178 {
    179 #ifdef PROBE_SRP_TCP
    180     (void)message;
    181     // We should never get a datagram
    182     ERROR("got a datagram on %p", context);
    183 #else
    184     int rcode = dns_rcode_get(&message->wire);
    185     probe_state_t *probe_state = context;
    186     if (connection->connection != NULL) {
    187         INFO("datagram from " PRI_S_SRP " on port %d xid %x (question xid %x) rcode %d", connection->name,
    188              ntohs(probe_state->connection->address.sin6.sin6_port), message->wire.id, probe_state->question.id, rcode);
    189     } else {
    190         SEGMENTED_IPv6_ADDR_GEN_SRP(&probe_state->connection->address.sin6.sin6_addr, addr_buf);
    191         INFO("datagram from " PRI_SEGMENTED_IPv6_ADDR_SRP " on port %d xid %x (question xid %x) rcode %d",
    192              SEGMENTED_IPv6_ADDR_PARAM_SRP(&probe_state->connection->address.sin6.sin6_addr, addr_buf),
    193              ntohs(probe_state->connection->address.sin6.sin6_port), message->wire.id, probe_state->question.id, rcode);
    194     }
    195     if (message->wire.id != probe_state->question.id) {
    196         return; // not a response to the question we asked
    197     }
    198     dns_message_t *dns_message = NULL;
    199     if (!dns_wire_parse(&dns_message, &message->wire, message->length, false)) {
    200         // Not a valid response, who knows what happened?
    201         return;
    202     }
    203     dns_message_free(dns_message);
    204     // If we get a servfail, treat it like a dropped packet, since that might just mean that the remote end is
    205     // temporarily busy.
    206     if (rcode == dns_rcode_servfail) {
    207         return;
    208     }
    209     probe_srp_done(context, rcode == dns_rcode_noerror);
    210     ioloop_comm_cancel(probe_state->connection); // Cancel the connection (should result in the state being released)
    211     if (probe_state->wakeup != NULL) {
    212         ioloop_cancel_wake_event(probe_state->wakeup);
    213     }
    214 #endif
    215 }
    216 
    217 static void
    218 probe_srp_probe_state_context_release(void *context)
    219 {
    220     probe_state_t *probe_state = context;
    221     RELEASE_HERE(probe_state, probe_state);
    222 }
    223 
    224 static void
    225 probe_srp_disconnected(comm_t *UNUSED connection, void *context, int UNUSED error)
    226 {
    227     probe_state_t *probe_state = context;
    228 
    229     // We can get here either because the connection object got canceled or because we made a TCP connection that
    230     // failed to connect. If we haven't signaled "done" yet, probe_state->service will be non-NULL.
    231     if (probe_state->service != NULL) {
    232         probe_srp_done(context, false);
    233     }
    234     // Once we've gotten the connection disconnect event, we should not get any more callbacks from the connection
    235     // object.
    236     if (probe_state->connection != NULL) {
    237         ioloop_comm_release(probe_state->connection);
    238         probe_state->connection = NULL;
    239     }
    240 }
    241 
    242 #ifndef PROBE_SRP_TCP
    243 static void probe_srp_schedule_retransmission(probe_state_t *probe_state);
    244 
    245 static void
    246 probe_srp_retransmit(void *context)
    247 {
    248     probe_state_t *probe_state = context;
    249 
    250     // Only retransmit three times.
    251     probe_state->num_retransmissions++;
    252     INFO("num_retransmissions = %d, time = %lg", probe_state->num_retransmissions, probe_state->retransmission_delay / 1000.0);
    253     if (probe_state->num_retransmissions > 3) {
    254         probe_srp_done(context, false); // fail
    255         ioloop_comm_cancel(probe_state->connection);
    256    } else {
    257         // Schedule a retransmission with exponential backoff
    258         probe_srp_schedule_retransmission(probe_state);
    259 
    260         // Send the question
    261         struct iovec iov;
    262         iov.iov_len = probe_state->question_length;
    263         iov.iov_base = &probe_state->question;
    264         ioloop_send_message(probe_state->connection, NULL, &iov, 1);
    265     }
    266 }
    267 
    268 static void
    269 probe_srp_context_release(void *context)
    270 {
    271     probe_state_t *probe_state = context;
    272     RELEASE_HERE(probe_state, probe_state);
    273 }
    274 
    275 static void
    276 probe_srp_schedule_retransmission(probe_state_t *probe_state)
    277 {
    278     if (probe_state->wakeup == NULL) {
    279         probe_state->wakeup = ioloop_wakeup_create();
    280         if (probe_state->wakeup == NULL) {
    281             ERROR("can't allocate probe state wakeup");
    282             probe_srp_done(probe_state, false);
    283             ioloop_comm_cancel(probe_state->connection);
    284             return;
    285         }
    286     }
    287     int next_time = probe_state->retransmission_delay + srp_random16() % probe_state->retransmission_delay;
    288     probe_state->retransmission_delay *= 2;
    289     ioloop_add_wake_event(probe_state->wakeup, probe_state, probe_srp_retransmit, probe_srp_context_release, next_time);
    290     RETAIN_HERE(probe_state, probe_state); // The wakeup holds a reference to probe_state.
    291 }
    292 #endif // PROBE_SRP_TCP
    293 
    294 static void
    295 probe_srp_connected(comm_t *connection, void *context)
    296 {
    297 #ifdef PROBE_SRP_TCP
    298     probe_srp_done(context, true);
    299 #else
    300     (void)connection;
    301     probe_state_t *probe_state = context;
    302     // Initialize a DNS message to send to the target
    303     memset(&probe_state->question, 0, DNS_HEADER_SIZE);
    304     dns_towire_state_t towire;
    305     memset(&towire, 0, sizeof(towire));
    306     towire.p = &probe_state->question.data[0];
    307     towire.lim = &probe_state->question.data[0] + DNS_DATA_SIZE;
    308     towire.message = &probe_state->question;
    309 
    310     // Set up the header.
    311     probe_state->question.id = srp_random16();
    312     probe_state->question.bitfield = 0;
    313     dns_qr_set(&probe_state->question, dns_qr_query);
    314     dns_opcode_set(&probe_state->question, dns_opcode_query);
    315     probe_state->question.qdcount = htons(1); // Just ask one question
    316 
    317     // Query SOA for default.service.arpa--if this fails, we can't use this server.
    318     dns_full_name_to_wire(NULL, &towire, "default.service.arpa");
    319     dns_u16_to_wire(&towire, dns_rrtype_soa);
    320     dns_u16_to_wire(&towire, dns_qclass_in);
    321     probe_state->question_length = (uint16_t)(towire.p - (uint8_t *)&probe_state->question);
    322 
    323     // We're not in a hurry; the goal is to probe.
    324     probe_state->retransmission_delay = 1000; // milliseconds
    325     probe_state->num_retransmissions = 0;
    326 
    327     // Schedule the first send
    328     probe_srp_schedule_retransmission(probe_state);
    329 #endif // PROBE_SRP_TCP
    330 }
    331 
    332 static probe_state_t *
    333 probe_srp_create(addr_t *address, thread_service_t *service, void *context,
    334                    void (*callback)(thread_service_t *service, void *context, bool succeeded),
    335                    void (*context_release)(void *context))
    336 {
    337     probe_state_t *ret = NULL, *probe_state = calloc(1, sizeof(*probe_state));
    338     if (probe_state == NULL) {
    339         INFO("failed to create probe state");
    340         goto out;
    341     }
    342     RETAIN_HERE(probe_state, probe_state); // Retain for the caller
    343     // tls   stream stable  opportunistic
    344     probe_state->connection = ioloop_connection_create(address, false, false, false, false,
    345                                                        probe_srp_datagram, probe_srp_connected, probe_srp_disconnected,
    346                                                        probe_srp_probe_state_context_release, probe_state);
    347     if (probe_state->connection == NULL) {
    348         INFO("failed to create connection");
    349         goto out;
    350     }
    351     RETAIN_HERE(probe_state, probe_state); // for connection
    352     SEGMENTED_IPv6_ADDR_GEN_SRP(&address->sin6.sin6_addr, addr_buf);
    353     INFO("probing service " PRI_SEGMENTED_IPv6_ADDR_SRP " on port %d",
    354          SEGMENTED_IPv6_ADDR_PARAM_SRP(&address->sin6.sin6_addr, addr_buf), ntohs(address->sin6.sin6_port));
    355     probe_state->context = context;
    356     probe_state->callback = callback;
    357     service->last_probe_time = srp_time();
    358 
    359     service->probe_state = probe_state;
    360     RETAIN_HERE(service->probe_state, probe_state);
    361 
    362     probe_state->service = service;
    363     thread_service_retain(probe_state->service);
    364 
    365     // connection holds the only reference to probe_state.
    366     ret = probe_state;
    367     probe_state = NULL;
    368 out:
    369     if (probe_state != NULL) {
    370         RELEASE_HERE(probe_state, probe_state);
    371     }
    372     if (ret == NULL && callback != NULL) {
    373         dispatch_async(dispatch_get_main_queue(), ^{
    374             callback(service, context, true); // We claim success here because this should never fail; if it does, it's our problem.
    375             if (context_release != NULL) {
    376                 context_release(context);
    377             }
    378         });
    379     }
    380     return ret;
    381 }
    382 
    383 // If we've been asked to probe a service, go through the list.
    384 static probe_state_t *
    385 probe_srp_anycast_service(thread_service_t *service, void *context,
    386                           void (*callback)(thread_service_t *service, void *context, bool succeeded),
    387                           void (*context_release)(void *context))
    388 {
    389     addr_t address;
    390     memset(&address, 0, sizeof(address));
    391     memcpy(&address.sin6.sin6_addr, &service->u.anycast.address, sizeof(service->u.anycast.address));
    392     address.sin6.sin6_port = htons(53);
    393     address.sa.sa_family = AF_INET6;
    394     return probe_srp_create(&address, service, context, callback, context_release);
    395 }
    396 
    397 static probe_state_t *
    398 probe_srp_unicast_service(thread_service_t *service, void *context,
    399                           void (*callback)(thread_service_t *service, void *context, bool succeeded),
    400                           void (*context_release)(void *context)){
    401     if (service->checking || service->user) {
    402         return NULL;
    403     }
    404     addr_t address;
    405     memset(&address, 0, sizeof(address));
    406     address.sa.sa_family = AF_INET6;
    407     memcpy(&address.sin6.sin6_addr, &service->u.unicast.address, sizeof(address.sin6.sin6_addr));
    408     memcpy(&address.sin6.sin6_port, service->u.unicast.port, sizeof(address.sin6.sin6_port));
    409     return probe_srp_create(&address, service, context, callback, context_release);
    410 }
    411 
    412 void
    413 probe_srp_service(thread_service_t *service, void *context,
    414                   void (*callback)(thread_service_t *service, void *context, bool succeeded),
    415                   void (*context_release)(void *context))
    416 {
    417     probe_state_t *probe_state;
    418     if (service->service_type == unicast_service) {
    419         probe_state = probe_srp_unicast_service(service, context, callback, context_release);
    420     } else if (service->service_type == anycast_service){
    421         probe_state = probe_srp_anycast_service(service, context, callback, context_release);
    422     } else {
    423         FAULT("bogus service type in probe_srp_service: %d", service->service_type);
    424         if (callback != NULL) {
    425             dispatch_async(dispatch_get_main_queue(), ^{
    426                     callback(service, context, false); // False because this isn't a valid service
    427                     if (context_release) {
    428                         context_release(context);
    429                     }
    430                 });
    431         }
    432         return;
    433     }
    434 
    435     // probe_srp_create returns this retained, but we don't store the pointer.
    436     RELEASE_HERE(probe_state, probe_state);
    437 }
    438 
    439 // Local Variables:
    440 // mode: C
    441 // tab-width: 4
    442 // c-file-style: "bsd"
    443 // c-basic-offset: 4
    444 // fill-column: 120
    445 // indent-tabs-mode: nil
    446 // End:
    447