/* ifpermit.c * * Copyright (c) 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. * * Implementation of a permitted interface list object, which maintains a list of * interfaces on which we are permitted to provide some service. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "srp.h" #include "ifpermit.h" #include "dns-msg.h" #include "ioloop.h" #include "srp-mdns-proxy.h" // If we aren't able to allocate a permitted interface list, we still need to return a non-NULL value so that we don't // fail open. So all of these functions need to treat this particular value as special but not dereference it. #define PERMITTED_INTERFACE_LIST_BLOCKED (ifpermit_list_t *)1 typedef struct ifpermit_name ifpermit_name_t; struct ifpermit_name { ifpermit_name_t *next; char *name; // Interface name uint32_t ifindex; // Interface index int count; // Number of permittors for this interface }; struct ifpermit_list { int ref_count; ifpermit_name_t *names; }; void ifpermit_list_add(ifpermit_list_t *permits, const char *name) { if (permits == PERMITTED_INTERFACE_LIST_BLOCKED) { ERROR("blocked permit list when adding " PUB_S_SRP, name); return; } ifpermit_name_t **pname = &permits->names; ifpermit_name_t *permit_name; while (*pname != NULL) { permit_name = *pname; if (!strcmp(name, permit_name->name)) { success: permit_name->count++; INFO("%d permits for interface " PUB_S_SRP " with index %d", permit_name->count, name, permit_name->ifindex); return; } pname = &permit_name->next; } permit_name = calloc(1, sizeof(*permit_name)); if (permit_name != NULL) { permit_name->name = strdup(name); if (permit_name->name == NULL) { free(permit_name); permit_name = NULL; } else { permit_name->ifindex = if_nametoindex(name); if (permit_name->ifindex == 0) { ERROR("if_nametoindex for interface " PUB_S_SRP " returned 0.", name); free(permit_name->name); free(permit_name); return; } *pname = permit_name; goto success; } } ERROR("no memory to add permit for " PUB_S_SRP, name); } void ifpermit_list_remove(ifpermit_list_t *permits, const char *name) { if (permits == PERMITTED_INTERFACE_LIST_BLOCKED) { ERROR("blocked permit list when removing " PUB_S_SRP, name); return; } ifpermit_name_t **pname = &permits->names; ifpermit_name_t *permit_name; while (*pname != NULL) { permit_name = *pname; if (!strcmp(name, permit_name->name)) { permit_name->count--; INFO("%d permits for interface " PUB_S_SRP " with index %d", permit_name->count, name, permit_name->ifindex); if (permit_name->count == 0) { *pname = permit_name->next; free(permit_name->name); free(permit_name); } return; } pname = &permit_name->next; } FAULT("permit remove for interface " PUB_S_SRP " which does not exist", name); } static void ifpermit_list_finalize(ifpermit_list_t *list) { if (list != NULL && list != PERMITTED_INTERFACE_LIST_BLOCKED) { ifpermit_name_t *names = list->names, *next = NULL; while (names != NULL) { next = names->next; free(names->name); free(names); names = next; } free(list); } } void ifpermit_list_retain_(ifpermit_list_t *list, const char *file, int line) { if (list != NULL && list != PERMITTED_INTERFACE_LIST_BLOCKED) { RETAIN(list, ifpermit_list); } } void ifpermit_list_release_(ifpermit_list_t *list, const char *file, int line) { if (list != NULL && list != PERMITTED_INTERFACE_LIST_BLOCKED) { RELEASE(list, ifpermit_list); } } ifpermit_list_t * ifpermit_list_create_(const char *file, int line) { ifpermit_list_t *permits = calloc(1, sizeof(*permits)); if (permits == NULL) { return PERMITTED_INTERFACE_LIST_BLOCKED; } RETAIN(permits, ifpermit_list); return permits; } bool ifpermit_interface_is_permitted(ifpermit_list_t *permits, uint32_t ifindex) { if (permits != NULL && permits != PERMITTED_INTERFACE_LIST_BLOCKED) { for (ifpermit_name_t *name = permits->names; name != NULL; name = name->next) { if (name->ifindex == ifindex) { return true; } } } return false; } void ifpermit_add_permitted_interface_to_server_(srp_server_t *NONNULL server_state, const char *NONNULL name, const char *file, int line) { if (server_state->permitted_interfaces == NULL) { server_state->permitted_interfaces = ifpermit_list_create_(file, line); } ifpermit_list_add(server_state->permitted_interfaces, name); } // Local Variables: // mode: C // tab-width: 4 // c-file-style: "bsd" // c-basic-offset: 4 // fill-column: 120 // indent-tabs-mode: nil // End: