/* * Copyright (c) 1999 - 2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * client.c * - client side program to talk to ipconfigd */ /* * Modification History * * September, 1999 Dieter Siegmund (dieter@apple.com) * - initial revision * July 31, 2000 Dieter Siegmund (dieter@apple.com) * - changed to add set, and new implementation of waitall * - removed waitif * February 24, 2003 Dieter Siegmund (dieter@apple.com) * - added support to retrieve information about the netboot (bsdp) * packet stored in the device tree */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipconfig_ext.h" #include "ipconfig_types.h" #include "dhcp_options.h" #include "dhcplib.h" #include "bsdp.h" #include "bsdplib.h" #include "ioregpath.h" #include "ipconfig.h" #include #include typedef int func_t(mach_port_t server, int argc, char * argv[]); typedef func_t * funcptr_t; static char * progname; static char * command_name; #define STARTUP_KEY CFSTR("Plugin:IPConfiguration") #define SHADOW_MOUNT_PATH_COMMAND "shadow_mount_path" #define SHADOW_FILE_PATH_COMMAND "shadow_file_path" #define MACHINE_NAME_COMMAND "machine_name" static void on_alarm(int sigraised) { exit(0); } #define WAIT_ALL_DEFAULT_TIMEOUT 90 #define WAIT_ALL_MAX_TIMEOUT 120 static void key_appeared(SCDynamicStoreRef session, CFArrayRef changes, void * arg) { exit(0); } static int S_wait_all(mach_port_t server, int argc, char * argv[]) { CFMutableArrayRef keys; SCDynamicStoreRef session; CFRunLoopSourceRef rls; unsigned long t = WAIT_ALL_DEFAULT_TIMEOUT; CFPropertyListRef value; struct itimerval v; if (argc > 0) { t = strtoul(argv[0], 0, 0); if (t > WAIT_ALL_MAX_TIMEOUT) { t = WAIT_ALL_MAX_TIMEOUT; } } session = SCDynamicStoreCreate(NULL, CFSTR("ipconfig command"), key_appeared, NULL); if (session == NULL) { fprintf(stderr, "SCDynamicStoreCreate failed: %s\n", SCErrorString(SCError())); return (0); } keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); CFArrayAppendValue(keys, STARTUP_KEY); SCDynamicStoreSetNotificationKeys(session, keys, NULL); CFRelease(keys); rls = SCDynamicStoreCreateRunLoopSource(NULL, session, 0); CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); CFRelease(rls); signal(SIGALRM, on_alarm); bzero(&v, sizeof(v)); v.it_value.tv_sec = t; if (setitimer(ITIMER_REAL, &v, NULL) < 0) { perror("setitimer"); return (0); } value = SCDynamicStoreCopyValue(session, STARTUP_KEY); if (value == NULL) { CFRunLoopRun(); return (0); } CFRelease(session); return (0); } #if 0 static int S_wait_if(mach_port_t server, int argc, char * argv[]) { return (0); if_name_t name; kern_return_t status; strlcpy(name, argv[0], sizeof(name)); status = ipconfig_wait_if(server, name); if (status == KERN_SUCCESS) { return (0); } fprintf(stderr, "wait if %s failed, %s\n", name, mach_error_string(status)); return (1); } #endif 0 static int S_bsdp_get_packet(mach_port_t server, int argc, char * argv[]) { CFDictionaryRef chosen = NULL; struct dhcp * dhcp; int length; CFDataRef response = NULL; int ret = 1; if (getuid() != 0) { return (EX_NOPERM); } chosen = myIORegistryEntryCopyValue("IODeviceTree:/chosen"); if (chosen == NULL) { goto done; } response = CFDictionaryGetValue(chosen, CFSTR("bsdp-response")); if (isA_CFData(response) == NULL) { response = CFDictionaryGetValue(chosen, CFSTR("bootp-response")); if (isA_CFData(response) == NULL) { goto done; } } dhcp = (struct dhcp *)CFDataGetBytePtr(response); length = CFDataGetLength(response); bsdp_print_packet(dhcp, length, 0); ret = 0; done: return (ret); } static int S_bsdp_option(mach_port_t server, int argc, char * argv[]) { CFDictionaryRef chosen = NULL; void * data = NULL; int data_len; struct dhcp * dhcp; int length; dhcpol_t options; CFDataRef response = NULL; int ret = 1; int tag = 0; dhcpol_t vendor_options; int vendor_tag = 0; if (getuid() != 0) { return (EX_NOPERM); } chosen = myIORegistryEntryCopyValue("IODeviceTree:/chosen"); if (chosen == NULL) { goto done; } response = CFDictionaryGetValue(chosen, CFSTR("bsdp-response")); if (isA_CFData(response) == NULL) { response = CFDictionaryGetValue(chosen, CFSTR("bootp-response")); if (isA_CFData(response) == NULL) { goto done; } } dhcp = (struct dhcp *)CFDataGetBytePtr(response); length = CFDataGetLength(response); if (dhcpol_parse_packet(&options, dhcp, length, NULL) == FALSE) { goto done; } if (strcmp(argv[0], SHADOW_MOUNT_PATH_COMMAND) == 0) { tag = dhcptag_vendor_specific_e; vendor_tag = bsdptag_shadow_mount_path_e; } else if (strcmp(argv[0], SHADOW_FILE_PATH_COMMAND) == 0) { tag = dhcptag_vendor_specific_e; vendor_tag = bsdptag_shadow_file_path_e; } else if (strcmp(argv[0], MACHINE_NAME_COMMAND) == 0) { tag = dhcptag_vendor_specific_e; vendor_tag = bsdptag_machine_name_e; } else { tag = atoi(argv[0]); if (argc == 2) { vendor_tag = atoi(argv[1]); } } if (tag == dhcptag_vendor_specific_e && vendor_tag != 0) { if (dhcpol_parse_vendor(&vendor_options, &options, NULL) == FALSE) { goto done; } data = dhcpol_get(&vendor_options, vendor_tag, &data_len); if (data != NULL) { dhcptype_print(bsdptag_type(vendor_tag), data, data_len); ret = 0; } dhcpol_free(&vendor_options); } else { const dhcptag_info_t * entry; entry = dhcptag_info(tag); if (entry == NULL) { goto done; } data = dhcpol_get(&options, tag, &data_len); if (data != NULL) { dhcptype_print(entry->type, data, data_len); ret = 0; } } done: if (data != NULL) { free(data); } if (chosen != NULL) { CFRelease(chosen); } return (ret); } static int S_if_addr(mach_port_t server, int argc, char * argv[]) { struct in_addr ip; if_name_t name; kern_return_t status; strlcpy(name, argv[0], sizeof(name)); status = ipconfig_if_addr(server, name, (ip_address_t *)&ip); if (status == KERN_SUCCESS) { printf("%s\n", inet_ntoa(ip)); return (0); } fprintf(stderr, "get if addr %s failed, %s\n", name, mach_error_string(status)); return (1); } static int S_if_count(mach_port_t server, int argc, char * argv[]) { int count = 0; kern_return_t status; status = ipconfig_if_count(server, &count); if (status == KERN_SUCCESS) { printf("%d\n", count); return (0); } fprintf(stderr, "get if count failed, %s\n", mach_error_string(status)); return (1); } static int S_get_option(mach_port_t server, int argc, char * argv[]) { char buf[1024]; inline_data_t data; unsigned int data_len = sizeof(data); if_name_t name; dhcpo_err_str_t err; int tag; kern_return_t status; strlcpy(name, argv[0], sizeof(name)); tag = dhcptag_with_name(argv[1]); if (tag == -1) tag = atoi(argv[1]); status = ipconfig_get_option(server, name, tag, data, &data_len); if (status == KERN_SUCCESS) { if (dhcptag_to_str(buf, sizeof(buf), tag, data, data_len, &err)) { printf("%s\n", buf); return (0); } fprintf(stderr, "couldn't convert the option, %s\n", err.str); } else { fprintf(stderr, "ipconfig_get_option failed, %s\n", mach_error_string(status)); } return (1); } static int S_get_packet(mach_port_t server, int argc, char * argv[]) { inline_data_t data; unsigned int data_len = sizeof(data); if_name_t name; int ret = 1; kern_return_t status; strlcpy(name, argv[0], sizeof(name)); status = ipconfig_get_packet(server, name, data, &data_len); if (status == KERN_SUCCESS) { dhcp_print_packet((struct dhcp *)data, data_len); ret = 0; } return (ret); } static __inline__ boolean_t ipconfig_method_from_string(const char * m, ipconfig_method_t * method) { if (strcmp(m, "BOOTP") == 0) { *method = ipconfig_method_bootp_e; } else if (strcmp(m, "DHCP") == 0) { *method = ipconfig_method_dhcp_e; } else if (strcmp(m, "MANUAL") == 0) { *method = ipconfig_method_manual_e; } else if (strcmp(m, "INFORM") == 0) { *method = ipconfig_method_inform_e; } else if (strcmp(m, "LINKLOCAL") == 0) { *method = ipconfig_method_linklocal_e; } else if (strcmp(m, "FAILOVER") == 0) { *method = ipconfig_method_failover_e; } else if (strcmp(m, "NONE") == 0) { *method = ipconfig_method_none_e; } else { return (FALSE); } return (TRUE); } static void get_method_information(int argc, char * argv[], const char * cmd, ipconfig_method_t * method_p, ipconfig_method_data_t * data, int * data_len_p) { char * method_name; method_name = argv[0]; if (ipconfig_method_from_string(method_name, method_p) == FALSE) { fprintf(stderr, "ipconfig: %s: method '%s' unknown\n", cmd, method_name); fprintf(stderr, "must be MANUAL, INFORM, BOOTP, DHCP, FAILOVER, or NONE\n"); exit(1); } argc--; argv++; switch (*method_p) { case ipconfig_method_failover_e: if (argc != 1 && argc != 2 && argc != 3) { fprintf(stderr, "usage: ipconfig %s en0 %s " "[ [ ] ]\n", cmd, method_name); exit(1); } goto common; case ipconfig_method_inform_e: case ipconfig_method_manual_e: if (argc != 1 && argc != 2) { fprintf(stderr, "usage: ipconfig %s en0 %s [ ]\n", cmd, method_name); exit(1); } common: bzero(data, *data_len_p); if (*data_len_p < (sizeof(*data) + sizeof(data->ip[0]))) { fprintf(stderr, "Invalid size passed %d < %ld\n", *data_len_p, sizeof(*data) + sizeof(data->ip[0])); exit(1); } data->n_ip = 1; if (inet_aton(argv[0], &data->ip[0].addr) == 0) { fprintf(stderr, "Invalid IP address %s\n", argv[0]); exit(1); } if (argc >= 2) { if (inet_aton(argv[1], &data->ip[0].mask) == 0) { fprintf(stderr, "Invalid IP mask %s\n", argv[1]); exit(1); } } if (argc >= 3) { data->u.failover_timeout = strtoul(argv[2], NULL, 0); } *data_len_p = sizeof(*data) + sizeof(data->ip[0]); break; default: if (argc) { fprintf(stderr, "too many arguments for method\n"); exit(1); } *data_len_p = 0; break; } return; } static int S_set(mach_port_t server, int argc, char * argv[]) { char buf[IPCONFIG_METHOD_DATA_MIN_SIZE]; ipconfig_method_data_t * data = (ipconfig_method_data_t *)buf; int data_len; if_name_t name; char * method_name; ipconfig_method_t method; kern_return_t status; ipconfig_status_t ipstatus = ipconfig_status_success_e; strcpy(name, argv[0]); argv++; argc--; method_name = argv[0]; data_len = sizeof(buf); get_method_information(argc, argv, command_name, &method, data, &data_len); status = ipconfig_set(server, name, method, (void *)data, data_len, &ipstatus); if (status != KERN_SUCCESS) { mach_error("ipconfig_set failed", status); return (1); } if (ipstatus != ipconfig_status_success_e) { fprintf(stderr, "ipconfig_set %s %s failed: %s\n", name, method_name, ipconfig_status_string(ipstatus)); return (1); } return (0); } static int S_set_verbose(mach_port_t server, int argc, char * argv[]) { ipconfig_status_t ipstatus = ipconfig_status_success_e; kern_return_t status; int verbose; verbose = strtol(argv[0], NULL, 0); errno = 0; if (verbose == 0 && errno != 0) { fprintf(stderr, "conversion to integer of %s failed\n", argv[0]); return (1); } status = ipconfig_set_verbose(server, verbose, &ipstatus); if (status != KERN_SUCCESS) { return (1); } if (ipstatus != ipconfig_status_success_e) { fprintf(stderr, "setverbose failed: %s\n", ipconfig_status_string(ipstatus)); return (1); } return (0); } #ifdef IPCONFIG_TEST_NO_ENTRY static int S_set_something(mach_port_t server, int argc, char * argv[]) { ipconfig_status_t ipstatus = ipconfig_status_success_e; kern_return_t status; int verbose; verbose = strtol(argv[0], NULL, 0); errno = 0; if (verbose == 0 && errno != 0) { fprintf(stderr, "conversion to integer of %s failed\n", argv[0]); return (1); } status = ipconfig_set_something(server, verbose, &ipstatus); if (status != KERN_SUCCESS) { return (1); } if (ipstatus != ipconfig_status_success_e) { fprintf(stderr, "setsomething failed: %s\n", ipconfig_status_string(ipstatus)); return (1); } return (0); } #endif IPCONFIG_TEST_NO_ENTRY static int S_add_or_set_service(mach_port_t server, int argc, char * argv[], bool add) { char buf[IPCONFIG_METHOD_DATA_MIN_SIZE]; ipconfig_method_data_t * data = (ipconfig_method_data_t *)buf; int data_len; if_name_t name; char * method_name; ipconfig_method_t method; inline_data_t service_id; unsigned int service_id_len = sizeof(service_id); kern_return_t status; ipconfig_status_t ipstatus = ipconfig_status_success_e; strcpy(name, argv[0]); argv++; argc--; method_name = argv[0]; data_len = sizeof(buf); get_method_information(argc, argv, command_name, &method, data, &data_len); if (add) { status = ipconfig_add_service(server, name, method, (void *)data, data_len, service_id, &service_id_len, &ipstatus); } else { status = ipconfig_set_service(server, name, method, (void *)data, data_len, service_id, &service_id_len, &ipstatus); } if (status != KERN_SUCCESS) { fprintf(stderr, "ipconfig_%s_service failed, %s\n", add ? "add" : "set", mach_error_string(status)); return (1); } if (ipstatus != ipconfig_status_success_e) { fprintf(stderr, "ipconfig_%s_service %s %s failed: %s\n", add ? "add" : "set", name, method_name, ipconfig_status_string(ipstatus)); return (1); } printf("%.*s\n", service_id_len, service_id); return (0); } static int S_add_service(mach_port_t server, int argc, char * argv[]) { return (S_add_or_set_service(server, argc, argv, TRUE)); } static int S_set_service(mach_port_t server, int argc, char * argv[]) { return (S_add_or_set_service(server, argc, argv, FALSE)); } static int S_remove_service_with_id(mach_port_t server, int argc, char * argv[]) { inline_data_t service_id; unsigned int service_id_len; kern_return_t status; ipconfig_status_t ipstatus = ipconfig_status_success_e; service_id_len = strlen(argv[0]); if (service_id_len > sizeof(service_id)) { service_id_len = sizeof(service_id); } memcpy(service_id, argv[0], service_id_len); status = ipconfig_remove_service_with_id(server, service_id, service_id_len, &ipstatus); if (status != KERN_SUCCESS) { mach_error("ipconfig_remove_service_with_id failed", status); return (1); } if (ipstatus != ipconfig_status_success_e) { fprintf(stderr, "ipconfig_remove_service_with_id %s failed: %s\n", argv[0], ipconfig_status_string(ipstatus)); return (1); } return (0); } static int S_find_service(mach_port_t server, int argc, char * argv[]) { char buf[IPCONFIG_METHOD_DATA_MIN_SIZE]; ipconfig_method_data_t * data = (ipconfig_method_data_t *)buf; int data_len; if_name_t name; boolean_t exact = FALSE; char * method_name; ipconfig_method_t method; inline_data_t service_id; unsigned int service_id_len = sizeof(service_id); kern_return_t status; ipconfig_status_t ipstatus = ipconfig_status_success_e; strcpy(name, argv[0]); argv++; argc--; if (strcasecmp(argv[0], "exact") == 0) { exact = TRUE; argc--; argv++; } method_name = argv[0]; data_len = sizeof(buf); get_method_information(argc, argv, command_name, &method, data, &data_len); status = ipconfig_find_service(server, name, exact, method, (void *)data, data_len, service_id, &service_id_len, &ipstatus); if (status != KERN_SUCCESS) { mach_error("ipconfig_find_service failed", status); return (1); } if (ipstatus != ipconfig_status_success_e) { fprintf(stderr, "ipconfig_find_service %s %s failed: %s\n", name, method_name, ipconfig_status_string(ipstatus)); return (1); } printf("%.*s\n", service_id_len, service_id); return (0); } static int S_remove_service(mach_port_t server, int argc, char * argv[]) { char buf[IPCONFIG_METHOD_DATA_MIN_SIZE]; ipconfig_method_data_t * data = (ipconfig_method_data_t *)buf; int data_len; if_name_t name; char * method_name; ipconfig_method_t method; kern_return_t status; ipconfig_status_t ipstatus = ipconfig_status_success_e; strcpy(name, argv[0]); argv++; argc--; method_name = argv[0]; data_len = sizeof(buf); get_method_information(argc, argv, command_name, &method, data, &data_len); status = ipconfig_remove_service(server, name, method, (void *)data, data_len, &ipstatus); if (status != KERN_SUCCESS) { mach_error("ipconfig_remove_service failed", status); return (1); } if (ipstatus != ipconfig_status_success_e) { fprintf(stderr, "ipconfig_remove_service %s %s failed: %s\n", name, method_name, ipconfig_status_string(ipstatus)); return (1); } return (0); } static const struct command_info { const char *command; funcptr_t func; int argc; const char *usage; int display; int no_server; } commands[] = { { "waitall", S_wait_all, 0, "[ timeout secs ]", 1, 1 }, { "getifaddr", S_if_addr, 1, "", 1, 0 }, #if 0 { "waitif", S_wait_if, 1, " ", 1, 0 }, #endif 0 { "ifcount", S_if_count, 0, "", 1, 0 }, { "getoption", S_get_option, 2, "