Пример #1
0
static int
ni_dhcp_option_get_domain(ni_buffer_t *bp, char **var, const char *what)
{
	unsigned int len;
	char *tmp = NULL;

	if (ni_dhcp_option_get_string(bp, &tmp, &len) < 0)
		return -1;

	if (!ni_check_domain_name(tmp, len, 0)) {
		ni_debug_dhcp("Discarded suspect %s: %s", what,
			ni_print_suspect(tmp, len));
		free(tmp);
		return -1;
	}

	if (*var)
		free(*var);
	*var = tmp;
	return 0;
}
Пример #2
0
static int
ni_dhcp4_option_get_domain_list(ni_buffer_t *bp, ni_string_array_t *var,
					const char *what)
{
	ni_string_array_t list = NI_STRING_ARRAY_INIT;
	unsigned int len, i;
	char *tmp = NULL;

	if (ni_dhcp4_option_get_string(bp, &tmp, &len) < 0)
		return -1;

	/*
	 * Hack to accept "compatibility abuse" of dns domain name
	 * option containing multiple domains instead to send them
	 * using a dns-search option...
	 */
	if (!ni_string_split(&list, tmp, " ", 0)) {
		ni_warn("Discarded suspect %s: '%s'", what,
				ni_print_suspect(tmp, len));
		free(tmp);
		return -1;
	}
	for (i = 0; i < list.count; ++i) {
		const char *dom = list.data[i];
		if (!ni_check_domain_name(dom, ni_string_len(dom), 0)) {
			ni_warn("Discarded suspect %s: '%s'", what,
				ni_print_suspect(tmp, len));
			ni_string_array_destroy(&list);
			free(tmp);
			return -1;
		}
	}
	if (list.count != 1) {
		ni_warn("Abuse of %s option to provide a list: '%s'",
			what, tmp);
	}
	free(tmp);
	ni_string_array_move(var, &list);
	return 0;
}
Пример #3
0
static int
__ni_rtevent_process_dnssl_info(ni_netdev_t *dev, const struct nd_opt_hdr *opt, size_t len)
{
	const struct ni_nd_opt_dnssl_info_p *dopt;
	ni_ipv6_devinfo_t *ipv6;
	unsigned int lifetime;
	struct timeval acquired;
	size_t length, cnt, off;
	ni_bool_t emit = FALSE;
	char domain[256];

	if (opt == NULL || len < sizeof(*dopt)) {
		ni_error("%s: unable to parse ipv6 dnssl info event data -- too short",
				dev->name);
		return -1;
	}

	ipv6 = ni_netdev_get_ipv6(dev);
	if (!ipv6) {
		ni_error("%s: unable to allocate device ipv6 structure: %m",
				dev->name);
		return -1;
	}

	dopt = (const struct ni_nd_opt_dnssl_info_p *)opt;
	len -= sizeof(*dopt);

	ni_timer_get_time(&acquired);
	lifetime = ntohl(dopt->nd_opt_dnssl_lifetime);

	length = 0;
	domain[length] = '\0';
	for (off = 0; off < len ; ) {
		cnt = dopt->nd_opt_dnssl_list[off++];
		if (cnt == 0) {
			/* just padding */
			if (domain[0] == '\0')
				continue;

			domain[length] = '\0';
			if (!ni_check_domain_name(domain, length, 0)) {
				ni_debug_verbose(NI_LOG_DEBUG, NI_TRACE_IPV6|NI_TRACE_EVENTS,
					"%s: ignoring suspect DNSSL domain: %s",
					dev->name, ni_print_suspect(domain, length));
			} else
			if (!ni_ipv6_ra_dnssl_list_update(&ipv6->radv.dnssl,
						domain, lifetime, &acquired)) {
				ni_debug_verbose(NI_LOG_DEBUG, NI_TRACE_IPV6|NI_TRACE_EVENTS,
						"%s: unable to track ipv6 dnssl domain %s",
						dev->name, domain);
			} else
				emit = TRUE;

			length = 0;
			domain[length] = '\0';
			continue;
		}

		if ((off + cnt >= len) || (length + cnt + 2 > sizeof(domain)))
			break;

		if (length)
			domain[length++] = '.';
		memcpy(&domain[length], &dopt->nd_opt_dnssl_list[off], cnt);
		off += cnt;
		length += cnt;
		domain[length] = '\0';
	}

	if (emit)
		__ni_netdev_nduseropt_event(dev, NI_EVENT_DNSSL_UPDATE);
	return 0;
}
Пример #4
0
static ni_bool_t
ni_dhcp4_tester_req_xml_init(ni_dhcp4_request_t *req, xml_document_t *doc)
{
	xml_node_t *xml, *child;
	const char *type;

	xml = xml_document_root(doc);
	if (xml && !xml->name && xml->children)
		xml = xml->children;

	if (!xml || !ni_string_eq(xml->name, "request")) {
		ni_error("Invalid dhcp4 request xml '%s'",
				xml ? xml_node_location(xml) : NULL);
		return FALSE;
	}

	type = xml_node_get_attr(xml, "type");
	if (ni_string_eq(type, "offer")) {
		req->dry_run = NI_DHCP4_RUN_OFFER;
	} else
	if (ni_string_eq(type, "lease")) {
		req->dry_run = NI_DHCP4_RUN_LEASE;
	}

	for (child = xml->children; child; child = child->next) {
		if (ni_string_eq(child->name, "uuid")) {
			if (ni_uuid_parse(&req->uuid, child->cdata) != 0)
				goto failure;
		} else
		if (ni_string_eq(child->name, "acquire-timeout")) {
			if (ni_parse_uint(child->cdata, &req->acquire_timeout, 10) != 0)
				goto failure;
		} else
		if (ni_string_eq(child->name, "hostname")) {
			if (!ni_check_domain_name(child->cdata, ni_string_len(child->cdata), 0))
				goto failure;
			ni_string_dup(&req->hostname, child->cdata);
		} else
		if (ni_string_eq(child->name, "fqdn")) {
			const xml_node_t *ptr;

			for (ptr = child->children; ptr; ptr = ptr->next) {
				if (ni_string_eq(ptr->name, "enabled")) {
					ni_bool_t b;
					if (ni_parse_boolean(ptr->cdata, &b) == 0)
						ni_tristate_set(&req->fqdn.enabled, b);
					else
					if (ni_string_eq(ptr->cdata, "default"))
						req->fqdn.enabled = NI_TRISTATE_DEFAULT;
					else
						goto failure;
				} else
				if (ni_string_eq(ptr->name, "update")) {
					if (!ni_dhcp_fqdn_update_name_to_mode(ptr->cdata, &req->fqdn.update))
						goto failure;
				} else
				if (ni_string_eq(ptr->name, "encode")) {
					if (ni_parse_boolean(ptr->cdata, &req->fqdn.encode) != 0)
						goto failure;
				} else
				if (ni_string_eq(ptr->name, "qualify")) {
					if (ni_parse_boolean(ptr->cdata, &req->fqdn.qualify) != 0)
						goto failure;
				}
			}
		} else
		if (ni_string_eq(child->name, "clientid")) {
			ni_opaque_t duid;

			if (ni_parse_hex(child->cdata, duid.data, sizeof(duid.data)) <= 0)
				goto failure;
			ni_string_dup(&req->clientid, child->cdata);
		} else
		if(ni_string_eq(child->name, "start-delay")) {
			if (ni_parse_uint(child->cdata, &req->start_delay, 10) != 0)
				goto failure;
		} else
		if (ni_string_eq(child->name, "lease-time")) {
			if (ni_parse_uint(child->cdata, &req->lease_time, 10) != 0)
				goto failure;
		} else
		if (ni_string_eq(child->name, "recover-lease")) {
			if (ni_parse_boolean(child->cdata, &req->recover_lease) != 0)
				goto failure;
		} else
		if (ni_string_eq(child->name, "release-lease")) {
			if (ni_parse_boolean(child->cdata, &req->release_lease) != 0)
				goto failure;
		} else
		if (ni_string_eq(child->name, "request-options")) {
			xml_node_t *opt;
			for (opt = child->children; opt; opt = opt->next) {
				if (ni_string_empty(opt->cdata))
					continue;
				ni_string_array_append(&req->request_options, opt->cdata);
			}
		}
	}

	return TRUE;
failure:
	if (child) {
		ni_error("Cannot parse dhcp4 request '%s': %s",
				child->name, xml_node_location(child));
	}
	return FALSE;
}
Пример #5
0
static ni_bool_t
dhcp4_tester_req_xml_init(ni_dhcp4_request_t *req, xml_document_t *doc)
{
	xml_node_t *xml, *child;
	const char *type;

	xml = xml_document_root(doc);
	if (xml && !xml->name && xml->children)
		xml = xml->children;

	if (!xml || !ni_string_eq(xml->name, "request")) {
		ni_error("Invalid dhcp4 request xml '%s'",
				xml ? xml_node_location(xml) : NULL);
		return FALSE;
	}

	type = xml_node_get_attr(xml, "type");
	if (ni_string_eq(type, "offer")) {
		req->dry_run = NI_DHCP4_RUN_OFFER;
	} else
	if (ni_string_eq(type, "lease")) {
		req->dry_run = NI_DHCP4_RUN_LEASE;
	}

	for (child = xml->children; child; child = child->next) {
		if (ni_string_eq(child->name, "uuid")) {
			if (ni_uuid_parse(&req->uuid, child->cdata) != 0)
				goto failure;
		} else
		if (ni_string_eq(child->name, "acquire-timeout")) {
			if (ni_parse_uint(child->cdata, &req->acquire_timeout, 10) != 0)
				goto failure;
		} else
		if (ni_string_eq(child->name, "hostname")) {
			if (!ni_check_domain_name(child->cdata, ni_string_len(child->cdata), 0))
				goto failure;
			ni_string_dup(&req->hostname, child->cdata);
		} else
		if (ni_string_eq(child->name, "clientid")) {
			ni_opaque_t duid;

			if (ni_parse_hex(child->cdata, duid.data, sizeof(duid.data)) <= 0)
				goto failure;
			ni_string_dup(&req->clientid, child->cdata);
		} else
		if(ni_string_eq(child->name, "start-delay")) {
			if (ni_parse_uint(child->cdata, &req->start_delay, 10) != 0)
				goto failure;
		} else
		if (ni_string_eq(child->name, "lease-time")) {
			if (ni_parse_uint(child->cdata, &req->lease_time, 10) != 0)
				goto failure;
		} else
		if (ni_string_eq(child->name, "recover-lease")) {
			if (ni_parse_boolean(child->cdata, &req->recover_lease) != 0)
				goto failure;
		} else
		if (ni_string_eq(child->name, "release-lease")) {
			if (ni_parse_boolean(child->cdata, &req->release_lease) != 0)
				goto failure;
		}
	}

	return TRUE;
failure:
	if (child) {
		ni_error("Cannot parse dhcp4 request '%s': %s",
				child->name, xml_node_location(child));
	}
	return FALSE;
}
Пример #6
0
/*
 * Process a request to reconfigure the device (ie rebind a lease, or discover
 * a new lease).
 */
int
ni_dhcp4_acquire(ni_dhcp4_device_t *dev, const ni_dhcp4_request_t *info)
{
	ni_dhcp4_config_t *config;
	const char *classid;
	size_t len;
	int rv;

	if ((rv = ni_dhcp4_device_refresh(dev)) < 0)
		return rv;

	config = xcalloc(1, sizeof(*config));
	config->dry_run = info->dry_run;
	config->resend_timeout = NI_DHCP4_RESEND_TIMEOUT_INIT;
	config->request_timeout = info->acquire_timeout?: NI_DHCP4_REQUEST_TIMEOUT;
	config->initial_discovery_timeout = NI_DHCP4_DISCOVERY_TIMEOUT;
	config->uuid = info->uuid;
	config->flags = info->flags;
	config->update = info->update;
	config->route_priority = info->route_priority;
	config->start_delay = info->start_delay;
	config->recover_lease = info->recover_lease;
	config->release_lease = info->release_lease;

	config->max_lease_time = ni_dhcp4_config_max_lease_time();
	if (config->max_lease_time == 0)
		config->max_lease_time = ~0U;
	if (info->lease_time && info->lease_time < config->max_lease_time)
		config->max_lease_time = info->lease_time;

	if ((len = ni_string_len(info->hostname)) > 0) {
		if (ni_check_domain_name(info->hostname, len, 0)) {
			strncpy(config->hostname, info->hostname, sizeof(config->hostname) - 1);
		} else {
			ni_debug_dhcp("Discarded request to use suspect hostname: '%s'",
				ni_print_suspect(info->hostname, len));
		}
	}

	if (info->clientid) {
		ni_dhcp4_parse_client_id(&config->client_id, dev->system.hwaddr.type,
					 info->clientid);
	} else {
		/* Set client ID from interface hwaddr */
		ni_dhcp4_set_client_id(&config->client_id, &dev->system.hwaddr);
	}

	if ((classid = info->vendor_class) == NULL)
		classid = ni_dhcp4_config_vendor_class();
	if (classid)
		strncpy(config->classid, classid, sizeof(config->classid) - 1);

	config->doflags = DHCP4_DO_DEFAULT;
	config->doflags |= ni_dhcp4_do_bits(info->update);

	if (ni_debug & NI_TRACE_DHCP) {
		ni_trace("Received request:");
		ni_trace("  acquire-timeout %u", config->request_timeout);
		ni_trace("  lease-time      %u", config->max_lease_time);
		ni_trace("  start-delay     %u", config->start_delay);
		ni_trace("  hostname        %s", config->hostname[0]? config->hostname : "<none>");
		ni_trace("  vendor-class    %s", config->classid[0]? config->classid : "<none>");
		ni_trace("  client-id       %s", ni_print_hex(config->client_id.data, config->client_id.len));
		ni_trace("  uuid            %s", ni_uuid_print(&config->uuid));
		ni_trace("  update-flags    %s", __ni_dhcp4_print_doflags(config->doflags));
		ni_trace("  recover_lease   %s", config->recover_lease ? "true" : "false");
		ni_trace("  release_lease   %s", config->release_lease ? "true" : "false");
	}

	ni_dhcp4_device_set_config(dev, config);

	if (!dev->lease)
		ni_dhcp4_recover_lease(dev);

	if (dev->lease) {
		if (!ni_addrconf_lease_is_valid(dev->lease)
		 || (config->client_id.len && !ni_opaque_eq(&config->client_id, &dev->lease->dhcp4.client_id))) {
			ni_debug_dhcp("%s: lease doesn't match request", dev->ifname);
			ni_dhcp4_device_drop_lease(dev);
			dev->notify = 1;
		}
	}

	ni_note("%s: Request to acquire DHCPv4 lease with UUID %s",
		dev->ifname, ni_uuid_print(&config->uuid));

	if (ni_dhcp4_device_start(dev) < 0)
		return -1;
	return 1;
}
Пример #7
0
/*
 * Parse a DHCP response.
 * FIXME: RFC2131 states that the server is allowed to split a DHCP option into
 * several (partial) options if the total length exceeds 255 octets. We don't
 * handle this yet.
 */
int
ni_dhcp_parse_response(const ni_dhcp_message_t *message, ni_buffer_t *options, ni_addrconf_lease_t **leasep)
{
	ni_buffer_t overload_buf;
	ni_addrconf_lease_t *lease;
	ni_route_array_t default_routes = NI_ROUTE_ARRAY_INIT;
	ni_route_array_t static_routes = NI_ROUTE_ARRAY_INIT;
	ni_route_array_t classless_routes = NI_ROUTE_ARRAY_INIT;
	ni_string_array_t dns_servers = NI_STRING_ARRAY_INIT;
	ni_string_array_t dns_search = NI_STRING_ARRAY_INIT;
	ni_string_array_t nis_servers = NI_STRING_ARRAY_INIT;
	char *nisdomain = NULL;
	char *dnsdomain = NULL;
	int opt_overload = 0;
	int msg_type = -1;
	int use_bootserver = 1;
	int use_bootfile = 1;

	lease = ni_addrconf_lease_new(NI_ADDRCONF_DHCP, AF_INET);

	lease->state = NI_ADDRCONF_STATE_GRANTED;
	lease->type = NI_ADDRCONF_DHCP;
	lease->family = AF_INET;
	lease->time_acquired = time(NULL);

	lease->dhcp.address.s_addr = message->yiaddr;
	lease->dhcp.serveraddress.s_addr = message->siaddr;
	lease->dhcp.address.s_addr = message->yiaddr;

parse_more:
	/* Loop as long as we still have data in the buffer. */
	while (ni_buffer_count(options) && !options->underflow) {
		ni_buffer_t buf;
		int option;

		option = ni_dhcp_option_next(options, &buf);

		//ni_debug_dhcp("handle option %s (%d)", ni_dhcp_option_name(option), option);
		if (option == DHCP_PAD)
			continue;

		if (option == DHCP_END)
			break;

		if (option < 0)
			goto error;

		if (ni_buffer_count(&buf) == 0) {
			ni_error("option %d has zero length", option);
			goto error;
		}

		switch (option) {
		case DHCP_MESSAGETYPE:
			msg_type = ni_buffer_getc(&buf);
			if (msg_type < 0)
				goto error;
			continue;
		case DHCP_ADDRESS:
			ni_dhcp_option_get_ipv4(&buf, &lease->dhcp.address);
			break;
		case DHCP_NETMASK:
			ni_dhcp_option_get_ipv4(&buf, &lease->dhcp.netmask);
			break;
		case DHCP_BROADCAST:
			ni_dhcp_option_get_ipv4(&buf, &lease->dhcp.broadcast);
			break;
		case DHCP_SERVERIDENTIFIER:
			ni_dhcp_option_get_ipv4(&buf, &lease->dhcp.serveraddress);
			break;
		case DHCP_LEASETIME:
			ni_dhcp_option_get32(&buf, &lease->dhcp.lease_time);
			break;
		case DHCP_RENEWALTIME:
			ni_dhcp_option_get32(&buf, &lease->dhcp.renewal_time);
			break;
		case DHCP_REBINDTIME:
			ni_dhcp_option_get32(&buf, &lease->dhcp.rebind_time);
			break;
		case DHCP_MTU:
			ni_dhcp_option_get16(&buf, &lease->dhcp.mtu);
			/* Minimum legal mtu is 68 accoridng to
			 * RFC 2132. In practise it's 576 which is the
			 * minimum maximum message size. */
			if (lease->dhcp.mtu < MTU_MIN) {
				ni_debug_dhcp("MTU %u is too low, minimum is %d; ignoring",
						lease->dhcp.mtu, MTU_MIN);
				lease->dhcp.mtu = 0;
			}
			break;
		case DHCP_HOSTNAME:
			ni_dhcp_option_get_domain(&buf, &lease->hostname,
							"hostname");
			break;
		case DHCP_DNSDOMAIN:
			ni_dhcp_option_get_domain(&buf, &dnsdomain,
							"dns-domain");
			break;
		case DHCP_MESSAGE:
			ni_dhcp_option_get_printable(&buf, &lease->dhcp.message,
							"dhcp-message");
			break;
		case DHCP_ROOTPATH:
			ni_dhcp_option_get_pathname(&buf, &lease->dhcp.rootpath,
							"root-path");
			break;
		case DHCP_NISDOMAIN:
			ni_dhcp_option_get_domain(&buf, &nisdomain,
							"nis-domain");
			break;
		case DHCP_NETBIOSNODETYPE:
			ni_dhcp_option_get_netbios_type(&buf, &lease->netbios_type);
			break;
		case DHCP_NETBIOSSCOPE:
			ni_dhcp_option_get_domain(&buf, &lease->netbios_scope,
							"netbios-scope");
			break;
		case DHCP_DNSSERVER:
			ni_dhcp_decode_address_list(&buf, &dns_servers);
			break;
		case DHCP_NTPSERVER:
			ni_dhcp_decode_address_list(&buf, &lease->ntp_servers);
			break;
		case DHCP_NISSERVER:
			ni_dhcp_decode_address_list(&buf, &nis_servers);
			break;
		case DHCP_LPRSERVER:
			ni_dhcp_decode_address_list(&buf, &lease->lpr_servers);
			break;
		case DHCP_LOGSERVER:
			ni_dhcp_decode_address_list(&buf, &lease->log_servers);
			break;
		case DHCP_NETBIOSNAMESERVER:
			ni_dhcp_decode_address_list(&buf, &lease->netbios_name_servers);
			break;
		case DHCP_NETBIOSDDSERVER:
			ni_dhcp_decode_address_list(&buf, &lease->netbios_dd_servers);
			break;
		case DHCP_DNSSEARCH:
			ni_dhcp_decode_dnssearch(&buf, &dns_search, "dns-search domain");
			break;

		case DHCP_CSR:
		case DHCP_MSCSR:
			ni_route_array_destroy(&classless_routes);
			if (ni_dhcp_decode_csr(&buf, &classless_routes) < 0)
				goto error;
			break;

		case DHCP_SIPSERVER:
			ni_dhcp_decode_sipservers(&buf, &lease->sip_servers);
			break;

		case DHCP_STATICROUTE:
			ni_route_array_destroy(&static_routes);
			if (ni_dhcp_decode_static_routes(&buf, &static_routes) < 0)
				goto error;
			break;

		case DHCP_ROUTERS:
			ni_route_array_destroy(&default_routes);
			if (ni_dhcp_decode_routers(&buf, &default_routes) < 0)
				goto error;
			break;

		case DHCP_OPTIONSOVERLOADED:
			if (options != &overload_buf) {
				opt_overload = ni_buffer_getc(&buf);
			} else {
				ni_debug_dhcp("DHCP: ignoring OVERLOAD option in overloaded data");
				(void) ni_buffer_getc(&buf);
			}
			break;

		case DHCP_FQDN:
			/* We ignore replies about FQDN */
			break;

		default:
			ni_debug_dhcp("ignoring unsupported DHCP code %u", option);
			break;
		}

		if (buf.underflow) {
			ni_debug_dhcp("unable to parse DHCP option %s: too short",
					ni_dhcp_option_name(option));
			goto error;
		} else if (ni_buffer_count(&buf)) {
			ni_debug_dhcp("excess data in DHCP option %s - %u bytes left",
					ni_dhcp_option_name(option),
					ni_buffer_count(&buf));
		}

	}

	if (options->underflow) {
		ni_debug_dhcp("unable to parse DHCP response: truncated packet");
		goto error;
	}

	if (opt_overload) {
		const void *more_data = NULL;
		size_t size = 0;

		if (opt_overload & DHCP_OVERLOAD_BOOTFILE) {
			use_bootfile = 0;
			more_data = message->bootfile;
			size = sizeof(message->bootfile);
			opt_overload &= ~DHCP_OVERLOAD_BOOTFILE;
		} else
		if (opt_overload & DHCP_OVERLOAD_SERVERNAME) {
			use_bootserver = 0;
			more_data = message->servername;
			size = sizeof(message->servername);
			opt_overload &= ~DHCP_OVERLOAD_SERVERNAME;
		} else {
			opt_overload = 0;
		}
		if (more_data) {
			ni_buffer_init_reader(&overload_buf, (void *) more_data, size);
			options = &overload_buf;
			goto parse_more;
		}
	}

	if (use_bootserver && message->servername[0]) {
		char tmp[sizeof(message->servername)];
		size_t len;

		assert(sizeof(lease->dhcp.servername) == sizeof(message->servername));
		memcpy(tmp, message->servername, sizeof(tmp));
		tmp[sizeof(tmp)-1] = '\0';

		len = ni_string_len(tmp);
		if (ni_check_domain_name(tmp, len, 0)) {
			memcpy(lease->dhcp.servername, tmp, sizeof(lease->dhcp.servername));
		} else {
			ni_debug_dhcp("Discarded suspect boot-server name: %s",
					ni_print_suspect(tmp, len));
		}
	}
	if (use_bootfile && message->bootfile[0]) {
		char tmp[sizeof(message->bootfile)];
		size_t len;

		memcpy(tmp, message->bootfile, sizeof(tmp));
		tmp[sizeof(tmp)-1] = '\0';
		len = ni_string_len(tmp);
		if (ni_check_pathname(tmp, len)) {
			ni_string_dup(&lease->dhcp.bootfile, tmp);
		} else {
			ni_debug_dhcp("Discarded suspect boot-file name: %s",
					ni_print_suspect(tmp, len));
		}
	}

	/* Fill in any missing fields */
	if (!lease->dhcp.netmask.s_addr) {
		unsigned int pfxlen = guess_prefix_len(lease->dhcp.address);

		lease->dhcp.netmask.s_addr = htonl(~(0xFFFFFFFF >> pfxlen));
	}
Пример #8
0
/*
 * Decode an RFC3397 DNS search order option.
 */
static int
ni_dhcp_decode_dnssearch(ni_buffer_t *optbuf, ni_string_array_t *list, const char *what)
{
	ni_stringbuf_t namebuf = NI_STRINGBUF_INIT_DYNAMIC;
	unsigned char *base = ni_buffer_head(optbuf);
	unsigned int base_offset = optbuf->head;
	size_t len;

	ni_string_array_destroy(list);

	while (ni_buffer_count(optbuf) && !optbuf->underflow) {
		ni_buffer_t *bp = optbuf;
		ni_buffer_t jumpbuf;

		while (1) {
			unsigned int pos = bp->head - base_offset;
			unsigned int pointer;
			char label[64];
			int length;

			if ((length = ni_buffer_getc(bp)) < 0)
				goto failure; /* unexpected EOF */

			if (length == 0)
				break;	/* end of this name */

			switch (length & 0xC0) {
			case 0:
				/* Plain name component */
				if (ni_buffer_get(bp, label, length) < 0)
					goto failure;

				label[length] = '\0';
				if (!ni_stringbuf_empty(&namebuf))
					ni_stringbuf_putc(&namebuf, '.');
				ni_stringbuf_puts(&namebuf, label);
				break;

			case 0xC0:
				/* Pointer */
				pointer = (length & 0x3F) << 8;
				if ((length = ni_buffer_getc(bp)) < 0)
					goto failure;

				pointer |= length;
				if (pointer >= pos)
					goto failure;

				ni_buffer_init_reader(&jumpbuf, base, pos);
				jumpbuf.head = pointer;
				bp = &jumpbuf;
				break;

			default:
				goto failure;
			}

		}

		if (!ni_stringbuf_empty(&namebuf)) {

			len = ni_string_len(namebuf.string);
			if (ni_check_domain_name(namebuf.string, len, 0)) {
				ni_string_array_append(list, namebuf.string);
			} else {
				ni_debug_dhcp("Discarded suspect %s: %s", what,
					ni_print_suspect(namebuf.string, len));
			}
		}
		ni_stringbuf_destroy(&namebuf);
	}

	return 0;

failure:
	ni_stringbuf_destroy(&namebuf);
	ni_string_array_destroy(list);
	return -1;
}
Пример #9
0
static int
__ni_dhcp4_build_msg_put_our_hostname(const ni_dhcp4_device_t *dev,
					ni_buffer_t *msgbuf)
{
	const ni_dhcp4_config_t *options = dev->config;
	size_t len = ni_string_len(options->hostname);

	if (!len)
		return 1; /* skipped hint */

	if (options->fqdn == FQDN_DISABLE) {
		char hname[64] = {'\0'}, *end;

		/*
		 * Truncate the domain part if fqdn to avoid attempts
		 * to update DNS with foo.bar + update-domain.
		 */
		strncat(hname, options->hostname, sizeof(hname)-1);
		if ((end = strchr(hname, '.')))
			*end = '\0';

		len = ni_string_len(hname);
		if (ni_check_domain_name(hname, len, 0)) {
			ni_dhcp4_option_puts(msgbuf, DHCP4_HOSTNAME, hname);
			ni_debug_verbose(NI_LOG_DEBUG1, NI_TRACE_DHCP,
				"%s: using hostname: %s", dev->ifname, hname);
		} else {
			ni_info("%s: not sending suspect hostname: '%s'",
				dev->ifname, ni_print_suspect(hname, len));
			return 1;
		}
	} else
	if (ni_check_domain_name(options->hostname, len, 0)) {
		/* IETF DHC-FQDN option(81)
		 * http://tools.ietf.org/html/rfc4702#section-2.1
		 *
		 * Flags: 0000NEOS
		 * S: 1 => Client requests Server to update
		 *         a RR in DNS as well as PTR
		 * O: 1 => Server indicates to client that
		 *         DNS has been updated
		 * E: 1 => Name data is DNS format
		 * N: 1 => Client requests Server to not
		 *         update DNS
		 */
		ni_buffer_putc(msgbuf, DHCP4_FQDN);
		ni_buffer_putc(msgbuf, len + 3);
		ni_buffer_putc(msgbuf, options->fqdn & 0x9);
		ni_buffer_putc(msgbuf, 0);	/* from server for PTR RR */
		ni_buffer_putc(msgbuf, 0);	/* from server for A RR if S=1 */
		ni_buffer_put(msgbuf, options->hostname, len);
		ni_debug_verbose(NI_LOG_DEBUG1, NI_TRACE_DHCP,
				"%s: using fqdn: %s", dev->ifname,
				options->hostname);
	} else {
		ni_info("%s: not sending suspect fqdn: '%s'",
			dev->ifname, ni_print_suspect(options->hostname, len));
		return 1;
	}

	return 0;
}