#include <ctype.h>
#include <pwd.h>
#include <netdb.h>
#include <string.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <machine/endian.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFArray.h>
#include <CoreFoundation/CFDictionary.h>
#include <CoreFoundation/CFData.h>
#include "dynarray.h"
#include "subnets.h"
#include "util.h"
#include "dhcp_options.h"
#include "cfutil.h"
#include "DNSNameList.h"
#include <SystemConfiguration/SCValidation.h>
static bool S_use_syslog;
static int S_log_level;
#define DEFAULT_LEASE_MIN ((dhcp_lease_time_t)60 * 60)
#define DEFAULT_LEASE_MAX ((dhcp_lease_time_t)60 * 60 * 24)
struct _SubnetList {
dynarray_t list;
};
typedef struct _OptionTLV {
int tag;
int length;
const void * value;
} OptionTLV, * OptionTLVRef;
typedef struct ip_range {
struct in_addr start;
struct in_addr end;
} ip_range_t;
struct _Subnet {
const char * name;
struct in_addr net_address;
struct in_addr net_mask;
ip_range_t net_range;
bool allocate;
dhcp_lease_time_t lease_min;
dhcp_lease_time_t lease_max;
struct in_addr nextip;
const char * supernet;
OptionTLVRef options;
int options_count;
};
typedef struct _Subnet Subnet;
static int
ip_range_compare(ip_range_t * a_p, ip_range_t * b_p, bool * overlap)
{
in_addr_t b_start = iptohl(b_p->start);
in_addr_t b_end = iptohl(b_p->end);
in_addr_t a_start = iptohl(a_p->start);
in_addr_t a_end = iptohl(a_p->end);
int result = 0;
*overlap = TRUE;
if (a_start == b_start) {
result = 0;
}
else if (a_start < b_start) {
result = -1;
if (a_end < b_start)
*overlap = FALSE;
}
else {
result = 1;
if (b_end < a_start)
*overlap = FALSE;
}
return (result);
}
#ifdef DEBUG
static void
S_timestamp(char * msg)
{
static struct timeval tvp = {0,0};
struct timeval tv;
gettimeofday(&tv, 0);
if (tvp.tv_sec) {
int sec, usec;
#define USECS_PER_SEC 1000000
sec = tv.tv_sec - tvp.tv_sec;
usec = tv.tv_usec - tvp.tv_usec;
if (usec < 0) {
usec += USECS_PER_SEC;
sec--;
}
printf("%d.%06d (%d.%06d): %s\n",
tv.tv_sec, tv.tv_usec, sec, usec, msg);
}
else
printf("%d.%06d (%d.%06d): %s\n",
tv.tv_sec, tv.tv_usec, 0, 0, msg);
tvp = tv;
}
#endif DEBUG
static void
SubnetPrint(SubnetRef subnet)
{
printf("Subnet '%s'",
(subnet->name != NULL)
? subnet->name : inet_nettoa(subnet->net_address, subnet->net_mask));
if (subnet->supernet != NULL) {
printf(": supernet %s\n", subnet->supernet);
}
else {
printf("\n");
}
printf("\tNetwork: %s", inet_ntoa(subnet->net_address));
printf("/%s\n", inet_ntoa(subnet->net_mask));
printf("\tRange: %s..", inet_ntoa(subnet->net_range.start));
printf("%s\n", inet_ntoa(subnet->net_range.end));
printf("\tAllocate: %s\n", (subnet->allocate) ? "yes" : "no");
if (subnet->allocate) {
printf("\tLease Min: %d Lease Max: %d\n",
subnet->lease_min, subnet->lease_max);
}
if (subnet->options_count != 0) {
int i;
printf("\tOptions:\n");
printf("\t%6s %6s %s\n", "Code", "Length", "Data");
for (i = 0; i < subnet->options_count; i++) {
printf("\t%6d %6d ",
subnet->options[i].tag, subnet->options[i].length);
print_bytes((void *)subnet->options[i].value,
subnet->options[i].length);
printf("\n");
}
}
return;
}
static __inline__ const char *
SubnetGetSupernet(SubnetRef subnet)
{
return (subnet->supernet);
}
static bool
SubnetIsAddressOnSubnet(SubnetRef subnet, struct in_addr addr)
{
return (in_subnet(subnet->net_address, subnet->net_mask, addr));
}
static bool
SubnetIsAddressWithinRange(SubnetRef subnet, struct in_addr ipaddr)
{
in_addr_t l;
if (SubnetIsAddressOnSubnet(subnet, ipaddr) == FALSE) {
return (FALSE);
}
l = iptohl(ipaddr);
if (l < iptohl(subnet->net_range.start)
|| l > iptohl(subnet->net_range.end)) {
return (FALSE);
}
return (TRUE);
}
static __inline__ ip_range_t
SubnetGetRange(SubnetRef subnet)
{
return (subnet->net_range);
}
static int
SubnetCompareWithSubnet(SubnetRef subnet1, SubnetRef subnet2, bool * overlap)
{
return (ip_range_compare(&subnet1->net_range, &subnet2->net_range, overlap));
}
static bool
SubnetSameSupernetAsSubnet(SubnetRef subnet1, SubnetRef subnet2)
{
if (subnet1->supernet == NULL || subnet2->supernet == NULL) {
return (FALSE);
}
if (strcmp(subnet1->supernet, subnet2->supernet) == 0) {
return (TRUE);
}
return (FALSE);
}
bool
SubnetDoesAllocate(SubnetRef subnet)
{
return (subnet->allocate);
}
static bool
SubnetAcquireAddress(SubnetRef subnet,
SubnetIsAddressInUseFuncRef func, void * arg,
struct in_addr * ret_addr)
{
in_addr_t end;
in_addr_t i;
if (SubnetDoesAllocate(subnet) == FALSE) {
return (FALSE);
}
end = iptohl(subnet->net_range.end);
i = iptohl(subnet->nextip);
if (i == (end + 1)) {
i = iptohl(subnet->net_range.start);
}
for (; i <= end; i++) {
if (func == NULL
|| (*func)(arg, hltoip(i)) == FALSE) {
*ret_addr = hltoip(i);
subnet->nextip = hltoip(i);
return (TRUE);
}
}
subnet->nextip = hltoip(end + 1);
return (FALSE);
}
static const char *
SubnetGetName(SubnetRef subnet)
{
return (subnet->name);
}
static bool
S_get_plist_boolean(CFDictionaryRef plist, CFStringRef key, bool d)
{
CFBooleanRef b;
boolean_t ret = d;
b = CFDictionaryGetValue(plist, key);
if (isA_CFBoolean(b) != NULL) {
ret = CFBooleanGetValue(b);
}
return (ret);
}
static char *
myCFStringCreateCStringWithRange(CFStringRef cfstr,
CFRange range, CFStringEncoding encoding)
{
CFIndex len = 0;
char * str;
CFStringGetBytes(cfstr, range, encoding, 0, FALSE, NULL, 0, &len);
if (len == 0) {
return (NULL);
}
str = malloc(len + 1);
CFStringGetBytes(cfstr, range, encoding, 0, FALSE, (UInt8 *)str, len, &len);
str[len] = '\0';
return (str);
}
static char *
myCFStringCreateCString(CFStringRef cfstr, CFStringEncoding encoding)
{
CFRange range;
range = CFRangeMake(0, CFStringGetLength(cfstr));
return (myCFStringCreateCStringWithRange(cfstr, range, encoding));
}
static CFDataRef
myCFDataCreateNumericType(dhcptype_t type, uint32_t l)
{
uint8_t b;
CFDataRef data = NULL;
uint16_t s;
switch (type) {
case dhcptype_bool_e:
b = (l != 0) ? 1 : 0;
data = CFDataCreate(NULL, &b, sizeof(b));
break;
case dhcptype_uint8_e:
b = l;
data = CFDataCreate(NULL, &b, sizeof(b));
break;
case dhcptype_uint16_e:
s = htons(l);
data = CFDataCreate(NULL, (UInt8 *)&s, sizeof(s));
break;
case dhcptype_int32_e:
case dhcptype_uint32_e:
l = htonl(l);
data = CFDataCreate(NULL, (UInt8 *)&l, sizeof(l));
break;
default:
break;
}
return (data);
}
static CFDataRef
myCFDataCreateWithTagAndCFType(dhcptag_t tag, CFTypeRef value,
char * err)
{
CFDataRef data = NULL;
dhcptype_t type;
const dhcptag_info_t * tag_info;
const dhcptype_info_t * type_info = NULL;
if (err != NULL) {
*err = '\0';
}
if (isA_CFData(value) != NULL) {
return (CFRetain(value));
}
tag_info = dhcptag_info(tag);
if (tag_info == NULL) {
if (err != NULL) {
strcpy(err, "Unknown tag");
}
goto done;
}
type_info = dhcptype_info(tag_info->type);
if (type_info == NULL) {
if (err != NULL) {
sprintf(err, "Unknown type %d", tag_info->type);
}
goto done;
}
type = tag_info->type;
if (isA_CFArray(value) != NULL) {
if (CFArrayGetCount(value) == 0) {
if (err != NULL) {
strcpy(err, "Empty array");
}
goto done;
}
if (type_info->string_list == FALSE) {
value = CFArrayGetValueAtIndex(value, 0);
}
}
if (isA_CFString(value) != NULL) {
struct in_addr ip;
uint32_t l;
if (type_info->multiple_of != dhcptype_none_e) {
if (type == dhcptype_ip_pairs_e) {
if (err != NULL) {
strcpy(err, "Type requires IP address pairs");
}
goto done;
}
type = type_info->multiple_of;
}
switch (type) {
case dhcptype_string_e:
data = CFStringCreateExternalRepresentation(NULL,
value,
kCFStringEncodingUTF8,
0);
break;
case dhcptype_ip_e:
if (my_CFStringToIPAddress(value, &ip) == FALSE) {
if (err != NULL) {
strcpy(err, "Invalid IP address");
}
goto done;
}
data = CFDataCreate(NULL, (UInt8 *)&ip, sizeof(ip));
break;
case dhcptype_bool_e:
case dhcptype_uint8_e:
case dhcptype_uint16_e:
case dhcptype_int32_e:
case dhcptype_uint32_e:
if (my_CFStringToNumber(value, &l) == FALSE) {
if (err != NULL) {
strcpy(err, "Invalid IP number");
}
goto done;
}
data = myCFDataCreateNumericType(type, l);
if (data == NULL) {
if (err != NULL) {
strcpy(err, "Failed to convert to numeric");
}
}
break;
case dhcptype_dns_namelist_e: {
uint8_t * encoded;
int encoded_length;
char * str;
str = myCFStringCreateCString(value, kCFStringEncodingUTF8);
if (str == NULL) {
if (err != NULL) {
strcpy(err, "Failed to convert to string");
}
goto done;
}
encoded = DNSNameListBufferCreate((const char * *)&str, 1,
NULL, &encoded_length);
free(str);
if (encoded == NULL) {
if (err != NULL) {
strcpy(err, "Failed to encode DNS search");
}
goto done;
}
data = CFDataCreate(NULL, encoded, encoded_length);
free(encoded);
break;
}
default:
if (err != NULL) {
sprintf(err, "Failed to convert from string to %s",
type_info->name);
}
goto done;
}
}
else if (isA_CFNumber(value) != NULL || isA_CFBoolean(value) != NULL) {
uint32_t l;
if (type_info->multiple_of != dhcptype_none_e) {
if (type == dhcptype_ip_pairs_e) {
if (err != NULL) {
strcpy(err, "Type requires pairs of IP address values");
}
goto done;
}
type = type_info->multiple_of;
}
if (isA_CFBoolean(value) != NULL) {
l = (CFEqual(value, kCFBooleanTrue)) ? 1 : 0;
}
else if (CFNumberGetValue(value, kCFNumberSInt32Type, &l)
== FALSE) {
if (err != NULL) {
strcpy(err, "Failed to convert to numeric");
}
goto done;
}
data = myCFDataCreateNumericType(type, l);
if (data == NULL) {
goto done;
}
}
else if (isA_CFArray(value) != NULL) {
int i;
int count = CFArrayGetCount(value);
switch (type) {
case dhcptype_ip_pairs_e:
if (count & 1) {
if (err != NULL) {
strcpy(err, "Type requires pairs of IP address values");
}
goto done;
}
case dhcptype_ip_mult_e: {
CFMutableDataRef d;
struct in_addr ip;
d = CFDataCreateMutable(NULL, count * sizeof(ip));
for (i = 0; i < count; i++) {
CFStringRef element = CFArrayGetValueAtIndex(value, i);
if (isA_CFString(element) == NULL) {
if (err != NULL) {
strcpy(err, "Can't convert non-string to IP address");
}
CFRelease(d);
goto done;
}
if (my_CFStringToIPAddress(element, &ip) == FALSE) {
if (err != NULL) {
strcpy(err, "Invalid IP address");
}
CFRelease(d);
goto done;
}
CFDataAppendBytes(d, (UInt8 *)&ip, sizeof(ip));
}
data = CFDataCreateCopy(NULL, d);
CFRelease(d);
break;
}
case dhcptype_uint8_mult_e:
case dhcptype_uint16_mult_e: {
CFMutableDataRef d;
uint32_t l;
d = CFDataCreateMutable(NULL, count * type_info->size);
for (i = 0; i < count; i++) {
CFTypeRef element = CFArrayGetValueAtIndex(value, i);
if (my_CFTypeToNumber(element, &l) == FALSE) {
if (err != NULL) {
strcpy(err, "Invalid number");
}
goto done;
CFRelease(d);
}
if (type_info->size == 1) {
uint8_t b = l;
CFDataAppendBytes(d, &b, sizeof(b));
}
else {
uint16_t s = htons(l);
CFDataAppendBytes(d, (UInt8 *)&s, sizeof(s));
}
}
data = CFDataCreateCopy(NULL, d);
CFRelease(d);
break;
}
case dhcptype_dns_namelist_e: {
uint8_t * encoded;
int encoded_length;
char * * strlist;
int strlist_count;
int strlist_size;
if (my_CFStringArrayToCStringArray(value, NULL, &strlist_size,
&strlist_count) == FALSE) {
if (err != NULL) {
strcpy(err, "Could not convert DNS search to string list");
}
goto done;
}
strlist = (char * *)malloc(strlist_size);
if (my_CFStringArrayToCStringArray(value,
(char *)strlist, &strlist_size,
&strlist_count) == FALSE) {
free(strlist);
goto done;
}
encoded = DNSNameListBufferCreate((const char * *)strlist,
strlist_count,
NULL, &encoded_length);
free(strlist);
if (encoded == NULL) {
if (err != NULL) {
strcpy(err, "Failed to encode DNS search");
}
goto done;
}
data = CFDataCreate(NULL, encoded, encoded_length);
free(encoded);
break;
}
default:
if (err != NULL) {
sprintf(err, "Can't convert array to %s", type_info->name);
}
goto done;
}
}
done:
return (data);
}
#define kOptionTag CFSTR("Tag")
#define kOptionData CFSTR("Data")
static CFArrayRef
createOptionsDataArrayFromDictionary(CFDictionaryRef plist, int * ret_space)
{
CFMutableArrayRef array;
int count;
int i;
const void * * keys;
int space;
const void * * values;
*ret_space = 0;
count = CFDictionaryGetCount(plist);
if (count == 0) {
return (NULL);
}
keys = malloc(count * sizeof(*keys));
values = malloc(count * sizeof(*values));
CFDictionaryGetKeysAndValues(plist, keys, values);
array = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks);
space = 0;
for (i = 0; i < count; i++) {
char err[256];
char * option_name;
CFRange range;
dhcptag_t tag;
CFDataRef this_data;
CFStringRef this_key = keys[i];
CFStringRef this_value = values[i];
if (CFStringHasPrefix(this_key, CFSTR("dhcp_")) == FALSE) {
continue;
}
range = CFRangeMake(5, CFStringGetLength(this_key));
option_name = myCFStringCreateCStringWithRange(this_key, range,
kCFStringEncodingASCII);
if (option_name == NULL) {
continue;
}
tag = dhcptag_with_name(option_name);
if (tag == -1) {
if (S_use_syslog) {
syslog(S_log_level,
"subnets: unrecognized option '%s'", option_name);
}
else {
fprintf(stderr,
"subnets: unrecognized option '%s'\n", option_name);
}
goto loop_done;
}
if (tag == dhcptag_subnet_mask_e) {
goto loop_done;
}
this_data = myCFDataCreateWithTagAndCFType(tag, (CFTypeRef)this_value,
err);
if (this_data != NULL) {
CFMutableDictionaryRef dict;
CFNumberRef num;
space += CFDataGetLength(this_data);
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
num = CFNumberCreate(NULL, kCFNumberIntType, &tag);
CFDictionarySetValue(dict, kOptionTag, num);
CFRelease(num);
CFDictionarySetValue(dict, kOptionData, this_data);
CFRelease(this_data);
CFArrayAppendValue(array, dict);
CFRelease(dict);
}
else {
if (S_use_syslog) {
syslog(S_log_level,
"subnets: Failed to convert '%s': %s",
option_name, err);
}
else {
fprintf(stderr,
"subnets: Failed to convert '%s': %s\n",
option_name, err);
}
}
loop_done:
free(option_name);
}
free(keys);
free(values);
if (CFArrayGetCount(array) == 0) {
CFRelease(array);
array = NULL;
}
else {
*ret_space = space;
}
return (array);
}
static OptionTLVRef
copyOptionsDataArrayToOptionTLVList(CFArrayRef option_list,
char * buf, int buf_space)
{
int count;
int i;
OptionTLVRef list = (OptionTLVRef)buf;
char * start_options;
count = CFArrayGetCount(option_list);
if (buf_space < (sizeof(OptionTLV) * count)) {
if (S_use_syslog) {
syslog(S_log_level,
"copyOptionsDataArrayToOptionTLVList %d < %d",
buf_space, (int)sizeof(OptionTLV) * count);
}
else {
fprintf(stderr, "copyOptionsDataArrayToOptionTLVList %d < %d\n",
buf_space, (int)sizeof(OptionTLV) * count);
}
return (NULL);
}
buf_space -= sizeof(OptionTLV) * count;
start_options = buf + sizeof(OptionTLV) * count;
for (i = 0; i < count; i++) {
CFDataRef data;
CFDictionaryRef dict = CFArrayGetValueAtIndex(option_list, i);
int this_length;
int tag;
data = CFDictionaryGetValue(dict, kOptionData);
(void)CFNumberGetValue(CFDictionaryGetValue(dict, kOptionTag),
kCFNumberIntType, &tag);
this_length = CFDataGetLength(data);
list[i].tag = tag;
list[i].length = this_length;
list[i].value = start_options;
if (buf_space < this_length) {
if (S_use_syslog) {
syslog(S_log_level,
"copyOptionsDataArrayToOptionTLVList option %d < %d",
buf_space, this_length);
}
else {
fprintf(stderr,
"copyOptionsDataArrayToOptionTLVList option %d < %d\n",
buf_space, this_length);
}
return (NULL);
}
memcpy(start_options, CFDataGetBytePtr(data), this_length);
start_options += this_length;
buf_space -= this_length;
}
return (list);
}
static void
SubnetSetLeaseMaxMin(SubnetRef subnet, CFDictionaryRef plist)
{
uint32_t l;
CFTypeRef value;
value = CFDictionaryGetValue(plist, CFSTR(SUBNET_PROP_LEASE_MAX));
if (my_CFTypeToNumber(value, &l)) {
subnet->lease_max = l;
}
else {
subnet->lease_max = DEFAULT_LEASE_MAX;
}
value = CFDictionaryGetValue(plist, CFSTR(SUBNET_PROP_LEASE_MIN));
if (my_CFTypeToNumber(value, &l)) {
subnet->lease_min = l;
}
else {
subnet->lease_min = DEFAULT_LEASE_MIN;
}
if (subnet->lease_min <= 0) {
subnet->lease_min = DEFAULT_LEASE_MIN;
}
if (subnet->lease_max < subnet->lease_min) {
subnet->lease_max = subnet->lease_min;
}
return;
}
dhcp_lease_time_t
SubnetGetMaxLease(SubnetRef subnet)
{
return (subnet->lease_max);
}
dhcp_lease_time_t
SubnetGetMinLease(SubnetRef subnet)
{
return (subnet->lease_min);
}
const char *
SubnetGetOptionPtrAndLength(SubnetRef subnet, dhcptag_t tag,
int * option_length)
{
int i;
OptionTLVRef scan;
if (tag == dhcptag_subnet_mask_e) {
*option_length = sizeof(subnet->net_mask);
return ((const char *)&subnet->net_mask);
}
for (i = 0, scan = subnet->options; i < subnet->options_count;
i++, scan++) {
if (scan->tag == tag) {
*option_length = scan->length;
return (scan->value);
}
}
return (NULL);
}
struct in_addr
SubnetGetMask(SubnetRef subnet)
{
return (subnet->net_mask);
}
static SubnetRef
SubnetCreateWithDictionary(CFDictionaryRef plist, char * err)
{
struct in_addr net_address;
struct in_addr net_mask;
ip_range_t net_range;
CFArrayRef net_range_prop;
CFStringRef name_prop;
int name_space = 0;
char * offset;
CFArrayRef option_list = NULL;
int option_space = 0;
CFStringRef prop_val;
SubnetRef subnet = NULL;
CFStringRef supernet_prop;
int supernet_space = 0;
int tail_space = 0;
if (isA_CFDictionary(plist) == NULL) {
if (err != NULL) {
strcpy(err, "subnet element is not a dictionary");
return (NULL);
}
}
if (err != NULL) {
err[0] = '\0';
}
name_prop = CFDictionaryGetValue(plist, CFSTR(SUBNET_PROP_NAME));
if (name_prop != NULL) {
if (isA_CFString(name_prop) == NULL) {
if (err != NULL) {
strcpy(err, "Invalid '" SUBNET_PROP_NAME "' property");
}
goto failed;
}
name_space
= my_CFStringToCStringAndLength(name_prop, NULL, 0);
if (name_space <= 1) {
name_space = 0;
}
else {
tail_space += name_space;
}
}
supernet_prop = CFDictionaryGetValue(plist, CFSTR(SUBNET_PROP_SUPERNET));
if (supernet_prop != NULL) {
if (isA_CFString(supernet_prop) == NULL) {
if (err != NULL) {
strcpy(err, "Invalid '" SUBNET_PROP_SUPERNET "' property");
}
goto failed;
}
supernet_space
= my_CFStringToCStringAndLength(supernet_prop, NULL, 0);
if (supernet_space <= 1) {
supernet_space = 0;
}
else {
tail_space += supernet_space;
}
}
prop_val = CFDictionaryGetValue(plist, CFSTR(SUBNET_PROP_NET_ADDRESS));
if (isA_CFString(prop_val) == NULL
|| my_CFStringToIPAddress(prop_val, &net_address) == FALSE) {
if (err != NULL) {
strcpy(err,
"Invalid/missing '" SUBNET_PROP_NET_ADDRESS "' property");
}
goto failed;
}
prop_val = CFDictionaryGetValue(plist, CFSTR(SUBNET_PROP_NET_MASK));
if (isA_CFString(prop_val) == NULL
|| my_CFStringToIPAddress(prop_val, &net_mask) == FALSE) {
if (err != NULL) {
strcpy(err,
"Invalid/missing '" SUBNET_PROP_NET_MASK "' property");
}
goto failed;
}
net_range_prop = CFDictionaryGetValue(plist, CFSTR(SUBNET_PROP_NET_RANGE));
if (isA_CFArray(net_range_prop) == NULL) {
if (err != NULL) {
strcpy(err,
"Invalid/missing '" SUBNET_PROP_NET_RANGE "' property");
}
goto failed;
}
if (CFArrayGetCount(net_range_prop) != 2) {
if (err != NULL) {
strcpy(err,
"'" SUBNET_PROP_NET_RANGE "' property must specify 2 values");
}
goto failed;
}
prop_val = CFArrayGetValueAtIndex(net_range_prop, 0);
if (isA_CFString(prop_val) == NULL
|| my_CFStringToIPAddress(prop_val, &net_range.start) == FALSE) {
if (err != NULL) {
strcpy(err, "Invalid '" SUBNET_PROP_NET_RANGE "' range start");
}
goto failed;
}
prop_val = CFArrayGetValueAtIndex(net_range_prop, 1);
if (isA_CFString(prop_val) == NULL
|| my_CFStringToIPAddress(prop_val, &net_range.end) == FALSE) {
if (err != NULL) {
strcpy(err, "Invalid '" SUBNET_PROP_NET_RANGE "' range end");
}
goto failed;
}
if (in_subnet(net_address, net_mask, net_range.start) == FALSE) {
if (err != NULL) {
strcpy(err, "'" SUBNET_PROP_NET_RANGE "' start not within subnet");
}
goto failed;
}
if (in_subnet(net_address, net_mask, net_range.end) == FALSE) {
if (err != NULL) {
strcpy(err, "'" SUBNET_PROP_NET_RANGE "' end not within subnet");
}
goto failed;
}
if (name_space == 0) {
name_prop = NULL;
name_space = strlen(inet_nettoa(net_address, net_mask)) + 1;
tail_space += name_space;
}
option_list = createOptionsDataArrayFromDictionary(plist, &option_space);
if (option_list != NULL) {
option_space = roundup(option_space, sizeof(char *))
+ CFArrayGetCount(option_list) * sizeof(OptionTLV);
tail_space += option_space;
}
subnet = malloc(sizeof(*subnet) + tail_space);
bzero(subnet, sizeof(*subnet));
SubnetSetLeaseMaxMin(subnet, plist);
subnet->net_address = net_address;
subnet->net_mask = net_mask;
subnet->net_range = net_range;
subnet->allocate = S_get_plist_boolean(plist, CFSTR("allocate"), FALSE);
offset = (char *)(subnet + 1);
if (option_list != NULL) {
subnet->options_count = CFArrayGetCount(option_list);
subnet->options =
copyOptionsDataArrayToOptionTLVList(option_list, offset,
option_space);
offset += option_space;
CFRelease(option_list);
}
subnet->name = offset;
if (name_prop != NULL) {
my_CFStringToCStringAndLength(name_prop, offset, name_space);
}
else {
strncpy(offset, inet_nettoa(net_address, net_mask), name_space);
}
offset += name_space;
if (supernet_space != 0) {
my_CFStringToCStringAndLength(supernet_prop, offset, supernet_space);
subnet->supernet = offset;
offset += supernet_space;
}
subnet->nextip = net_range.start;
failed:
return (subnet);
}
void
SubnetListLogErrors(int level)
{
S_use_syslog = TRUE;
S_log_level = level;
return;
}
static __inline__ int
SubnetListCount(SubnetListRef subnets)
{
return (dynarray_count(&subnets->list));
}
static __inline__ SubnetRef
SubnetListElement(SubnetListRef subnets, int i)
{
return (dynarray_element(&subnets->list, i));
}
static bool
SubnetListAddSubnet(SubnetListRef subnets, SubnetRef new_entry)
{
int count;
int i;
count = SubnetListCount(subnets);
for (i = 0; i < count; i++) {
int c;
SubnetRef entry = SubnetListElement(subnets, i);
bool overlap = FALSE;
c = SubnetCompareWithSubnet(new_entry, entry, &overlap);
if (overlap) {
if (S_use_syslog) {
syslog(S_log_level,
"subnets: net_range in '%s' overlaps with subnet '%s'",
SubnetGetName(new_entry),
SubnetGetName(entry));
}
else {
fprintf(stderr,
"subnets: net_range in '%s' overlaps with '%s'\n",
SubnetGetName(new_entry),
SubnetGetName(entry));
}
return (FALSE);
}
if (c < 0) {
dynarray_insert(&subnets->list, new_entry, i);
return (TRUE);
}
}
dynarray_add(&subnets->list, new_entry);
return (TRUE);
}
static void
SubnetFree(SubnetRef subnet)
{
free(subnet);
return;
}
SubnetListRef
SubnetListCreateWithArray(CFArrayRef list)
{
char err[256];
int count;
int i;
SubnetListRef subnets = NULL;
if (isA_CFArray(list) == NULL) {
if (S_use_syslog) {
syslog(S_log_level,
"subnets: type is not an array");
}
else {
fprintf(stderr, "subnets: type is not an array\n");
}
return (NULL);
}
subnets = (SubnetListRef)malloc(sizeof(*subnets));
if (subnets == NULL) {
return (NULL);
}
bzero(subnets, sizeof(*subnets));
dynarray_init(&subnets->list, (void *)SubnetFree, NULL);
count = CFArrayGetCount(list);
for (i = 0; i < count; i++) {
CFDictionaryRef dict = CFArrayGetValueAtIndex(list, i);
SubnetRef entry;
entry = SubnetCreateWithDictionary(dict, err);
if (entry == NULL) {
if (S_use_syslog) {
syslog(S_log_level,
"subnets: create failed, %s", err);
}
else {
fprintf(stderr, "subnets: create failed, %s\n", err);
}
goto failed;
}
if (SubnetListAddSubnet(subnets, entry) == FALSE) {
SubnetFree(entry);
goto failed;
}
}
return (subnets);
failed:
SubnetListFree(&subnets);
return (NULL);
}
void
SubnetListFree(SubnetListRef * subnets_p)
{
SubnetListRef subnets;
if (subnets_p == NULL) {
return;
}
subnets = *subnets_p;
if (subnets == NULL) {
return;
}
dynarray_free(&subnets->list);
free(subnets);
*subnets_p = NULL;
return;
}
void
SubnetListPrint(SubnetListRef subnets)
{
int count;
int i;
count = SubnetListCount(subnets);
printf("There are %d entries\n", count);
for (i = 0; i < count; i++) {
SubnetRef entry = SubnetListElement(subnets, i);
printf("%2d. ", i + 1);
SubnetPrint(entry);
}
return;
}
SubnetRef
SubnetListAcquireAddress(SubnetListRef subnets, struct in_addr * addr,
SubnetIsAddressInUseFuncRef func, void * arg)
{
int count;
SubnetRef entry;
int i;
struct in_addr subnet_address = *addr;
entry = SubnetListGetSubnetForAddress(subnets, subnet_address, FALSE);
if (entry == NULL) {
return (NULL);
}
count = SubnetListCount(subnets);
for (i = 0; i < count; i++) {
SubnetRef this_entry = SubnetListElement(subnets, i);
if (this_entry == entry
|| SubnetIsAddressOnSubnet(this_entry, subnet_address)
|| SubnetSameSupernetAsSubnet(this_entry, entry)) {
if (SubnetAcquireAddress(this_entry, func, arg, addr)) {
return (this_entry);
}
}
}
return (NULL);
}
SubnetRef
SubnetListGetSubnetForAddress(SubnetListRef subnets, struct in_addr addr,
bool in_range)
{
bool (*func)(SubnetRef subnet, struct in_addr addr);
int count;
int i;
count = SubnetListCount(subnets);
func = in_range ? SubnetIsAddressWithinRange : SubnetIsAddressOnSubnet;
for (i = 0; i < count; i++) {
SubnetRef entry = SubnetListElement(subnets, i);
if ((*func)(entry, addr)) {
return (entry);
}
}
return (NULL);
}
bool
SubnetListAreAddressesOnSameSupernet(SubnetListRef subnets,
struct in_addr addr1,
struct in_addr addr2)
{
SubnetRef subnet1;
SubnetRef subnet2;
subnet1 = SubnetListGetSubnetForAddress(subnets, addr1, FALSE);
subnet2 = SubnetListGetSubnetForAddress(subnets, addr2, FALSE);
if (subnet1 == NULL || subnet2 == NULL) {
return (FALSE);
}
if (subnet1 == subnet2) {
return (TRUE);
}
return (SubnetSameSupernetAsSubnet(subnet1, subnet2));
}
#ifdef TEST_SUBNETS
int
main(int argc, const char * argv[])
{
CFArrayRef list;
SubnetListRef subnets;
CFDictionaryRef plist;
if (argc != 2) {
fprintf(stderr, "Usage: subnets <file>\n");
exit(1);
}
plist = my_CFPropertyListCreateFromFile(argv[1]);
if (isA_CFDictionary(plist) == NULL) {
fprintf(stderr, "failed to load %s\n", argv[1]);
exit(1);
}
list = CFDictionaryGetValue(plist, CFSTR("Subnets"));
subnets = SubnetListCreateWithArray(list);
if (subnets != NULL) {
struct in_addr ip;
SubnetRef subnet;
inet_aton("17.202.42.110", &ip);
SubnetListPrint(subnets);
subnet = SubnetListAcquireAddress(subnets, &ip, NULL, NULL);
if (subnet != NULL) {
SubnetPrint(subnet);
printf("Allocated %s\n", inet_ntoa(ip));
}
SubnetListFree(&subnets);
}
else {
fprintf(stderr, "SubnetListCreateWithArray failed\n");
}
exit(0);
}
#endif TEST_SUBNETS