Exemple #1
0
/**
 * @brief Print data as integer, not as VALUE.
 */
static size_t xlat_integer(UNUSED void *instance, REQUEST *request,
			   const char *fmt, char *out, size_t outlen)
{
	VALUE_PAIR *vp;

	while (isspace((int) *fmt)) fmt++;

	if (!radius_get_vp(request, fmt, &vp) || !vp) {
		*out = '\0';
		return 0;
	}

	if ((vp->type != PW_TYPE_IPADDR) &&
	    (vp->type != PW_TYPE_INTEGER) &&
	    (vp->type != PW_TYPE_INTEGER64) &&
	    (vp->type != PW_TYPE_SHORT) &&
	    (vp->type != PW_TYPE_BYTE) &&
	    (vp->type != PW_TYPE_DATE)) {
		*out = '\0';
		return 0;
	}

	if (vp->type == PW_TYPE_INTEGER64) {
		return snprintf(out, outlen, "%llu", vp->vp_integer64);
	}

	return snprintf(out, outlen, "%u", vp->vp_integer);
}
Exemple #2
0
/**
 * @brief Print data as hex, not as VALUE.
 */
static size_t xlat_hex(UNUSED void *instance, REQUEST *request,
		       const char *fmt, char *out, size_t outlen)
{
	size_t i;
	VALUE_PAIR *vp;
	uint8_t	buffer[MAX_STRING_LEN];
	ssize_t	ret;
	size_t	len;

	while (isspace((int) *fmt)) fmt++;

	if (!radius_get_vp(request, fmt, &vp) || !vp) {
		*out = '\0';
		return 0;
	}
	
	ret = rad_vp2data(vp, buffer, sizeof(buffer));
	len = (size_t) ret;
	
	/*
	 *	Don't truncate the data.
	 */
	if ((ret < 0 ) || (outlen < (len * 2))) {
		*out = 0;
		return 0;
	}

	for (i = 0; i < len; i++) {
		snprintf(out + 2*i, 3, "%02x", buffer[i]);
	}

	return len * 2;
}
Exemple #3
0
/**
 * @brief Print data as string, if possible.
 *
 * If attribute "Foo" is defined as "octets" it will normally
 * be printed as 0x0a0a0a. The xlat "%{string:Foo}" will instead
 * expand to "\n\n\n"
 */
static size_t xlat_string(UNUSED void *instance, REQUEST *request,
			  const char *fmt, char *out, size_t outlen)
{
	int len;
	VALUE_PAIR *vp;

	while (isspace((int) *fmt)) fmt++;

	if (outlen < 3) {
	nothing:
		*out = '\0';
		return 0;
	}

	if (!radius_get_vp(request, fmt, &vp)) goto nothing;

	if (!vp) goto nothing;

	if (vp->type != PW_TYPE_OCTETS) goto nothing;

	len = fr_print_string(vp->vp_strvalue, vp->length, out, outlen);
	out[len] = '\0';

	return len;
}
Exemple #4
0
/**
 * @brief Print data as hex, not as VALUE.
 */
static size_t xlat_hex(UNUSED void *instance, REQUEST *request,
		       const char *fmt, char *out, size_t outlen)
{
	size_t i;
	uint8_t *p;
	VALUE_PAIR *vp;

	while (isspace((int) *fmt)) fmt++;

	if (!radius_get_vp(request, fmt, &vp) || !vp) {
		*out = '\0';
		return 0;
	}

	/*
	 *	Don't truncate the data.
	 */
	if (outlen < (vp->length * 2)) {
		*out = 0;
		return 0;
	}

	p = &vp->vp_octets[0];
	for (i = 0; i < vp->length; i++) {
		snprintf(out + 2*i, 3, "%02x", p[i]);
	}

	return vp->length * 2;
}
Exemple #5
0
/** Print the size of the attribute in bytes.
 *
 */
static ssize_t xlat_length(UNUSED void *instance, UNUSED REQUEST *request,
                           char const *fmt, char *out, size_t outlen)
{
	VALUE_PAIR *vp;
	while (isspace((int) *fmt)) fmt++;

	if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
		*out = '\0';
		return 0;
	}

	snprintf(out, outlen, "%zu", vp->length);
	return strlen(out);
}
Exemple #6
0
/** xlat expand string attribute value
 *
 */
static size_t xlat_xlat(UNUSED void *instance, REQUEST *request,
			const char *fmt, char *out, size_t outlen)
{
	VALUE_PAIR *vp;

	while (isspace((int) *fmt)) fmt++;

	if (outlen < 3) {
	nothing:
		*out = '\0';
		return 0;
	}

	if ((radius_get_vp(request, fmt, &vp) < 0) || !vp) goto nothing;

	return radius_xlat(out, outlen, vp->vp_strvalue, request, NULL, NULL);
}
Exemple #7
0
/** Crappy temporary function to add attribute ref support to xlats
 *
 * This needs to die, and hopefully will die, when xlat functions accept
 * xlat node structures.
 *
 * Provides either a pointer to a buffer which contains the value of the reference VALUE_PAIR
 * in an architecture independent format. Or a pointer to the start of the fmt string.
 *
 * The pointer is only guaranteed to be valid between calls to xlat_fmt_to_ref,
 * and so long as the source VALUE_PAIR is not freed.
 *
 * @param out where to write a pointer to the buffer to the data the xlat function needs to work on.
 * @param request current request.
 * @param fmt string.
 * @returns the length of the data or -1 on error.
 */
ssize_t xlat_fmt_to_ref(uint8_t const **out, REQUEST *request, char const *fmt)
{
	VALUE_PAIR *vp;

	while (isspace((int) *fmt)) fmt++;

	if (fmt[0] == '&') {
		if ((radius_get_vp(&vp, request, fmt + 1) < 0) || !vp) {
			*out = NULL;
			return -1;
		}

		return rad_vp2data(out, vp);
	}

	*out = (uint8_t const *)fmt;
	return strlen(fmt);
}
Exemple #8
0
/** Print data as integer, not as VALUE.
 *
 */
static ssize_t xlat_integer(UNUSED void *instance, REQUEST *request,
			    char const *fmt, char *out, size_t outlen)
{
	VALUE_PAIR 	*vp;

	uint64_t 	integer;

	while (isspace((int) *fmt)) fmt++;

	if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
		*out = '\0';
		return 0;
	}

	switch (vp->da->type) {
	case PW_TYPE_OCTETS:
	case PW_TYPE_STRING:
		if (vp->length > 8) {
			break;
		}

		memcpy(&integer, &(vp->vp_octets), vp->length);

		return snprintf(out, outlen, "%" PRIu64, ntohll(integer));

	case PW_TYPE_INTEGER64:
		return snprintf(out, outlen, "%" PRIu64, vp->vp_integer64);

	case PW_TYPE_IPADDR:
	case PW_TYPE_INTEGER:
	case PW_TYPE_SHORT:
	case PW_TYPE_BYTE:
	case PW_TYPE_DATE:
		return snprintf(out, outlen, "%u", vp->vp_integer);
	default:
		break;
	}

	REDEBUG("Type \"%s\" cannot be converted to integer",
		fr_int2str(dict_attr_types, vp->da->type, PW_TYPE_INVALID));
	*out = '\0';

	return -1;
}
Exemple #9
0
/** Print data as integer, not as VALUE.
 *
 */
static size_t xlat_integer(UNUSED void *instance, REQUEST *request,
			   const char *fmt, char *out, size_t outlen)
{
	VALUE_PAIR 	*vp;

	uint64_t 	integer;
	
	while (isspace((int) *fmt)) fmt++;

	if ((radius_get_vp(request, fmt, &vp) < 0) || !vp) {
		*out = '\0';
		return 0;
	}

	switch (vp->da->type)
	{		
		case PW_TYPE_OCTETS:
		case PW_TYPE_STRING:
			if (vp->length > 8) {
				break;
			} 

			memcpy(&integer, &(vp->vp_octets), vp->length);
			
			return snprintf(out, outlen, "%llu", ntohll(integer));	
			
		case PW_TYPE_INTEGER64:
			return snprintf(out, outlen, "%llu", vp->vp_integer64);
			
		case PW_TYPE_IPADDR:
		case PW_TYPE_INTEGER:
		case PW_TYPE_SHORT:
		case PW_TYPE_BYTE:
		case PW_TYPE_DATE:
			return snprintf(out, outlen, "%u", vp->vp_integer);
		default:
			break;
	}
	
	*out = '\0';
	return 0;
}
Exemple #10
0
/*
 *	Allow single attribute values to be retrieved from the dhcp.
 */
static size_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request,
                                char const *fmt, char *out, size_t freespace)
{
    vp_cursor_t cursor;
    VALUE_PAIR *vp, *head = NULL;
    int decoded = 0;

    while (isspace((int) *fmt)) fmt++;


    if ((radius_get_vp(request, fmt, &vp) < 0) || !vp) {
        *out = '\0';

        return 0;
    }

    if ((fr_dhcp_decode_options(request->packet,
                                vp->vp_octets, vp->length, &head) < 0) ||
            (!head)) {
        RWDEBUG("DHCP option decoding failed");
        goto fail;
    }


    for (vp = paircursor(&cursor, &head);
            vp;
            vp = pairnext(&cursor)) {
        decoded++;
    }

    pairmove(request->packet, &(request->packet->vps), &head);

    /* Free any unmoved pairs */
    pairfree(&head);

fail:

    snprintf(out, freespace, "%i", decoded);

    return strlen(out);
}
Exemple #11
0
/** Print data as string, if possible.
 *
 * If attribute "Foo" is defined as "octets" it will normally
 * be printed as 0x0a0a0a. The xlat "%{string:Foo}" will instead
 * expand to "\n\n\n"
 */
static ssize_t xlat_string(UNUSED void *instance, REQUEST *request,
			   char const *fmt, char *out, size_t outlen)
{
	size_t len;
	ssize_t ret;
	VALUE_PAIR *vp;
	uint8_t const *p;

	while (isspace((int) *fmt)) fmt++;
	if (*fmt == '&') fmt++;

	if (outlen < 3) {
	nothing:
		*out = '\0';
		return 0;
	}

	if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing;

	ret = rad_vp2data(&p, vp);
	if (ret < 0) {
		return ret;
	}

	switch (vp->da->type) {
		case PW_TYPE_OCTETS:
			len = fr_print_string((char const *) p, vp->length, out, outlen);
			break;

		case PW_TYPE_STRING:
			len = strlcpy(out, vp->vp_strvalue, outlen);
			break;

		default:
			len = fr_print_string((char const *) p, ret, out, outlen);
			break;
	}

	return len;
}
/*
 *	Allow single attribute values to be retrieved from the dhcp.
 */
static size_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request,
			 	const char *fmt, char *out, size_t freespace)
{
	VALUE_PAIR *vp, *head = NULL, *next;
	int decoded = 0;
	
	while (isspace((int) *fmt)) fmt++;
	
	
	if ((radius_get_vp(request, fmt, &vp) < 0) || !vp) {
		 *out = '\0';
		 
		 return 0;
	}
	
	if ((fr_dhcp_decode_options(vp->vp_octets, vp->length, &head) < 0) ||
	    (head == NULL)) {
		RDEBUG("WARNING: DHCP option decoding failed");
		goto fail;
	}
	
	next = head;
	
	do {
		 next = next->next;
		 decoded++;
	} while (next);
	
	pairmove(&(request->packet->vps), &head);
	
	/* Free any unmoved pairs */
	pairfree(&head);
	
	fail:
	
	snprintf(out, freespace, "%i", decoded);
			 
	return strlen(out);
}
Exemple #13
0
/** Print data as base64, not as VALUE
 *
 */
static size_t xlat_base64(UNUSED void *instance, REQUEST *request,
			  const char *fmt, char *out, size_t outlen)
{
	VALUE_PAIR *vp;
	uint8_t buffer[MAX_STRING_LEN];
	ssize_t	ret;
	
	while (isspace((int) *fmt)) fmt++;

	if ((radius_get_vp(request, fmt, &vp) < 0) || !vp) {
		*out = '\0';
		return 0;
	}
	
	ret = rad_vp2data(vp, buffer, sizeof(buffer));
	if (ret < 0) {
		*out = 0;
		return 0;
	}

	return fr_base64_encode(buffer, (size_t) ret, out, outlen);
}
/**
 * @brief Print data as base64, not as VALUE
 */
static size_t xlat_base64(UNUSED void *instance, REQUEST *request,
			  char *fmt, char *out, size_t outlen,
			  UNUSED RADIUS_ESCAPE_STRING func)
{
	VALUE_PAIR *vp;
	uint8_t buffer[MAX_STRING_LEN];
	ssize_t	ret;
	size_t	len;
	size_t	enc;

	while (isspace((int) *fmt)) fmt++;

	if (!radius_get_vp(request, fmt, &vp) || !vp) {
		*out = '\0';
		return 0;
	}

	ret = rad_vp2data(vp, buffer, sizeof(buffer));
	if (ret < 0) {
		*out = 0;
		return 0;
	}

	len = (size_t) ret;

	enc = FR_BASE64_ENC_LENGTH(len);

	/*
	 *	Don't truncate the data.
	 */
	if (outlen < (enc + 1)) {
		*out = 0;
		return 0;
	}

	fr_base64_encode(buffer, len, out, outlen);

	return enc;
}
Exemple #15
0
/** Print data as hex, not as VALUE.
 *
 */
static ssize_t xlat_hex(UNUSED void *instance, REQUEST *request,
		        char const *fmt, char *out, size_t outlen)
{
	size_t i;
	VALUE_PAIR *vp;
	uint8_t const *p;
	ssize_t	ret;
	size_t	len;

	while (isspace((int) *fmt)) fmt++;

	if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
		*out = '\0';
		return -1;
	}

	ret = rad_vp2data(&p, vp);
	if (ret < 0) {
		return ret;
	}
	len = (size_t) ret;

	/*
	 *	Don't truncate the data.
	 */
	if ((ret < 0 ) || (outlen < (len * 2))) {
		*out = 0;
		return 0;
	}

	for (i = 0; i < len; i++) {
		snprintf(out + 2*i, 3, "%02x", p[i]);
	}

	return len * 2;
}
Exemple #16
0
/*
 *	*presult is "did comparison match or not"
 */
static int radius_do_cmp(REQUEST *request, int *presult,
			 FR_TOKEN lt, const char *pleft, FR_TOKEN token,
			 FR_TOKEN rt, const char *pright,
			 int cflags, int modreturn)
{
	int result;
	uint32_t lint, rint;
	VALUE_PAIR *vp = NULL;
#ifdef HAVE_REGEX_H
	char buffer[8192];
#else
	cflags = cflags;	/* -Wunused */
#endif

	rt = rt;		/* -Wunused */

	if (lt == T_BARE_WORD) {
		/*
		 *	Maybe check the last return code.
		 */
		if (token == T_OP_CMP_TRUE) {
			int isreturn;

			/*
			 *	Looks like a return code, treat is as such.
			 */
			isreturn = fr_str2int(modreturn_table, pleft, -1);
			if (isreturn != -1) {
				*presult = (modreturn == isreturn);
				return TRUE;
			}
		}

		/*
		 *	Bare words on the left can be attribute names.
		 */
		if (radius_get_vp(request, pleft, &vp)) {
			VALUE_PAIR myvp;

			/*
			 *	VP exists, and that's all we're looking for.
			 */
			if (token == T_OP_CMP_TRUE) {
				*presult = (vp != NULL);
				return TRUE;
			}

			if (!vp) {
				DICT_ATTR *da;
				
				/*
				 *	The attribute on the LHS may
				 *	have been a dynamically
				 *	registered callback.  i.e. it
				 *	doesn't exist as a VALUE_PAIR.
				 *	If so, try looking for it.
				 */
				da = dict_attrbyname(pleft);
				if (da && (da->vendor == 0) && radius_find_compare(da->attr)) {
					VALUE_PAIR *check = pairmake(pleft, pright, token);
					*presult = (radius_callback_compare(request, NULL, check, NULL, NULL) == 0);
					RDEBUG3("  Callback returns %d",
						*presult);
					pairfree(&check);
					return TRUE;
				}
				
				RDEBUG2("    (Attribute %s was not found)",
				       pleft);
				*presult = 0;
				return TRUE;
			}

#ifdef HAVE_REGEX_H
			/*
			 * 	Regex comparisons treat everything as
			 *	strings.
			 */
			if ((token == T_OP_REG_EQ) ||
			    (token == T_OP_REG_NE)) {
				vp_prints_value(buffer, sizeof(buffer), vp, 0);
				pleft = buffer;
				goto do_checks;
			}
#endif

			memcpy(&myvp, vp, sizeof(myvp));
			if (!pairparsevalue(&myvp, pright)) {
				RDEBUG2("Failed parsing \"%s\": %s",
				       pright, fr_strerror());
				return FALSE;
			}

			myvp.operator = token;
			*presult = paircmp(&myvp, vp);
			RDEBUG3("  paircmp -> %d", *presult);
			return TRUE;
		} /* else it's not a VP in a list */
	}

#ifdef HAVE_REGEX_H
	do_checks:
#endif
	switch (token) {
	case T_OP_GE:
	case T_OP_GT:
	case T_OP_LE:
	case T_OP_LT:
		if (!all_digits(pright)) {
			RDEBUG2("    (Right field is not a number at: %s)", pright);
			return FALSE;
		}
		rint = strtoul(pright, NULL, 0);
		if (!all_digits(pleft)) {
			RDEBUG2("    (Left field is not a number at: %s)", pleft);
			return FALSE;
		}
		lint = strtoul(pleft, NULL, 0);
		break;
		
	default:
		lint = rint = 0;  /* quiet the compiler */
		break;
	}
	
	switch (token) {
	case T_OP_CMP_TRUE:
		/*
		 *	Check for truth or falsehood.
		 */
		if (all_digits(pleft)) {
			lint = strtoul(pleft, NULL, 0);
			result = (lint != 0);
			
		} else {
			result = (*pleft != '\0');
		}
		break;
		

	case T_OP_CMP_EQ:
		result = (strcmp(pleft, pright) == 0);
		break;
		
	case T_OP_NE:
		result = (strcmp(pleft, pright) != 0);
		break;
		
	case T_OP_GE:
		result = (lint >= rint);
		break;
		
	case T_OP_GT:
		result = (lint > rint);
		break;
		
	case T_OP_LE:
		result = (lint <= rint);
		break;
		
	case T_OP_LT:
		result = (lint < rint);
		break;

#ifdef HAVE_REGEX_H
	case T_OP_REG_EQ: {
		int i, compare;
		regex_t reg;
		regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
		
		/*
		 *	Include substring matches.
		 */
		compare = regcomp(&reg, pright, cflags);
		if (compare != 0) {
			if (debug_flag) {
				char errbuf[128];

				regerror(compare, &reg, errbuf, sizeof(errbuf));
				DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
			}
			return FALSE;
		}

		compare = regexec(&reg, pleft,
				  REQUEST_MAX_REGEX + 1,
				  rxmatch, 0);
		regfree(&reg);
		
		/*
		 *	Add new %{0}, %{1}, etc.
		 */
		if (compare == 0) for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
			char *r;

			free(request_data_get(request, request,
					      REQUEST_DATA_REGEX | i));

			/*
			 *	No %{i}, skip it.
			 *	We MAY have %{2} without %{1}.
			 */
			if (rxmatch[i].rm_so == -1) continue;
			
			/*
			 *	Copy substring into allocated buffer
			 */
			r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1);
			memcpy(r, pleft + rxmatch[i].rm_so,
			       rxmatch[i].rm_eo - rxmatch[i].rm_so);
			r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';

			request_data_add(request, request,
					 REQUEST_DATA_REGEX | i,
					 r, free);
		}
		result = (compare == 0);
	}
		break;
		
	case T_OP_REG_NE: {
		int compare;
		regex_t reg;
		regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
		
		/*
		 *	Include substring matches.
		 */
		compare = regcomp(&reg, pright, cflags);
		if (compare != 0) {
			if (debug_flag) {
				char errbuf[128];

				regerror(compare, &reg, errbuf, sizeof(errbuf));
				DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
			}
			return FALSE;
		}

		compare = regexec(&reg, pleft,
				  REQUEST_MAX_REGEX + 1,
				  rxmatch, 0);
		regfree(&reg);
		
		result = (compare != 0);
	}
		break;
#endif
		
	default:
		DEBUG("ERROR: Comparison operator %s is not supported",
		      fr_token_name(token));
		result = FALSE;
		break;
	}
	
	*presult = result;
	return TRUE;
}
Exemple #17
0
/** Print data as integer, not as VALUE.
 *
 */
static ssize_t xlat_integer(UNUSED void *instance, REQUEST *request,
			    char const *fmt, char *out, size_t outlen)
{
	VALUE_PAIR 	*vp;

	uint64_t 	int64 = 0;	/* Needs to be initialised to zero */
	uint32_t	int32 = 0;	/* Needs to be initialised to zero */

	while (isspace((int) *fmt)) fmt++;

	if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
		*out = '\0';
		return 0;
	}

	switch (vp->da->type) {
	case PW_TYPE_OCTETS:
	case PW_TYPE_STRING:
		if (vp->length > 8) {
			break;
		}

		if (vp->length > 4) {
			memcpy(&int64, vp->vp_octets, vp->length);
			return snprintf(out, outlen, "%" PRIu64, htonll(int64));
		}

		memcpy(&int32, vp->vp_octets, vp->length);
		return snprintf(out, outlen, "%i", htonl(int32));

	case PW_TYPE_INTEGER64:
		return snprintf(out, outlen, "%" PRIu64, vp->vp_integer64);

	/*
	 *	IP addresses are treated specially, as parsing functions assume the value
	 *	is bigendian and will convert it for us.
	 */
	case PW_TYPE_IPADDR:
		return snprintf(out, outlen, "%u", htonl(vp->vp_ipaddr));

	case PW_TYPE_INTEGER:
	case PW_TYPE_DATE:
	case PW_TYPE_BYTE:
	case PW_TYPE_SHORT:
		return snprintf(out, outlen, "%u", vp->vp_integer);

	/*
	 *	Ethernet is weird... It's network related, so we assume to it should be
	 *	bigendian.
	 */
	case PW_TYPE_ETHERNET:
		memcpy(&int64, &vp->vp_ether, vp->length);
		return snprintf(out, outlen, "%" PRIu64, htonll(int64));

	case PW_TYPE_SIGNED:
		return snprintf(out, outlen, "%i", vp->vp_signed);

	default:
		break;
	}

	REDEBUG("Type '%s' of length %zu cannot be converted to integer",
		fr_int2str(dict_attr_types, vp->da->type, PW_TYPE_INVALID), vp->length);
	*out = '\0';

	return -1;
}
/** Unpack data
 *
 *  Example: %{unpack:&Class 0 integer}
 *
 *  Expands Class, treating octet at offset 0 (bytes 0-3) as an "integer".
 */
static ssize_t unpack_xlat(UNUSED void *instance, REQUEST *request, char const *fmt,
			   char *out, size_t outlen)
{
	char *data_name, *data_size, *data_type;
	char *p;
	size_t len, input_len;
	int offset;
	PW_TYPE type;
	DICT_ATTR const *da;
	VALUE_PAIR *vp, *cast;
	uint8_t const *input;
	char buffer[256];
	uint8_t blob[256];

	/*
	 *	FIXME: copy only the fields here, as we parse them.
	 */
	strlcpy(buffer, fmt, sizeof(buffer));

	p = buffer;
	while (isspace((int) *p)) p++; /* skip leading spaces */

	data_name = p;

	while (*p && !isspace((int) *p)) p++;

	if (!*p) {
	error:
		REDEBUG("Format string should be '<data> <offset> <type>' e.g. '&Class 1 integer'");
	nothing:
		*out = '\0';
		return -1;
	}

	while (isspace((int) *p)) *(p++) = '\0';
	if (!*p) GOTO_ERROR;

	data_size = p;

	while (*p && !isspace((int) *p)) p++;
	if (!*p) GOTO_ERROR;

	while (isspace((int) *p)) *(p++) = '\0';
	if (!*p) GOTO_ERROR;

	data_type = p;

	while (*p && !isspace((int) *p)) p++;
	if (*p) GOTO_ERROR;	/* anything after the type is an error */

	/*
	 *	Attribute reference
	 */
	if (*data_name == '&') {
		if (radius_get_vp(&vp, request, data_name) < 0) goto nothing;

		if ((vp->da->type != PW_TYPE_OCTETS) &&
		    (vp->da->type != PW_TYPE_STRING)) {
			REDEBUG("unpack requires the input attribute to be 'string' or 'octets'");
			goto nothing;
		}
		input = vp->vp_octets;
		input_len = vp->vp_length;

	} else if ((data_name[0] == '0') && (data_name[1] == 'x')) {
		/*
		 *	Hex data.
		 */
		len = strlen(data_name + 2);
		if ((len & 0x01) != 0) {
			RDEBUG("Invalid hex string in '%s'", data_name);
			goto nothing;
		}
		input = blob;
		input_len = fr_hex2bin(blob, sizeof(blob), data_name + 2, len);

	} else {
		GOTO_ERROR;
	}

	offset = (int) strtoul(data_size, &p, 10);
	if (*p) {
		REDEBUG("unpack requires a decimal number, not '%s'", data_size);
		goto nothing;
	}

	type = fr_str2int(dict_attr_types, data_type, PW_TYPE_INVALID);
	if (type == PW_TYPE_INVALID) {
		REDEBUG("Invalid data type '%s'", data_type);
		goto nothing;
	}

	/*
	 *	Output must be a non-zero limited size.
	 */
	if ((dict_attr_sizes[type][0] ==  0) ||
	    (dict_attr_sizes[type][0] != dict_attr_sizes[type][1])) {
		REDEBUG("unpack requires fixed-size output type, not '%s'", data_type);
		goto nothing;
	}

	if (input_len < (offset + dict_attr_sizes[type][0])) {
		REDEBUG("Insufficient data to unpack '%s' from '%s'", data_type, data_name);
		goto nothing;
	}

	da = dict_attrbyvalue(PW_CAST_BASE + type, 0);
	if (!da) {
		REDEBUG("Cannot decode type '%s'", data_type);
		goto nothing;
	}

	cast = pairalloc(request, da);
	if (!cast) goto nothing;

	memcpy(&(cast->data), input + offset, dict_attr_sizes[type][0]);
	cast->vp_length = dict_attr_sizes[type][0];

	/*
	 *	Hacks
	 */
	switch (type) {
	case PW_TYPE_SIGNED:
	case PW_TYPE_INTEGER:
	case PW_TYPE_DATE:
		cast->vp_integer = ntohl(cast->vp_integer);
		break;

	case PW_TYPE_SHORT:
		cast->vp_short = ((input[offset] << 8) | input[offset + 1]);
		break;

	case PW_TYPE_INTEGER64:
		cast->vp_integer64 = ntohll(cast->vp_integer64);
		break;

	default:
		break;
	}

	len = vp_prints_value(out, outlen, cast, 0);
	talloc_free(cast);
	if (is_truncated(len, outlen)) {
		REDEBUG("Insufficient buffer space to unpack data");
		goto nothing;
	}

	return len;
}