blob: 246c30f2bd331cb7ad76765a2f0cf8cf667a8eab [file] [log] [blame] [edit]
/* 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <time.h>
#include <dns_sd.h>
#include <net/if.h>
#include <inttypes.h>
#include <sys/resource.h>
#include <netinet/icmp6.h>
#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: