xref: /onnv-gate/usr/src/uts/common/io/idm/idm_text.c (revision 9601:e0ed15140e6d)
17978SPeter.Dunlap@Sun.COM /*
27978SPeter.Dunlap@Sun.COM  * CDDL HEADER START
37978SPeter.Dunlap@Sun.COM  *
47978SPeter.Dunlap@Sun.COM  * The contents of this file are subject to the terms of the
57978SPeter.Dunlap@Sun.COM  * Common Development and Distribution License (the "License").
67978SPeter.Dunlap@Sun.COM  * You may not use this file except in compliance with the License.
77978SPeter.Dunlap@Sun.COM  *
87978SPeter.Dunlap@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97978SPeter.Dunlap@Sun.COM  * or http://www.opensolaris.org/os/licensing.
107978SPeter.Dunlap@Sun.COM  * See the License for the specific language governing permissions
117978SPeter.Dunlap@Sun.COM  * and limitations under the License.
127978SPeter.Dunlap@Sun.COM  *
137978SPeter.Dunlap@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
147978SPeter.Dunlap@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157978SPeter.Dunlap@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
167978SPeter.Dunlap@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
177978SPeter.Dunlap@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
187978SPeter.Dunlap@Sun.COM  *
197978SPeter.Dunlap@Sun.COM  * CDDL HEADER END
207978SPeter.Dunlap@Sun.COM  */
217978SPeter.Dunlap@Sun.COM /*
228872SJordan.Vaughan@Sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237978SPeter.Dunlap@Sun.COM  * Use is subject to license terms.
247978SPeter.Dunlap@Sun.COM  */
257978SPeter.Dunlap@Sun.COM 
267978SPeter.Dunlap@Sun.COM #include <sys/types.h>
277978SPeter.Dunlap@Sun.COM #include <sys/conf.h>
287978SPeter.Dunlap@Sun.COM #include <sys/file.h>
297978SPeter.Dunlap@Sun.COM #include <sys/ddi.h>
307978SPeter.Dunlap@Sun.COM #include <sys/sunddi.h>
317978SPeter.Dunlap@Sun.COM #include <sys/sysmacros.h>
327978SPeter.Dunlap@Sun.COM #include <sys/socket.h>
337978SPeter.Dunlap@Sun.COM 
347978SPeter.Dunlap@Sun.COM #include <sys/iscsi_protocol.h>
357978SPeter.Dunlap@Sun.COM #include <sys/idm/idm.h>
367978SPeter.Dunlap@Sun.COM #include <sys/idm/idm_text.h>
377978SPeter.Dunlap@Sun.COM 
387978SPeter.Dunlap@Sun.COM 
397978SPeter.Dunlap@Sun.COM extern int
407978SPeter.Dunlap@Sun.COM iscsi_base64_str_to_binary(char *hstr, int hstr_len,
417978SPeter.Dunlap@Sun.COM     uint8_t *binary, int binary_buf_len, int *out_len);
427978SPeter.Dunlap@Sun.COM 
437978SPeter.Dunlap@Sun.COM 
447978SPeter.Dunlap@Sun.COM static const char idm_hex_to_ascii[] = "0123456789abcdefABCDEF";
457978SPeter.Dunlap@Sun.COM 
467978SPeter.Dunlap@Sun.COM static const idm_kv_xlate_t idm_kvpair_xlate[] = {
477978SPeter.Dunlap@Sun.COM 	/*
487978SPeter.Dunlap@Sun.COM 	 * iSCSI Security Text Keys and Authentication Methods
497978SPeter.Dunlap@Sun.COM 	 */
507978SPeter.Dunlap@Sun.COM 
517978SPeter.Dunlap@Sun.COM 	{ KI_AUTH_METHOD, "AuthMethod", KT_LIST_OF_VALUES, B_FALSE },
527978SPeter.Dunlap@Sun.COM 	/*
537978SPeter.Dunlap@Sun.COM 	 * For values with RFC comments we need to read the RFC to see
547978SPeter.Dunlap@Sun.COM 	 * what type is appropriate.  For now just treat the value as
557978SPeter.Dunlap@Sun.COM 	 * text.
567978SPeter.Dunlap@Sun.COM 	 */
577978SPeter.Dunlap@Sun.COM 
587978SPeter.Dunlap@Sun.COM 	/* Kerberos */
597978SPeter.Dunlap@Sun.COM 	{ KI_KRB_AP_REQ, "KRB_AP_REQ", KT_TEXT /* RFC1510 */, B_TRUE},
607978SPeter.Dunlap@Sun.COM 	{ KI_KRB_AP_REP, "KRB_AP_REP", KT_TEXT /* RFC1510 */, B_TRUE},
617978SPeter.Dunlap@Sun.COM 
627978SPeter.Dunlap@Sun.COM 	/* SPKM */
637978SPeter.Dunlap@Sun.COM 	{ KI_SPKM_REQ, "SPKM_REQ", KT_TEXT /* RFC2025 */, B_TRUE},
647978SPeter.Dunlap@Sun.COM 	{ KI_SPKM_ERROR, "SPKM_ERROR", KT_TEXT /* RFC2025 */, B_TRUE},
657978SPeter.Dunlap@Sun.COM 	{ KI_SPKM_REP_TI, "SPKM_REP_TI", KT_TEXT /* RFC2025 */, B_TRUE},
667978SPeter.Dunlap@Sun.COM 	{ KI_SPKM_REP_IT, "SPKM_REP_IT", KT_TEXT /* RFC2025 */, B_TRUE},
677978SPeter.Dunlap@Sun.COM 
687978SPeter.Dunlap@Sun.COM 	/*
697978SPeter.Dunlap@Sun.COM 	 * SRP
707978SPeter.Dunlap@Sun.COM 	 * U, s, A, B, M, and H(A | M | K) are defined in [RFC2945]
717978SPeter.Dunlap@Sun.COM 	 */
727978SPeter.Dunlap@Sun.COM 	{ KI_SRP_U, "SRP_U", KT_TEXT /* <U> */, B_TRUE},
737978SPeter.Dunlap@Sun.COM 	{ KI_TARGET_AUTH, "TargetAuth", KT_BOOLEAN, B_TRUE},
747978SPeter.Dunlap@Sun.COM 	{ KI_SRP_GROUP, "SRP_GROUP", KT_LIST_OF_VALUES /* <G1,..> */, B_FALSE},
757978SPeter.Dunlap@Sun.COM 	{ KI_SRP_A, "SRP_A", KT_TEXT /* <A> */, B_TRUE},
767978SPeter.Dunlap@Sun.COM 	{ KI_SRP_B, "SRP_B", KT_TEXT /* <B> */, B_TRUE},
777978SPeter.Dunlap@Sun.COM 	{ KI_SRP_M, "SRP_M", KT_TEXT /* <M> */, B_TRUE},
787978SPeter.Dunlap@Sun.COM 	{ KI_SRM_HM, "SRP_HM", KT_TEXT /* <H(A | M | K)> */, B_TRUE},
797978SPeter.Dunlap@Sun.COM 
807978SPeter.Dunlap@Sun.COM 	/*
817978SPeter.Dunlap@Sun.COM 	 * CHAP
827978SPeter.Dunlap@Sun.COM 	 */
837978SPeter.Dunlap@Sun.COM 	{ KI_CHAP_A, "CHAP_A", KT_LIST_OF_VALUES /* <A1,A2,..> */, B_FALSE },
847978SPeter.Dunlap@Sun.COM 	{ KI_CHAP_I, "CHAP_I", KT_NUMERICAL /* <I> */, B_TRUE },
857978SPeter.Dunlap@Sun.COM 	{ KI_CHAP_C, "CHAP_C", KT_BINARY /* <C> */, B_TRUE },
867978SPeter.Dunlap@Sun.COM 	{ KI_CHAP_N, "CHAP_N", KT_TEXT /* <N> */, B_TRUE },
877978SPeter.Dunlap@Sun.COM 	{ KI_CHAP_R, "CHAP_R", KT_BINARY /* <N> */, B_TRUE },
887978SPeter.Dunlap@Sun.COM 
897978SPeter.Dunlap@Sun.COM 
907978SPeter.Dunlap@Sun.COM 	/*
917978SPeter.Dunlap@Sun.COM 	 * ISCSI Operational Parameter Keys
927978SPeter.Dunlap@Sun.COM 	 */
937978SPeter.Dunlap@Sun.COM 	{ KI_HEADER_DIGEST, "HeaderDigest", KT_LIST_OF_VALUES, B_FALSE },
947978SPeter.Dunlap@Sun.COM 	{ KI_DATA_DIGEST, "DataDigest", KT_LIST_OF_VALUES, B_FALSE },
957978SPeter.Dunlap@Sun.COM 	{ KI_MAX_CONNECTIONS, "MaxConnections", KT_NUMERICAL, B_FALSE },
967978SPeter.Dunlap@Sun.COM 	{ KI_SEND_TARGETS, "SendTargets", KT_TEXT, B_FALSE },
977978SPeter.Dunlap@Sun.COM 	{ KI_TARGET_NAME, "TargetName", KT_ISCSI_NAME, B_TRUE},
987978SPeter.Dunlap@Sun.COM 	{ KI_INITIATOR_NAME, "InitiatorName", KT_ISCSI_NAME, B_TRUE},
997978SPeter.Dunlap@Sun.COM 	{ KI_TARGET_ALIAS, "TargetAlias", KT_ISCSI_LOCAL_NAME, B_TRUE},
1007978SPeter.Dunlap@Sun.COM 	{ KI_INITIATOR_ALIAS, "InitiatorAlias", KT_ISCSI_LOCAL_NAME, B_TRUE},
1017978SPeter.Dunlap@Sun.COM 	{ KI_TARGET_ADDRESS, "TargetAddress", KT_TEXT, B_TRUE},
1027978SPeter.Dunlap@Sun.COM 	{ KI_TARGET_PORTAL_GROUP_TAG, "TargetPortalGroupTag",
1037978SPeter.Dunlap@Sun.COM 	    KT_NUMERICAL, B_TRUE },
1047978SPeter.Dunlap@Sun.COM 	{ KI_INITIAL_R2T, "InitialR2T", KT_BOOLEAN, B_FALSE },
1057978SPeter.Dunlap@Sun.COM 	{ KI_IMMEDIATE_DATA, "ImmediateData", KT_BOOLEAN, B_FALSE },
1067978SPeter.Dunlap@Sun.COM 	{ KI_MAX_RECV_DATA_SEGMENT_LENGTH, "MaxRecvDataSegmentLength",
1077978SPeter.Dunlap@Sun.COM 	    KT_NUMERICAL /* 512 to 2^24 - 1 */, B_TRUE },
1087978SPeter.Dunlap@Sun.COM 	{ KI_MAX_BURST_LENGTH, "MaxBurstLength",
1097978SPeter.Dunlap@Sun.COM 	    KT_NUMERICAL /* 512 to 2^24 - 1 */, B_FALSE },
1107978SPeter.Dunlap@Sun.COM 	{ KI_FIRST_BURST_LENGTH, "FirstBurstLength",
1117978SPeter.Dunlap@Sun.COM 	    KT_NUMERICAL /* 512 to 2^24 - 1 */, B_FALSE },
1127978SPeter.Dunlap@Sun.COM 	{ KI_DEFAULT_TIME_2_WAIT, "DefaultTime2Wait",
1137978SPeter.Dunlap@Sun.COM 	    KT_NUMERICAL /* 0 to 2600 */, B_FALSE },
1147978SPeter.Dunlap@Sun.COM 	{ KI_DEFAULT_TIME_2_RETAIN, "DefaultTime2Retain",
1157978SPeter.Dunlap@Sun.COM 	    KT_NUMERICAL /* 0 to 2600 */, B_FALSE },
1167978SPeter.Dunlap@Sun.COM 	{ KI_MAX_OUTSTANDING_R2T, "MaxOutstandingR2T",
1177978SPeter.Dunlap@Sun.COM 	    KT_NUMERICAL /* 1 to 65535 */, B_FALSE },
1187978SPeter.Dunlap@Sun.COM 	{ KI_DATA_PDU_IN_ORDER, "DataPDUInOrder", KT_BOOLEAN, B_FALSE },
1197978SPeter.Dunlap@Sun.COM 	{ KI_DATA_SEQUENCE_IN_ORDER, "DataSequenceInOrder",
1207978SPeter.Dunlap@Sun.COM 	    KT_BOOLEAN, B_FALSE },
1217978SPeter.Dunlap@Sun.COM 	{ KI_ERROR_RECOVERY_LEVEL, "ErrorRecoveryLevel",
1227978SPeter.Dunlap@Sun.COM 	    KT_NUMERICAL /* 0 to 2 */, B_FALSE },
1237978SPeter.Dunlap@Sun.COM 	{ KI_SESSION_TYPE, "SessionType", KT_TEXT, B_TRUE },
1247978SPeter.Dunlap@Sun.COM 	{ KI_OFMARKER, "OFMarker", KT_BOOLEAN, B_FALSE },
1257978SPeter.Dunlap@Sun.COM 	{ KI_OFMARKERINT, "OFMarkerInt", KT_NUMERIC_RANGE, B_FALSE },
1267978SPeter.Dunlap@Sun.COM 	{ KI_IFMARKER, "IFMarker", KT_BOOLEAN, B_FALSE },
1277978SPeter.Dunlap@Sun.COM 	{ KI_IFMARKERINT, "IFMarkerInt", KT_NUMERIC_RANGE, B_FALSE },
1287978SPeter.Dunlap@Sun.COM 
1297978SPeter.Dunlap@Sun.COM 	/*
1307978SPeter.Dunlap@Sun.COM 	 * iSER-specific keys
1317978SPeter.Dunlap@Sun.COM 	 */
1327978SPeter.Dunlap@Sun.COM 	{ KI_RDMA_EXTENSIONS, "RDMAExtensions", KT_BOOLEAN, B_FALSE },
1337978SPeter.Dunlap@Sun.COM 	{ KI_TARGET_RECV_DATA_SEGMENT_LENGTH, "TargetRecvDataSegmentLength",
1347978SPeter.Dunlap@Sun.COM 	    KT_NUMERICAL /* 512 to 2^24 - 1 */, B_FALSE },
1357978SPeter.Dunlap@Sun.COM 	{ KI_INITIATOR_RECV_DATA_SEGMENT_LENGTH,
1367978SPeter.Dunlap@Sun.COM 	    "InitiatorRecvDataSegmentLength",
1377978SPeter.Dunlap@Sun.COM 	    KT_NUMERICAL /* 512 to 2^24 - 1 */, B_FALSE },
1387978SPeter.Dunlap@Sun.COM 	{ KI_MAX_OUTSTANDING_UNEXPECTED_PDUS, "MaxOutstandingUnexpectedPDUs",
1397978SPeter.Dunlap@Sun.COM 	    KT_NUMERICAL /* 2 to 2^32 - 1 | 0 */, B_TRUE },
1407978SPeter.Dunlap@Sun.COM 
1417978SPeter.Dunlap@Sun.COM 	/*
1427978SPeter.Dunlap@Sun.COM 	 * Table terminator. The type KT_TEXT will allow the response
1437978SPeter.Dunlap@Sun.COM 	 * value of "NotUnderstood".
1447978SPeter.Dunlap@Sun.COM 	 */
1457978SPeter.Dunlap@Sun.COM 	{ KI_MAX_KEY, NULL, KT_TEXT, B_TRUE } /* Terminator */
1467978SPeter.Dunlap@Sun.COM };
1477978SPeter.Dunlap@Sun.COM 
1487978SPeter.Dunlap@Sun.COM 
1497978SPeter.Dunlap@Sun.COM #define	TEXTBUF_CHUNKSIZE 8192
1507978SPeter.Dunlap@Sun.COM 
1517978SPeter.Dunlap@Sun.COM typedef struct {
1527978SPeter.Dunlap@Sun.COM 	char	*itb_mem;
1537978SPeter.Dunlap@Sun.COM 	int	itb_offset;
1547978SPeter.Dunlap@Sun.COM 	int	itb_mem_len;
1557978SPeter.Dunlap@Sun.COM } idm_textbuf_t;
1567978SPeter.Dunlap@Sun.COM 
1577978SPeter.Dunlap@Sun.COM /*
1587978SPeter.Dunlap@Sun.COM  * Ignore all but the following keys during security negotiation
1597978SPeter.Dunlap@Sun.COM  *
1607978SPeter.Dunlap@Sun.COM  * SessionType
1617978SPeter.Dunlap@Sun.COM  * InitiatorName
1627978SPeter.Dunlap@Sun.COM  * TargetName
1637978SPeter.Dunlap@Sun.COM  * TargetAddress
1647978SPeter.Dunlap@Sun.COM  * InitiatorAlias
1657978SPeter.Dunlap@Sun.COM  * TargetAlias
1667978SPeter.Dunlap@Sun.COM  * TargetPortalGroupTag
1677978SPeter.Dunlap@Sun.COM  * AuthMethod and associated auth keys
1687978SPeter.Dunlap@Sun.COM  */
1697978SPeter.Dunlap@Sun.COM 
1707978SPeter.Dunlap@Sun.COM static int idm_keyvalue_get_next(char **tb_scan, int *tb_len,
1717978SPeter.Dunlap@Sun.COM     char **key, int *keylen, char **value);
1727978SPeter.Dunlap@Sun.COM 
1737978SPeter.Dunlap@Sun.COM static int idm_nvlist_add_kv(nvlist_t *nvl, const idm_kv_xlate_t *ikvx,
1747978SPeter.Dunlap@Sun.COM     char *value);
1757978SPeter.Dunlap@Sun.COM 
1767978SPeter.Dunlap@Sun.COM static int idm_nvlist_add_string(nvlist_t *nvl,
1777978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, char *value);
1787978SPeter.Dunlap@Sun.COM 
1797978SPeter.Dunlap@Sun.COM static int idm_nvlist_add_boolean(nvlist_t *nvl,
1807978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, char *value);
1817978SPeter.Dunlap@Sun.COM 
1827978SPeter.Dunlap@Sun.COM static int idm_nvlist_add_binary(nvlist_t *nvl,
1837978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, char *value);
1847978SPeter.Dunlap@Sun.COM 
1857978SPeter.Dunlap@Sun.COM static int idm_nvlist_add_large_numerical(nvlist_t *nvl,
1867978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, char *value);
1877978SPeter.Dunlap@Sun.COM 
1887978SPeter.Dunlap@Sun.COM static int idm_nvlist_add_numerical(nvlist_t *nvl,
1897978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, char *value);
1907978SPeter.Dunlap@Sun.COM 
1917978SPeter.Dunlap@Sun.COM static int idm_nvlist_add_numeric_range(nvlist_t *nvl,
1927978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, char *value);
1937978SPeter.Dunlap@Sun.COM 
1947978SPeter.Dunlap@Sun.COM static int idm_nvlist_add_list_of_values(nvlist_t *nvl,
1957978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, char *value);
1967978SPeter.Dunlap@Sun.COM 
1977978SPeter.Dunlap@Sun.COM static int idm_itextbuf_add_nvpair(nvpair_t *nvp, idm_textbuf_t *itb);
1987978SPeter.Dunlap@Sun.COM 
1997978SPeter.Dunlap@Sun.COM static int idm_itextbuf_add_string(nvpair_t *nvp,
2007978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
2017978SPeter.Dunlap@Sun.COM 
2027978SPeter.Dunlap@Sun.COM static int idm_itextbuf_add_boolean(nvpair_t *nvp,
2037978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
2047978SPeter.Dunlap@Sun.COM 
2057978SPeter.Dunlap@Sun.COM static int idm_itextbuf_add_binary(nvpair_t *nvp,
2067978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
2077978SPeter.Dunlap@Sun.COM 
2087978SPeter.Dunlap@Sun.COM static int idm_itextbuf_add_large_numerical(nvpair_t *nvp,
2097978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
2107978SPeter.Dunlap@Sun.COM 
2117978SPeter.Dunlap@Sun.COM static int idm_itextbuf_add_numerical(nvpair_t *nvp,
2127978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
2137978SPeter.Dunlap@Sun.COM 
2147978SPeter.Dunlap@Sun.COM static int idm_itextbuf_add_numeric_range(nvpair_t *nvp,
2157978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
2167978SPeter.Dunlap@Sun.COM 
2177978SPeter.Dunlap@Sun.COM static int idm_itextbuf_add_list_of_values(nvpair_t *nvp,
2187978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
2197978SPeter.Dunlap@Sun.COM 
2207978SPeter.Dunlap@Sun.COM static void textbuf_memcpy(idm_textbuf_t *itb, void *mem, int mem_len);
2217978SPeter.Dunlap@Sun.COM 
2227978SPeter.Dunlap@Sun.COM static void textbuf_strcpy(idm_textbuf_t *itb, char *str);
2237978SPeter.Dunlap@Sun.COM 
2247978SPeter.Dunlap@Sun.COM static void textbuf_append_char(idm_textbuf_t *itb, char c);
2257978SPeter.Dunlap@Sun.COM 
2267978SPeter.Dunlap@Sun.COM static void textbuf_terminate_kvpair(idm_textbuf_t *itb);
2277978SPeter.Dunlap@Sun.COM 
2287978SPeter.Dunlap@Sun.COM static int idm_ascii_to_hex(char *enc_hex_byte, uint8_t *bin_val);
2297978SPeter.Dunlap@Sun.COM 
2307978SPeter.Dunlap@Sun.COM static int idm_base16_str_to_binary(char *hstr, int hstr_len,
2317978SPeter.Dunlap@Sun.COM     uint8_t *binary, int binary_length);
2327978SPeter.Dunlap@Sun.COM 
2337978SPeter.Dunlap@Sun.COM static size_t idm_strcspn(const char *string, const char *charset);
2347978SPeter.Dunlap@Sun.COM 
2357978SPeter.Dunlap@Sun.COM static size_t idm_strnlen(const char *str, size_t maxlen);
2367978SPeter.Dunlap@Sun.COM 
2377978SPeter.Dunlap@Sun.COM /*
2387978SPeter.Dunlap@Sun.COM  * Processes all whole iSCSI name-value pairs in a text buffer and adds
2397978SPeter.Dunlap@Sun.COM  * a corresponding Solaris nvpair_t to the provided nvlist.  If the last
2407978SPeter.Dunlap@Sun.COM  * iSCSI name-value pair in textbuf is truncated (which can occur when
2417978SPeter.Dunlap@Sun.COM  * the request spans multiple PDU's) then upon return textbuf will
2427978SPeter.Dunlap@Sun.COM  * point to the truncated iSCSI name-value pair in the buffer and
2437978SPeter.Dunlap@Sun.COM  * textbuflen will contain the remaining bytes in the buffer.  The
2447978SPeter.Dunlap@Sun.COM  * caller can save off this fragment of the iSCSI name-value pair for
2457978SPeter.Dunlap@Sun.COM  * use when the next PDU in the request arrives.
2467978SPeter.Dunlap@Sun.COM  *
2477978SPeter.Dunlap@Sun.COM  * textbuflen includes the trailing 0x00!
2487978SPeter.Dunlap@Sun.COM  */
2497978SPeter.Dunlap@Sun.COM 
2507978SPeter.Dunlap@Sun.COM int
idm_textbuf_to_nvlist(nvlist_t * nvl,char ** textbuf,int * textbuflen)2517978SPeter.Dunlap@Sun.COM idm_textbuf_to_nvlist(nvlist_t *nvl, char **textbuf, int *textbuflen)
2527978SPeter.Dunlap@Sun.COM {
2537978SPeter.Dunlap@Sun.COM 	int rc = 0;
2547978SPeter.Dunlap@Sun.COM 	char *tbscan, *key, *value;
2557978SPeter.Dunlap@Sun.COM 	int tblen, keylen;
2567978SPeter.Dunlap@Sun.COM 
2577978SPeter.Dunlap@Sun.COM 	tbscan = *textbuf;
2587978SPeter.Dunlap@Sun.COM 	tblen = *textbuflen;
2597978SPeter.Dunlap@Sun.COM 
2607978SPeter.Dunlap@Sun.COM 	for (;;) {
2617978SPeter.Dunlap@Sun.COM 		if ((rc = idm_keyvalue_get_next(&tbscan, &tblen,
2627978SPeter.Dunlap@Sun.COM 		    &key, &keylen, &value)) != 0) {
2637978SPeter.Dunlap@Sun.COM 			/* There was a problem reading the key/value pair */
2647978SPeter.Dunlap@Sun.COM 			break;
2657978SPeter.Dunlap@Sun.COM 		}
2667978SPeter.Dunlap@Sun.COM 
2677978SPeter.Dunlap@Sun.COM 		if ((rc = idm_nvlist_add_keyvalue(nvl,
2687978SPeter.Dunlap@Sun.COM 		    key, keylen, value)) != 0) {
2697978SPeter.Dunlap@Sun.COM 			/* Something was wrong with either the key or value */
2707978SPeter.Dunlap@Sun.COM 			break;
2717978SPeter.Dunlap@Sun.COM 		}
2727978SPeter.Dunlap@Sun.COM 
2737978SPeter.Dunlap@Sun.COM 		if (tblen == 0) {
2747978SPeter.Dunlap@Sun.COM 			/* End of text buffer */
2757978SPeter.Dunlap@Sun.COM 			break;
2767978SPeter.Dunlap@Sun.COM 		}
2777978SPeter.Dunlap@Sun.COM 	}
2787978SPeter.Dunlap@Sun.COM 
2797978SPeter.Dunlap@Sun.COM 	*textbuf = tbscan;
2807978SPeter.Dunlap@Sun.COM 	*textbuflen = tblen;
2817978SPeter.Dunlap@Sun.COM 
2827978SPeter.Dunlap@Sun.COM 	return (rc);
2837978SPeter.Dunlap@Sun.COM }
2847978SPeter.Dunlap@Sun.COM 
2857978SPeter.Dunlap@Sun.COM /*
2867978SPeter.Dunlap@Sun.COM  * If a test buffer starts with an ISCSI name-value pair fragment (a
2877978SPeter.Dunlap@Sun.COM  * continuation from a previous buffer) return the length of the fragment
2887978SPeter.Dunlap@Sun.COM  * contained in this buffer.  We do not handle name-value pairs that span
2897978SPeter.Dunlap@Sun.COM  * more than two buffers so if this buffer does not contain the remainder
2907978SPeter.Dunlap@Sun.COM  * of the name value pair the function will return 0.  If the first
2917978SPeter.Dunlap@Sun.COM  * name-value pair in the buffer is complete the functionw will return 0.
2927978SPeter.Dunlap@Sun.COM  */
2937978SPeter.Dunlap@Sun.COM int
idm_textbuf_to_firstfraglen(void * textbuf,int textbuflen)2947978SPeter.Dunlap@Sun.COM idm_textbuf_to_firstfraglen(void *textbuf, int textbuflen)
2957978SPeter.Dunlap@Sun.COM {
2967978SPeter.Dunlap@Sun.COM 	return (idm_strnlen(textbuf, textbuflen));
2977978SPeter.Dunlap@Sun.COM }
2987978SPeter.Dunlap@Sun.COM 
2997978SPeter.Dunlap@Sun.COM static int
idm_keyvalue_get_next(char ** tb_scan,int * tb_len,char ** key,int * keylen,char ** value)3007978SPeter.Dunlap@Sun.COM idm_keyvalue_get_next(char **tb_scan, int *tb_len,
3017978SPeter.Dunlap@Sun.COM     char **key, int *keylen, char **value)
3027978SPeter.Dunlap@Sun.COM {
3037978SPeter.Dunlap@Sun.COM 	/*
3047978SPeter.Dunlap@Sun.COM 	 * Caller doesn't need "valuelen" returned since "value" will
3057978SPeter.Dunlap@Sun.COM 	 * always be a NULL-terminated string.
3067978SPeter.Dunlap@Sun.COM 	 */
3077978SPeter.Dunlap@Sun.COM 	size_t total_len, valuelen;
3087978SPeter.Dunlap@Sun.COM 
3097978SPeter.Dunlap@Sun.COM 	/*
3107978SPeter.Dunlap@Sun.COM 	 * How many bytes to the first '\0'?  This represents the total
3117978SPeter.Dunlap@Sun.COM 	 * length of our iSCSI key/value pair.
3127978SPeter.Dunlap@Sun.COM 	 */
3137978SPeter.Dunlap@Sun.COM 	total_len = idm_strnlen(*tb_scan, *tb_len);
3147978SPeter.Dunlap@Sun.COM 	if (total_len == *tb_len) {
3157978SPeter.Dunlap@Sun.COM 		/*
3167978SPeter.Dunlap@Sun.COM 		 * No '\0', perhaps this key/value pair is continued in
3177978SPeter.Dunlap@Sun.COM 		 * another buffer
3187978SPeter.Dunlap@Sun.COM 		 */
3197978SPeter.Dunlap@Sun.COM 		return (E2BIG);
3207978SPeter.Dunlap@Sun.COM 	}
3217978SPeter.Dunlap@Sun.COM 
3227978SPeter.Dunlap@Sun.COM 	/*
3237978SPeter.Dunlap@Sun.COM 	 * Found NULL, so this is a possible key-value pair.  At
3247978SPeter.Dunlap@Sun.COM 	 * the same time we've validated that there is actually a
3257978SPeter.Dunlap@Sun.COM 	 * NULL in this string so it's safe to use regular
3267978SPeter.Dunlap@Sun.COM 	 * string functions (i.e. strcpy instead of strncpy)
3277978SPeter.Dunlap@Sun.COM 	 */
3287978SPeter.Dunlap@Sun.COM 	*key = *tb_scan;
3297978SPeter.Dunlap@Sun.COM 	*keylen = idm_strcspn(*tb_scan, "=");
3307978SPeter.Dunlap@Sun.COM 
3317978SPeter.Dunlap@Sun.COM 	if (*keylen == total_len) {
3327978SPeter.Dunlap@Sun.COM 		/* No '=', bad format */
3337978SPeter.Dunlap@Sun.COM 		return (EINVAL);
3347978SPeter.Dunlap@Sun.COM 	}
3357978SPeter.Dunlap@Sun.COM 
3367978SPeter.Dunlap@Sun.COM 	*tb_scan += *keylen + 1; /* Skip the '=' */
3377978SPeter.Dunlap@Sun.COM 	*tb_len -= *keylen + 1;
3387978SPeter.Dunlap@Sun.COM 
3397978SPeter.Dunlap@Sun.COM 	/*
3407978SPeter.Dunlap@Sun.COM 	 * The remaining text after the '=' is the value
3417978SPeter.Dunlap@Sun.COM 	 */
3427978SPeter.Dunlap@Sun.COM 	*value = *tb_scan;
3437978SPeter.Dunlap@Sun.COM 	valuelen = total_len - (*keylen + 1);
3447978SPeter.Dunlap@Sun.COM 
3457978SPeter.Dunlap@Sun.COM 	*tb_scan += valuelen + 1; /* Skip the '\0' */
3467978SPeter.Dunlap@Sun.COM 	*tb_len -= valuelen + 1;
3477978SPeter.Dunlap@Sun.COM 
3487978SPeter.Dunlap@Sun.COM 	return (0);
3497978SPeter.Dunlap@Sun.COM }
3507978SPeter.Dunlap@Sun.COM 
3517978SPeter.Dunlap@Sun.COM const idm_kv_xlate_t *
idm_lookup_kv_xlate(const char * key,int keylen)3527978SPeter.Dunlap@Sun.COM idm_lookup_kv_xlate(const char *key, int keylen)
3537978SPeter.Dunlap@Sun.COM {
3547978SPeter.Dunlap@Sun.COM 	const idm_kv_xlate_t *ikvx = &idm_kvpair_xlate[0];
3557978SPeter.Dunlap@Sun.COM 
3567978SPeter.Dunlap@Sun.COM 	/*
3577978SPeter.Dunlap@Sun.COM 	 * Look for a matching key value in the key/value pair table.
3587978SPeter.Dunlap@Sun.COM 	 * The matching entry in the table will tell us how to encode
3597978SPeter.Dunlap@Sun.COM 	 * the key and value in the nvlist.  If we don't recognize
3607978SPeter.Dunlap@Sun.COM 	 * the key then we will simply encode it in string format.
3617978SPeter.Dunlap@Sun.COM 	 * The login or text request code can generate the appropriate
3627978SPeter.Dunlap@Sun.COM 	 * "not understood" resposne.
3637978SPeter.Dunlap@Sun.COM 	 */
3647978SPeter.Dunlap@Sun.COM 	while (ikvx->ik_key_id != KI_MAX_KEY) {
3657978SPeter.Dunlap@Sun.COM 		/*
3667978SPeter.Dunlap@Sun.COM 		 * Compare strings.  "key" is not NULL-terminated so
3677978SPeter.Dunlap@Sun.COM 		 * use strncmp.  Since we are using strncmp we
3687978SPeter.Dunlap@Sun.COM 		 * need to check that the lengths match, otherwise
3697978SPeter.Dunlap@Sun.COM 		 * we might unintentionally lookup "TargetAddress"
3707978SPeter.Dunlap@Sun.COM 		 * with a key of "Target" (or something similar).
3717978SPeter.Dunlap@Sun.COM 		 *
3727978SPeter.Dunlap@Sun.COM 		 * "value" is NULL-terminated so we can use it as
3737978SPeter.Dunlap@Sun.COM 		 * a regular string.
3747978SPeter.Dunlap@Sun.COM 		 */
3757978SPeter.Dunlap@Sun.COM 		if ((strncmp(ikvx->ik_key_name, key, keylen) == 0) &&
3767978SPeter.Dunlap@Sun.COM 		    (strlen(ikvx->ik_key_name) == keylen)) {
3777978SPeter.Dunlap@Sun.COM 			/* Exit the loop since we found a match */
3787978SPeter.Dunlap@Sun.COM 			break;
3797978SPeter.Dunlap@Sun.COM 		}
3807978SPeter.Dunlap@Sun.COM 
3817978SPeter.Dunlap@Sun.COM 		/* No match, look at the next entry */
3827978SPeter.Dunlap@Sun.COM 		ikvx++;
3837978SPeter.Dunlap@Sun.COM 	}
3847978SPeter.Dunlap@Sun.COM 
3857978SPeter.Dunlap@Sun.COM 	return (ikvx);
3867978SPeter.Dunlap@Sun.COM }
3877978SPeter.Dunlap@Sun.COM 
3887978SPeter.Dunlap@Sun.COM static int
idm_nvlist_add_kv(nvlist_t * nvl,const idm_kv_xlate_t * ikvx,char * value)3897978SPeter.Dunlap@Sun.COM idm_nvlist_add_kv(nvlist_t *nvl,  const idm_kv_xlate_t *ikvx, char *value)
3907978SPeter.Dunlap@Sun.COM {
3917978SPeter.Dunlap@Sun.COM 	int rc;
3927978SPeter.Dunlap@Sun.COM 
3937978SPeter.Dunlap@Sun.COM 	switch (ikvx->ik_idm_type) {
3947978SPeter.Dunlap@Sun.COM 	case KT_TEXT:
3957978SPeter.Dunlap@Sun.COM 	case KT_SIMPLE:
3967978SPeter.Dunlap@Sun.COM 	case KT_ISCSI_NAME:
3977978SPeter.Dunlap@Sun.COM 	case KT_ISCSI_LOCAL_NAME:
3987978SPeter.Dunlap@Sun.COM 		rc = idm_nvlist_add_string(nvl, ikvx, value);
3997978SPeter.Dunlap@Sun.COM 		break;
4007978SPeter.Dunlap@Sun.COM 	case KT_BOOLEAN:
4017978SPeter.Dunlap@Sun.COM 		rc = idm_nvlist_add_boolean(nvl, ikvx, value);
4027978SPeter.Dunlap@Sun.COM 		break;
4037978SPeter.Dunlap@Sun.COM 	case KT_REGULAR_BINARY:
4047978SPeter.Dunlap@Sun.COM 	case KT_LARGE_BINARY:
4057978SPeter.Dunlap@Sun.COM 	case KT_BINARY:
4067978SPeter.Dunlap@Sun.COM 		rc = idm_nvlist_add_binary(nvl, ikvx, value);
4077978SPeter.Dunlap@Sun.COM 		break;
4087978SPeter.Dunlap@Sun.COM 	case KT_LARGE_NUMERICAL:
4097978SPeter.Dunlap@Sun.COM 		rc = idm_nvlist_add_large_numerical(nvl, ikvx,
4107978SPeter.Dunlap@Sun.COM 		    value);
4117978SPeter.Dunlap@Sun.COM 		break;
4127978SPeter.Dunlap@Sun.COM 	case KT_NUMERICAL:
4137978SPeter.Dunlap@Sun.COM 		rc = idm_nvlist_add_numerical(nvl, ikvx,
4147978SPeter.Dunlap@Sun.COM 		    value);
4157978SPeter.Dunlap@Sun.COM 		break;
4167978SPeter.Dunlap@Sun.COM 	case KT_NUMERIC_RANGE:
4177978SPeter.Dunlap@Sun.COM 		rc = idm_nvlist_add_numeric_range(nvl, ikvx,
4187978SPeter.Dunlap@Sun.COM 		    value);
4197978SPeter.Dunlap@Sun.COM 		break;
4207978SPeter.Dunlap@Sun.COM 	case KT_LIST_OF_VALUES:
4217978SPeter.Dunlap@Sun.COM 		rc = idm_nvlist_add_list_of_values(nvl, ikvx,
4227978SPeter.Dunlap@Sun.COM 		    value);
4237978SPeter.Dunlap@Sun.COM 		break;
4247978SPeter.Dunlap@Sun.COM 	default:
4257978SPeter.Dunlap@Sun.COM 		ASSERT(0); /* This should never happen */
4267978SPeter.Dunlap@Sun.COM 		break;
4277978SPeter.Dunlap@Sun.COM 	}
4287978SPeter.Dunlap@Sun.COM 	if (rc != 0) {
4297978SPeter.Dunlap@Sun.COM 		/* could be one of the text constants */
4307978SPeter.Dunlap@Sun.COM 		rc = idm_nvlist_add_string(nvl, ikvx, value);
4317978SPeter.Dunlap@Sun.COM 	}
4327978SPeter.Dunlap@Sun.COM 
4337978SPeter.Dunlap@Sun.COM 	return (rc);
4347978SPeter.Dunlap@Sun.COM }
4357978SPeter.Dunlap@Sun.COM 
4367978SPeter.Dunlap@Sun.COM static int
idm_nvlist_add_string(nvlist_t * nvl,const idm_kv_xlate_t * ikvx,char * value)4377978SPeter.Dunlap@Sun.COM idm_nvlist_add_string(nvlist_t *nvl,
4387978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, char *value)
4397978SPeter.Dunlap@Sun.COM {
4407978SPeter.Dunlap@Sun.COM 	return (nvlist_add_string(nvl, ikvx->ik_key_name, value));
4417978SPeter.Dunlap@Sun.COM }
4427978SPeter.Dunlap@Sun.COM 
4437978SPeter.Dunlap@Sun.COM static int
idm_nvlist_add_boolean(nvlist_t * nvl,const idm_kv_xlate_t * ikvx,char * value)4447978SPeter.Dunlap@Sun.COM idm_nvlist_add_boolean(nvlist_t *nvl,
4457978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, char *value)
4467978SPeter.Dunlap@Sun.COM {
4477978SPeter.Dunlap@Sun.COM 	int rc;
4487978SPeter.Dunlap@Sun.COM 	boolean_t bool_val;
4497978SPeter.Dunlap@Sun.COM 
4507978SPeter.Dunlap@Sun.COM 	if (strcasecmp(value, "Yes") == 0) {
4517978SPeter.Dunlap@Sun.COM 		bool_val = B_TRUE;
4527978SPeter.Dunlap@Sun.COM 	} else if (strcasecmp(value, "No") == 0) {
4537978SPeter.Dunlap@Sun.COM 		bool_val = B_FALSE;
4547978SPeter.Dunlap@Sun.COM 	} else {
4557978SPeter.Dunlap@Sun.COM 		return (EINVAL);
4567978SPeter.Dunlap@Sun.COM 	}
4577978SPeter.Dunlap@Sun.COM 
4587978SPeter.Dunlap@Sun.COM 	rc = nvlist_add_boolean_value(nvl, ikvx->ik_key_name, bool_val);
4597978SPeter.Dunlap@Sun.COM 
4607978SPeter.Dunlap@Sun.COM 	return (rc);
4617978SPeter.Dunlap@Sun.COM }
4627978SPeter.Dunlap@Sun.COM 
4637978SPeter.Dunlap@Sun.COM static boolean_t
kv_is_hex(char * value)4647978SPeter.Dunlap@Sun.COM kv_is_hex(char *value)
4657978SPeter.Dunlap@Sun.COM {
4667978SPeter.Dunlap@Sun.COM 	return ((strncmp(value, "0x", strlen("0x")) == 0) ||
4677978SPeter.Dunlap@Sun.COM 	    (strncmp(value, "0X", strlen("0X")) == 0));
4687978SPeter.Dunlap@Sun.COM }
4697978SPeter.Dunlap@Sun.COM 
4707978SPeter.Dunlap@Sun.COM static boolean_t
kv_is_base64(char * value)4717978SPeter.Dunlap@Sun.COM kv_is_base64(char *value)
4727978SPeter.Dunlap@Sun.COM {
4737978SPeter.Dunlap@Sun.COM 	return ((strncmp(value, "0b", strlen("0b")) == 0) ||
4747978SPeter.Dunlap@Sun.COM 	    (strncmp(value, "0B", strlen("0B")) == 0));
4757978SPeter.Dunlap@Sun.COM }
4767978SPeter.Dunlap@Sun.COM 
4777978SPeter.Dunlap@Sun.COM 
4787978SPeter.Dunlap@Sun.COM static int
idm_nvlist_add_binary(nvlist_t * nvl,const idm_kv_xlate_t * ikvx,char * value)4797978SPeter.Dunlap@Sun.COM idm_nvlist_add_binary(nvlist_t *nvl,
4807978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, char *value)
4817978SPeter.Dunlap@Sun.COM {
4827978SPeter.Dunlap@Sun.COM 	int		rc;
4837978SPeter.Dunlap@Sun.COM 	int		value_length;
4847978SPeter.Dunlap@Sun.COM 	uint64_t	uint64_value;
4857978SPeter.Dunlap@Sun.COM 	int		binary_length;
4867978SPeter.Dunlap@Sun.COM 	uchar_t		*binary_array;
4877978SPeter.Dunlap@Sun.COM 
4887978SPeter.Dunlap@Sun.COM 	/*
4897978SPeter.Dunlap@Sun.COM 	 * A binary value can be either decimal, hex or base64.  If it's
4907978SPeter.Dunlap@Sun.COM 	 * decimal then the encoded string must be less than 64 bits in
4917978SPeter.Dunlap@Sun.COM 	 * length (8 characters).  In all cases we will convert the
4927978SPeter.Dunlap@Sun.COM 	 * included value to a byte array starting with the MSB.  The
4937978SPeter.Dunlap@Sun.COM 	 * assumption is that values meant to be treated as integers will
4947978SPeter.Dunlap@Sun.COM 	 * use the "numerical" and "large numerical" types.
4957978SPeter.Dunlap@Sun.COM 	 */
4967978SPeter.Dunlap@Sun.COM 	if (kv_is_hex(value)) {
4977978SPeter.Dunlap@Sun.COM 		value += strlen("0x");
4987978SPeter.Dunlap@Sun.COM 		value_length = strlen(value);
4997978SPeter.Dunlap@Sun.COM 		binary_length = (value_length + 1) / 2;
5007978SPeter.Dunlap@Sun.COM 		binary_array = kmem_alloc(binary_length, KM_SLEEP);
5017978SPeter.Dunlap@Sun.COM 
5027978SPeter.Dunlap@Sun.COM 		if (idm_base16_str_to_binary(value, value_length,
5037978SPeter.Dunlap@Sun.COM 		    binary_array, binary_length) != 0) {
5047978SPeter.Dunlap@Sun.COM 			kmem_free(binary_array, binary_length);
5057978SPeter.Dunlap@Sun.COM 			return (EINVAL);
5067978SPeter.Dunlap@Sun.COM 		}
5077978SPeter.Dunlap@Sun.COM 
5087978SPeter.Dunlap@Sun.COM 		rc = nvlist_add_byte_array(nvl, ikvx->ik_key_name,
5097978SPeter.Dunlap@Sun.COM 		    binary_array, binary_length);
5107978SPeter.Dunlap@Sun.COM 
5117978SPeter.Dunlap@Sun.COM 		kmem_free(binary_array, binary_length);
5127978SPeter.Dunlap@Sun.COM 
5137978SPeter.Dunlap@Sun.COM 		return (rc);
5147978SPeter.Dunlap@Sun.COM 
5157978SPeter.Dunlap@Sun.COM 	} else if (kv_is_base64(value)) {
5167978SPeter.Dunlap@Sun.COM 		value += strlen("0b");
5177978SPeter.Dunlap@Sun.COM 		value_length = strlen(value);
5187978SPeter.Dunlap@Sun.COM 		binary_array = kmem_alloc(value_length, KM_NOSLEEP);
5197978SPeter.Dunlap@Sun.COM 		if (binary_array == NULL) {
5207978SPeter.Dunlap@Sun.COM 			return (ENOMEM);
5217978SPeter.Dunlap@Sun.COM 		}
5227978SPeter.Dunlap@Sun.COM 
5237978SPeter.Dunlap@Sun.COM 		if (iscsi_base64_str_to_binary(value, value_length,
5247978SPeter.Dunlap@Sun.COM 		    binary_array, value_length, &binary_length) != 0) {
5257978SPeter.Dunlap@Sun.COM 			kmem_free(binary_array, value_length);
5267978SPeter.Dunlap@Sun.COM 			return (EINVAL);
5277978SPeter.Dunlap@Sun.COM 		}
5287978SPeter.Dunlap@Sun.COM 
5297978SPeter.Dunlap@Sun.COM 		rc = nvlist_add_byte_array(nvl, ikvx->ik_key_name,
5307978SPeter.Dunlap@Sun.COM 		    binary_array, binary_length);
5317978SPeter.Dunlap@Sun.COM 
5327978SPeter.Dunlap@Sun.COM 		kmem_free(binary_array, value_length);
5337978SPeter.Dunlap@Sun.COM 
5347978SPeter.Dunlap@Sun.COM 		return (rc);
5357978SPeter.Dunlap@Sun.COM 	} else {
5367978SPeter.Dunlap@Sun.COM 		/*
5377978SPeter.Dunlap@Sun.COM 		 * Decimal value (not permitted for "large-binary_value" so
5387978SPeter.Dunlap@Sun.COM 		 * it must be smaller than 64 bits.  It's not really
5397978SPeter.Dunlap@Sun.COM 		 * clear from the RFC what a decimal-binary-value might
5407978SPeter.Dunlap@Sun.COM 		 * represent but presumably it should be treated the same
5417978SPeter.Dunlap@Sun.COM 		 * as a hex or base64 value.  Therefore we'll convert it
5427978SPeter.Dunlap@Sun.COM 		 * to an array of bytes.
5437978SPeter.Dunlap@Sun.COM 		 */
5448872SJordan.Vaughan@Sun.com 		if ((rc = ddi_strtoull(value, NULL, 0,
5457978SPeter.Dunlap@Sun.COM 		    (u_longlong_t *)&uint64_value)) != 0)
5467978SPeter.Dunlap@Sun.COM 			return (rc);
5477978SPeter.Dunlap@Sun.COM 
5487978SPeter.Dunlap@Sun.COM 		rc = nvlist_add_byte_array(nvl, ikvx->ik_key_name,
5497978SPeter.Dunlap@Sun.COM 		    (uint8_t *)&uint64_value, sizeof (uint64_value));
5507978SPeter.Dunlap@Sun.COM 
5517978SPeter.Dunlap@Sun.COM 		return (rc);
5527978SPeter.Dunlap@Sun.COM 	}
5537978SPeter.Dunlap@Sun.COM 
5547978SPeter.Dunlap@Sun.COM 	/* NOTREACHED */
5557978SPeter.Dunlap@Sun.COM }
5567978SPeter.Dunlap@Sun.COM 
5577978SPeter.Dunlap@Sun.COM 
5587978SPeter.Dunlap@Sun.COM static int
idm_nvlist_add_large_numerical(nvlist_t * nvl,const idm_kv_xlate_t * ikvx,char * value)5597978SPeter.Dunlap@Sun.COM idm_nvlist_add_large_numerical(nvlist_t *nvl,
5607978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, char *value)
5617978SPeter.Dunlap@Sun.COM {
5627978SPeter.Dunlap@Sun.COM 	/*
5637978SPeter.Dunlap@Sun.COM 	 * A "large numerical" value can be larger than 64-bits.  Since
5647978SPeter.Dunlap@Sun.COM 	 * there is no upper bound on the size of the value, we will
5657978SPeter.Dunlap@Sun.COM 	 * punt and store it in string form.  We could also potentially
5667978SPeter.Dunlap@Sun.COM 	 * treat the value as binary data.
5677978SPeter.Dunlap@Sun.COM 	 */
5687978SPeter.Dunlap@Sun.COM 	return (nvlist_add_string(nvl, ikvx->ik_key_name, value));
5697978SPeter.Dunlap@Sun.COM }
5707978SPeter.Dunlap@Sun.COM 
5717978SPeter.Dunlap@Sun.COM 
5727978SPeter.Dunlap@Sun.COM static int
idm_nvlist_add_numerical(nvlist_t * nvl,const idm_kv_xlate_t * ikvx,char * value)5737978SPeter.Dunlap@Sun.COM idm_nvlist_add_numerical(nvlist_t *nvl,
5747978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, char *value)
5757978SPeter.Dunlap@Sun.COM {
5767978SPeter.Dunlap@Sun.COM 	int rc;
5777978SPeter.Dunlap@Sun.COM 	uint64_t uint64_value;
5787978SPeter.Dunlap@Sun.COM 
5797978SPeter.Dunlap@Sun.COM 	/*
5807978SPeter.Dunlap@Sun.COM 	 * "Numerical" values in the iSCSI standard are up to 64-bits wide.
5817978SPeter.Dunlap@Sun.COM 	 * On a 32-bit system we could see an overflow here during conversion.
5827978SPeter.Dunlap@Sun.COM 	 * This shouldn't happen with real-world values for the current
5837978SPeter.Dunlap@Sun.COM 	 * iSCSI parameters of "numerical" type.
5847978SPeter.Dunlap@Sun.COM 	 */
5858872SJordan.Vaughan@Sun.com 	rc = ddi_strtoull(value, NULL, 0, (u_longlong_t *)&uint64_value);
5867978SPeter.Dunlap@Sun.COM 	if (rc == 0) {
5877978SPeter.Dunlap@Sun.COM 		rc = nvlist_add_uint64(nvl, ikvx->ik_key_name, uint64_value);
5887978SPeter.Dunlap@Sun.COM 	}
5897978SPeter.Dunlap@Sun.COM 
5907978SPeter.Dunlap@Sun.COM 	return (rc);
5917978SPeter.Dunlap@Sun.COM }
5927978SPeter.Dunlap@Sun.COM 
5937978SPeter.Dunlap@Sun.COM 
5947978SPeter.Dunlap@Sun.COM static int
idm_nvlist_add_numeric_range(nvlist_t * nvl,const idm_kv_xlate_t * ikvx,char * range)5957978SPeter.Dunlap@Sun.COM idm_nvlist_add_numeric_range(nvlist_t *nvl,
5967978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, char *range)
5977978SPeter.Dunlap@Sun.COM {
5987978SPeter.Dunlap@Sun.COM 	nvlist_t *range_nvl;
5997978SPeter.Dunlap@Sun.COM 	char *val_scan = range;
6007978SPeter.Dunlap@Sun.COM 	uint64_t start_val, end_val;
6017978SPeter.Dunlap@Sun.COM 	int val_len, range_len;
6027978SPeter.Dunlap@Sun.COM 	int rc;
6037978SPeter.Dunlap@Sun.COM 
6047978SPeter.Dunlap@Sun.COM 	/* We'll store the range an an nvlist with two values */
6057978SPeter.Dunlap@Sun.COM 	rc = nvlist_alloc(&range_nvl, NV_UNIQUE_NAME, KM_NOSLEEP);
6067978SPeter.Dunlap@Sun.COM 	if (rc != 0) {
6077978SPeter.Dunlap@Sun.COM 		return (rc);
6087978SPeter.Dunlap@Sun.COM 	}
6097978SPeter.Dunlap@Sun.COM 
6107978SPeter.Dunlap@Sun.COM 	/*
6117978SPeter.Dunlap@Sun.COM 	 * We expect idm_keyvalue_get_next to ensure the string is
6127978SPeter.Dunlap@Sun.COM 	 * terminated
6137978SPeter.Dunlap@Sun.COM 	 */
6147978SPeter.Dunlap@Sun.COM 	range_len = strlen(range);
6157978SPeter.Dunlap@Sun.COM 
6167978SPeter.Dunlap@Sun.COM 	/*
6177978SPeter.Dunlap@Sun.COM 	 * Find range separator
6187978SPeter.Dunlap@Sun.COM 	 */
6197978SPeter.Dunlap@Sun.COM 	val_len = idm_strcspn(val_scan, "~");
6207978SPeter.Dunlap@Sun.COM 
6217978SPeter.Dunlap@Sun.COM 	if (val_len == range_len) {
6227978SPeter.Dunlap@Sun.COM 		/* invalid range */
6237978SPeter.Dunlap@Sun.COM 		nvlist_free(range_nvl);
6247978SPeter.Dunlap@Sun.COM 		return (EINVAL);
6257978SPeter.Dunlap@Sun.COM 	}
6267978SPeter.Dunlap@Sun.COM 
6277978SPeter.Dunlap@Sun.COM 	/*
6287978SPeter.Dunlap@Sun.COM 	 * Start value
6297978SPeter.Dunlap@Sun.COM 	 */
6307978SPeter.Dunlap@Sun.COM 	*(val_scan + val_len + 1) = '\0';
6318872SJordan.Vaughan@Sun.com 	rc = ddi_strtoull(val_scan, NULL, 0, (u_longlong_t *)&start_val);
6327978SPeter.Dunlap@Sun.COM 	if (rc == 0) {
6337978SPeter.Dunlap@Sun.COM 		rc = nvlist_add_uint64(range_nvl, "start", start_val);
6347978SPeter.Dunlap@Sun.COM 	}
6357978SPeter.Dunlap@Sun.COM 	if (rc != 0) {
6367978SPeter.Dunlap@Sun.COM 		nvlist_free(range_nvl);
6377978SPeter.Dunlap@Sun.COM 		return (rc);
6387978SPeter.Dunlap@Sun.COM 	}
6397978SPeter.Dunlap@Sun.COM 
6407978SPeter.Dunlap@Sun.COM 	/*
6417978SPeter.Dunlap@Sun.COM 	 * End value
6427978SPeter.Dunlap@Sun.COM 	 */
6437978SPeter.Dunlap@Sun.COM 	val_scan += val_len + 1;
6448872SJordan.Vaughan@Sun.com 	rc = ddi_strtoull(val_scan, NULL, 0, (u_longlong_t *)&end_val);
6457978SPeter.Dunlap@Sun.COM 	if (rc == 0) {
6467978SPeter.Dunlap@Sun.COM 		rc = nvlist_add_uint64(range_nvl, "start", end_val);
6477978SPeter.Dunlap@Sun.COM 	}
6487978SPeter.Dunlap@Sun.COM 	if (rc != 0) {
6497978SPeter.Dunlap@Sun.COM 		nvlist_free(range_nvl);
6507978SPeter.Dunlap@Sun.COM 		return (rc);
6517978SPeter.Dunlap@Sun.COM 	}
6527978SPeter.Dunlap@Sun.COM 
6537978SPeter.Dunlap@Sun.COM 	/*
6547978SPeter.Dunlap@Sun.COM 	 * Now add the "range" nvlist to the main nvlist
6557978SPeter.Dunlap@Sun.COM 	 */
6567978SPeter.Dunlap@Sun.COM 	rc = nvlist_add_nvlist(nvl, ikvx->ik_key_name, range_nvl);
6577978SPeter.Dunlap@Sun.COM 	if (rc != 0) {
6587978SPeter.Dunlap@Sun.COM 		nvlist_free(range_nvl);
6597978SPeter.Dunlap@Sun.COM 		return (rc);
6607978SPeter.Dunlap@Sun.COM 	}
6617978SPeter.Dunlap@Sun.COM 
6627978SPeter.Dunlap@Sun.COM 	nvlist_free(range_nvl);
6637978SPeter.Dunlap@Sun.COM 	return (0);
6647978SPeter.Dunlap@Sun.COM }
6657978SPeter.Dunlap@Sun.COM 
6667978SPeter.Dunlap@Sun.COM 
6677978SPeter.Dunlap@Sun.COM static int
idm_nvlist_add_list_of_values(nvlist_t * nvl,const idm_kv_xlate_t * ikvx,char * value_list)6687978SPeter.Dunlap@Sun.COM idm_nvlist_add_list_of_values(nvlist_t *nvl,
6697978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, char *value_list)
6707978SPeter.Dunlap@Sun.COM {
6717978SPeter.Dunlap@Sun.COM 	char value_name[8];
6727978SPeter.Dunlap@Sun.COM 	nvlist_t *value_list_nvl;
6737978SPeter.Dunlap@Sun.COM 	char *val_scan = value_list;
6747978SPeter.Dunlap@Sun.COM 	int value_index = 0;
6757978SPeter.Dunlap@Sun.COM 	int val_len, val_list_len;
6767978SPeter.Dunlap@Sun.COM 	int rc;
6777978SPeter.Dunlap@Sun.COM 
6787978SPeter.Dunlap@Sun.COM 	rc = nvlist_alloc(&value_list_nvl, NV_UNIQUE_NAME, KM_NOSLEEP);
6797978SPeter.Dunlap@Sun.COM 	if (rc != 0) {
6807978SPeter.Dunlap@Sun.COM 		return (rc);
6817978SPeter.Dunlap@Sun.COM 	}
6827978SPeter.Dunlap@Sun.COM 
6837978SPeter.Dunlap@Sun.COM 	/*
6847978SPeter.Dunlap@Sun.COM 	 * We expect idm_keyvalue_get_next to ensure the string is
6857978SPeter.Dunlap@Sun.COM 	 * terminated
6867978SPeter.Dunlap@Sun.COM 	 */
6877978SPeter.Dunlap@Sun.COM 	val_list_len = strlen(value_list);
6887978SPeter.Dunlap@Sun.COM 	if (val_list_len == 0) {
6897978SPeter.Dunlap@Sun.COM 		nvlist_free(value_list_nvl);
6907978SPeter.Dunlap@Sun.COM 		return (EINVAL);
6917978SPeter.Dunlap@Sun.COM 	}
6927978SPeter.Dunlap@Sun.COM 
6937978SPeter.Dunlap@Sun.COM 	for (;;) {
6947978SPeter.Dunlap@Sun.COM 		(void) snprintf(value_name, 8, "value%d", value_index);
6957978SPeter.Dunlap@Sun.COM 
6967978SPeter.Dunlap@Sun.COM 		val_len = idm_strcspn(val_scan, ",");
6977978SPeter.Dunlap@Sun.COM 
6987978SPeter.Dunlap@Sun.COM 		if (*(val_scan + val_len) != '\0') {
6997978SPeter.Dunlap@Sun.COM 			*(val_scan + val_len) = '\0';
7007978SPeter.Dunlap@Sun.COM 		}
7017978SPeter.Dunlap@Sun.COM 		rc = nvlist_add_string(value_list_nvl, value_name, val_scan);
7027978SPeter.Dunlap@Sun.COM 		if (rc != 0) {
7037978SPeter.Dunlap@Sun.COM 			nvlist_free(value_list_nvl);
7047978SPeter.Dunlap@Sun.COM 			return (rc);
7057978SPeter.Dunlap@Sun.COM 		}
7067978SPeter.Dunlap@Sun.COM 
7077978SPeter.Dunlap@Sun.COM 		/*
7087978SPeter.Dunlap@Sun.COM 		 * Move to next value, see if we're at the end of the value
7097978SPeter.Dunlap@Sun.COM 		 * list
7107978SPeter.Dunlap@Sun.COM 		 */
7117978SPeter.Dunlap@Sun.COM 		val_scan += val_len + 1;
7127978SPeter.Dunlap@Sun.COM 		if (val_scan == value_list + val_list_len + 1) {
7137978SPeter.Dunlap@Sun.COM 			break;
7147978SPeter.Dunlap@Sun.COM 		}
7157978SPeter.Dunlap@Sun.COM 
7167978SPeter.Dunlap@Sun.COM 		value_index++;
7177978SPeter.Dunlap@Sun.COM 	}
7187978SPeter.Dunlap@Sun.COM 
7197978SPeter.Dunlap@Sun.COM 	rc = nvlist_add_nvlist(nvl, ikvx->ik_key_name, value_list_nvl);
7207978SPeter.Dunlap@Sun.COM 	if (rc != 0) {
7217978SPeter.Dunlap@Sun.COM 		nvlist_free(value_list_nvl);
7227978SPeter.Dunlap@Sun.COM 		return (rc);
7237978SPeter.Dunlap@Sun.COM 	}
7247978SPeter.Dunlap@Sun.COM 
7257978SPeter.Dunlap@Sun.COM 	nvlist_free(value_list_nvl);
7267978SPeter.Dunlap@Sun.COM 	return (0);
7277978SPeter.Dunlap@Sun.COM }
7287978SPeter.Dunlap@Sun.COM 
7297978SPeter.Dunlap@Sun.COM /*
7307978SPeter.Dunlap@Sun.COM  * Convert an nvlist containing standard iSCSI key names and values into
7317978SPeter.Dunlap@Sun.COM  * a text buffer with properly formatted iSCSI key-value pairs ready to
7327978SPeter.Dunlap@Sun.COM  * transmit on the wire.  *textbuf should be NULL and will be set to point
7337978SPeter.Dunlap@Sun.COM  * the resulting text buffer.
7347978SPeter.Dunlap@Sun.COM  */
7357978SPeter.Dunlap@Sun.COM 
7367978SPeter.Dunlap@Sun.COM int
idm_nvlist_to_textbuf(nvlist_t * nvl,char ** textbuf,int * textbuflen,int * validlen)7377978SPeter.Dunlap@Sun.COM idm_nvlist_to_textbuf(nvlist_t *nvl, char **textbuf, int *textbuflen,
7387978SPeter.Dunlap@Sun.COM 	int *validlen)
7397978SPeter.Dunlap@Sun.COM {
7407978SPeter.Dunlap@Sun.COM 	int rc = 0;
7417978SPeter.Dunlap@Sun.COM 	nvpair_t *nvp = NULL;
7427978SPeter.Dunlap@Sun.COM 	idm_textbuf_t itb;
7437978SPeter.Dunlap@Sun.COM 
7447978SPeter.Dunlap@Sun.COM 	bzero(&itb, sizeof (itb));
7457978SPeter.Dunlap@Sun.COM 
7467978SPeter.Dunlap@Sun.COM 	for (;;) {
7477978SPeter.Dunlap@Sun.COM 		nvp = nvlist_next_nvpair(nvl, nvp);
7487978SPeter.Dunlap@Sun.COM 
7497978SPeter.Dunlap@Sun.COM 		if (nvp == NULL) {
7507978SPeter.Dunlap@Sun.COM 			/* Last nvpair in nvlist, we're done */
7517978SPeter.Dunlap@Sun.COM 			break;
7527978SPeter.Dunlap@Sun.COM 		}
7537978SPeter.Dunlap@Sun.COM 
7547978SPeter.Dunlap@Sun.COM 		if ((rc = idm_itextbuf_add_nvpair(nvp, &itb)) != 0) {
7557978SPeter.Dunlap@Sun.COM 			/* There was a problem building the key/value pair */
7567978SPeter.Dunlap@Sun.COM 			break;
7577978SPeter.Dunlap@Sun.COM 		}
7587978SPeter.Dunlap@Sun.COM 	}
7597978SPeter.Dunlap@Sun.COM 
7607978SPeter.Dunlap@Sun.COM 	*textbuf = itb.itb_mem;
7617978SPeter.Dunlap@Sun.COM 	*textbuflen = itb.itb_mem_len;
7627978SPeter.Dunlap@Sun.COM 	*validlen = itb.itb_offset;
7637978SPeter.Dunlap@Sun.COM 
7647978SPeter.Dunlap@Sun.COM 	return (rc);
7657978SPeter.Dunlap@Sun.COM }
7667978SPeter.Dunlap@Sun.COM 
7677978SPeter.Dunlap@Sun.COM static int
idm_itextbuf_add_nvpair(nvpair_t * nvp,idm_textbuf_t * itb)7687978SPeter.Dunlap@Sun.COM idm_itextbuf_add_nvpair(nvpair_t *nvp,
7697978SPeter.Dunlap@Sun.COM     idm_textbuf_t *itb)
7707978SPeter.Dunlap@Sun.COM {
7717978SPeter.Dunlap@Sun.COM 	int rc = 0;
7727978SPeter.Dunlap@Sun.COM 	char *key;
7737978SPeter.Dunlap@Sun.COM 	const idm_kv_xlate_t *ikvx;
7747978SPeter.Dunlap@Sun.COM 
7757978SPeter.Dunlap@Sun.COM 	key = nvpair_name(nvp);
7767978SPeter.Dunlap@Sun.COM 
7777978SPeter.Dunlap@Sun.COM 	ikvx = idm_lookup_kv_xlate(key, strlen(key));
7787978SPeter.Dunlap@Sun.COM 
7797978SPeter.Dunlap@Sun.COM 	/*
7807978SPeter.Dunlap@Sun.COM 	 * Any key supplied by the initiator that is not in our table
7817978SPeter.Dunlap@Sun.COM 	 * will be responded to with the string value "NotUnderstood".
7827978SPeter.Dunlap@Sun.COM 	 * An example is a vendor specific key.
7837978SPeter.Dunlap@Sun.COM 	 */
7847978SPeter.Dunlap@Sun.COM 	ASSERT((ikvx->ik_key_id != KI_MAX_KEY) ||
7857978SPeter.Dunlap@Sun.COM 	    (nvpair_type(nvp) == DATA_TYPE_STRING));
7867978SPeter.Dunlap@Sun.COM 
7877978SPeter.Dunlap@Sun.COM 	/*
7887978SPeter.Dunlap@Sun.COM 	 * Look for a matching key value in the key/value pair table.
7897978SPeter.Dunlap@Sun.COM 	 * The matching entry in the table will tell us how to encode
7907978SPeter.Dunlap@Sun.COM 	 * the key and value in the nvlist.
7917978SPeter.Dunlap@Sun.COM 	 */
7927978SPeter.Dunlap@Sun.COM 	switch (ikvx->ik_idm_type) {
7937978SPeter.Dunlap@Sun.COM 	case KT_TEXT:
7947978SPeter.Dunlap@Sun.COM 	case KT_SIMPLE:
7957978SPeter.Dunlap@Sun.COM 	case KT_ISCSI_NAME:
7967978SPeter.Dunlap@Sun.COM 	case KT_ISCSI_LOCAL_NAME:
7977978SPeter.Dunlap@Sun.COM 		rc = idm_itextbuf_add_string(nvp, ikvx, itb);
7987978SPeter.Dunlap@Sun.COM 		break;
7997978SPeter.Dunlap@Sun.COM 	case KT_BOOLEAN:
8007978SPeter.Dunlap@Sun.COM 		rc = idm_itextbuf_add_boolean(nvp, ikvx, itb);
8017978SPeter.Dunlap@Sun.COM 		break;
8027978SPeter.Dunlap@Sun.COM 	case KT_REGULAR_BINARY:
8037978SPeter.Dunlap@Sun.COM 	case KT_LARGE_BINARY:
8047978SPeter.Dunlap@Sun.COM 	case KT_BINARY:
8057978SPeter.Dunlap@Sun.COM 		rc = idm_itextbuf_add_binary(nvp, ikvx, itb);
8067978SPeter.Dunlap@Sun.COM 		break;
8077978SPeter.Dunlap@Sun.COM 	case KT_LARGE_NUMERICAL:
8087978SPeter.Dunlap@Sun.COM 		rc = idm_itextbuf_add_large_numerical(nvp, ikvx, itb);
8097978SPeter.Dunlap@Sun.COM 		break;
8107978SPeter.Dunlap@Sun.COM 	case KT_NUMERICAL:
8117978SPeter.Dunlap@Sun.COM 		rc = idm_itextbuf_add_numerical(nvp, ikvx, itb);
8127978SPeter.Dunlap@Sun.COM 		break;
8137978SPeter.Dunlap@Sun.COM 	case KT_NUMERIC_RANGE:
8147978SPeter.Dunlap@Sun.COM 		rc = idm_itextbuf_add_numeric_range(nvp, ikvx, itb);
8157978SPeter.Dunlap@Sun.COM 		break;
8167978SPeter.Dunlap@Sun.COM 	case KT_LIST_OF_VALUES:
8177978SPeter.Dunlap@Sun.COM 		rc = idm_itextbuf_add_list_of_values(nvp, ikvx, itb);
8187978SPeter.Dunlap@Sun.COM 		break;
8197978SPeter.Dunlap@Sun.COM 	default:
8207978SPeter.Dunlap@Sun.COM 		ASSERT(0); /* This should never happen */
8217978SPeter.Dunlap@Sun.COM 		break;
8227978SPeter.Dunlap@Sun.COM 	}
8237978SPeter.Dunlap@Sun.COM 
8247978SPeter.Dunlap@Sun.COM 	return (rc);
8257978SPeter.Dunlap@Sun.COM }
8267978SPeter.Dunlap@Sun.COM 
8277978SPeter.Dunlap@Sun.COM /* ARGSUSED */
8287978SPeter.Dunlap@Sun.COM static int
idm_itextbuf_add_string(nvpair_t * nvp,const idm_kv_xlate_t * ikvx,idm_textbuf_t * itb)8297978SPeter.Dunlap@Sun.COM idm_itextbuf_add_string(nvpair_t *nvp,
8307978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
8317978SPeter.Dunlap@Sun.COM {
8327978SPeter.Dunlap@Sun.COM 	char	*key_name;
8337978SPeter.Dunlap@Sun.COM 	char	*value;
8347978SPeter.Dunlap@Sun.COM 	int	rc;
8357978SPeter.Dunlap@Sun.COM 
8367978SPeter.Dunlap@Sun.COM 	/* Start with the key name */
8377978SPeter.Dunlap@Sun.COM 	key_name = nvpair_name(nvp);
8387978SPeter.Dunlap@Sun.COM 	textbuf_strcpy(itb, key_name);
8397978SPeter.Dunlap@Sun.COM 
8407978SPeter.Dunlap@Sun.COM 	/* Add separator */
8417978SPeter.Dunlap@Sun.COM 	textbuf_append_char(itb, '=');
8427978SPeter.Dunlap@Sun.COM 
8437978SPeter.Dunlap@Sun.COM 	/* Add value */
8447978SPeter.Dunlap@Sun.COM 	rc = nvpair_value_string(nvp, &value);
8457978SPeter.Dunlap@Sun.COM 	ASSERT(rc == 0);
8467978SPeter.Dunlap@Sun.COM 	textbuf_strcpy(itb, value);
8477978SPeter.Dunlap@Sun.COM 
8487978SPeter.Dunlap@Sun.COM 	/* Add trailing 0x00 */
8497978SPeter.Dunlap@Sun.COM 	textbuf_terminate_kvpair(itb);
8507978SPeter.Dunlap@Sun.COM 
8517978SPeter.Dunlap@Sun.COM 	return (0);
8527978SPeter.Dunlap@Sun.COM }
8537978SPeter.Dunlap@Sun.COM 
8547978SPeter.Dunlap@Sun.COM 
8557978SPeter.Dunlap@Sun.COM /* ARGSUSED */
8567978SPeter.Dunlap@Sun.COM static int
idm_itextbuf_add_boolean(nvpair_t * nvp,const idm_kv_xlate_t * ikvx,idm_textbuf_t * itb)8577978SPeter.Dunlap@Sun.COM idm_itextbuf_add_boolean(nvpair_t *nvp,
8587978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
8597978SPeter.Dunlap@Sun.COM {
8607978SPeter.Dunlap@Sun.COM 	char		*key_name;
8617978SPeter.Dunlap@Sun.COM 	boolean_t	value;
8627978SPeter.Dunlap@Sun.COM 	int	rc;
8637978SPeter.Dunlap@Sun.COM 
8647978SPeter.Dunlap@Sun.COM 	/* Start with the key name */
8657978SPeter.Dunlap@Sun.COM 	key_name = nvpair_name(nvp);
8667978SPeter.Dunlap@Sun.COM 	textbuf_strcpy(itb, key_name);
8677978SPeter.Dunlap@Sun.COM 
8687978SPeter.Dunlap@Sun.COM 	/* Add separator */
8697978SPeter.Dunlap@Sun.COM 	textbuf_append_char(itb, '=');
8707978SPeter.Dunlap@Sun.COM 
8717978SPeter.Dunlap@Sun.COM 	/* Add value */
8727978SPeter.Dunlap@Sun.COM 	rc = nvpair_value_boolean_value(nvp, &value);
8737978SPeter.Dunlap@Sun.COM 	ASSERT(rc == 0);
8747978SPeter.Dunlap@Sun.COM 	textbuf_strcpy(itb, value ? "Yes" : "No");
8757978SPeter.Dunlap@Sun.COM 
8767978SPeter.Dunlap@Sun.COM 	/* Add trailing 0x00 */
8777978SPeter.Dunlap@Sun.COM 	textbuf_terminate_kvpair(itb);
8787978SPeter.Dunlap@Sun.COM 
8797978SPeter.Dunlap@Sun.COM 	return (0);
8807978SPeter.Dunlap@Sun.COM }
8817978SPeter.Dunlap@Sun.COM 
8827978SPeter.Dunlap@Sun.COM /* ARGSUSED */
8837978SPeter.Dunlap@Sun.COM static int
idm_itextbuf_add_binary(nvpair_t * nvp,const idm_kv_xlate_t * ikvx,idm_textbuf_t * itb)8847978SPeter.Dunlap@Sun.COM idm_itextbuf_add_binary(nvpair_t *nvp,
8857978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
8867978SPeter.Dunlap@Sun.COM {
8877978SPeter.Dunlap@Sun.COM 	char		*key_name;
8887978SPeter.Dunlap@Sun.COM 	unsigned char	*value;
8897978SPeter.Dunlap@Sun.COM 	unsigned int	len;
8907978SPeter.Dunlap@Sun.COM 	unsigned long	n;
8917978SPeter.Dunlap@Sun.COM 	int	rc;
8927978SPeter.Dunlap@Sun.COM 
8937978SPeter.Dunlap@Sun.COM 	/* Start with the key name */
8947978SPeter.Dunlap@Sun.COM 	key_name = nvpair_name(nvp);
8957978SPeter.Dunlap@Sun.COM 	textbuf_strcpy(itb, key_name);
8967978SPeter.Dunlap@Sun.COM 
8977978SPeter.Dunlap@Sun.COM 	/* Add separator */
8987978SPeter.Dunlap@Sun.COM 	textbuf_append_char(itb, '=');
8997978SPeter.Dunlap@Sun.COM 
9007978SPeter.Dunlap@Sun.COM 	/* Add value */
9017978SPeter.Dunlap@Sun.COM 	rc = nvpair_value_byte_array(nvp, &value, &len);
9027978SPeter.Dunlap@Sun.COM 	ASSERT(rc == 0);
9037978SPeter.Dunlap@Sun.COM 
9047978SPeter.Dunlap@Sun.COM 	textbuf_strcpy(itb, "0x");
9057978SPeter.Dunlap@Sun.COM 
9067978SPeter.Dunlap@Sun.COM 	while (len > 0) {
9077978SPeter.Dunlap@Sun.COM 		n = *value++;
9087978SPeter.Dunlap@Sun.COM 		len--;
9097978SPeter.Dunlap@Sun.COM 
9107978SPeter.Dunlap@Sun.COM 		textbuf_append_char(itb, idm_hex_to_ascii[(n >> 4) & 0xf]);
9117978SPeter.Dunlap@Sun.COM 		textbuf_append_char(itb, idm_hex_to_ascii[n & 0xf]);
9127978SPeter.Dunlap@Sun.COM 	}
9137978SPeter.Dunlap@Sun.COM 
9147978SPeter.Dunlap@Sun.COM 	/* Add trailing 0x00 */
9157978SPeter.Dunlap@Sun.COM 	textbuf_terminate_kvpair(itb);
9167978SPeter.Dunlap@Sun.COM 
9177978SPeter.Dunlap@Sun.COM 	return (0);
9187978SPeter.Dunlap@Sun.COM }
9197978SPeter.Dunlap@Sun.COM 
9207978SPeter.Dunlap@Sun.COM /* ARGSUSED */
9217978SPeter.Dunlap@Sun.COM static int
idm_itextbuf_add_large_numerical(nvpair_t * nvp,const idm_kv_xlate_t * ikvx,idm_textbuf_t * itb)9227978SPeter.Dunlap@Sun.COM idm_itextbuf_add_large_numerical(nvpair_t *nvp,
9237978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
9247978SPeter.Dunlap@Sun.COM {
9257978SPeter.Dunlap@Sun.COM 	ASSERT(0);
9267978SPeter.Dunlap@Sun.COM 	return (0);
9277978SPeter.Dunlap@Sun.COM }
9287978SPeter.Dunlap@Sun.COM 
9297978SPeter.Dunlap@Sun.COM /* ARGSUSED */
9307978SPeter.Dunlap@Sun.COM static int
idm_itextbuf_add_numerical(nvpair_t * nvp,const idm_kv_xlate_t * ikvx,idm_textbuf_t * itb)9317978SPeter.Dunlap@Sun.COM idm_itextbuf_add_numerical(nvpair_t *nvp,
9327978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
9337978SPeter.Dunlap@Sun.COM {
9347978SPeter.Dunlap@Sun.COM 	char		*key_name;
9357978SPeter.Dunlap@Sun.COM 	uint64_t	value;
9367978SPeter.Dunlap@Sun.COM 	int	rc;
9377978SPeter.Dunlap@Sun.COM 	char		str[16];
9387978SPeter.Dunlap@Sun.COM 
9397978SPeter.Dunlap@Sun.COM 	/* Start with the key name */
9407978SPeter.Dunlap@Sun.COM 	key_name = nvpair_name(nvp);
9417978SPeter.Dunlap@Sun.COM 	textbuf_strcpy(itb, key_name);
9427978SPeter.Dunlap@Sun.COM 
9437978SPeter.Dunlap@Sun.COM 	/* Add separator */
9447978SPeter.Dunlap@Sun.COM 	textbuf_append_char(itb, '=');
9457978SPeter.Dunlap@Sun.COM 
9467978SPeter.Dunlap@Sun.COM 	/* Add value */
9477978SPeter.Dunlap@Sun.COM 	rc = nvpair_value_uint64(nvp, &value);
9487978SPeter.Dunlap@Sun.COM 	ASSERT(rc == 0);
9497978SPeter.Dunlap@Sun.COM 	(void) sprintf(str, "%llu", (u_longlong_t)value);
9507978SPeter.Dunlap@Sun.COM 	textbuf_strcpy(itb, str);
9517978SPeter.Dunlap@Sun.COM 
9527978SPeter.Dunlap@Sun.COM 	/* Add trailing 0x00 */
9537978SPeter.Dunlap@Sun.COM 	textbuf_terminate_kvpair(itb);
9547978SPeter.Dunlap@Sun.COM 
9557978SPeter.Dunlap@Sun.COM 	return (0);
9567978SPeter.Dunlap@Sun.COM }
9577978SPeter.Dunlap@Sun.COM 
9587978SPeter.Dunlap@Sun.COM /* ARGSUSED */
9597978SPeter.Dunlap@Sun.COM static int
idm_itextbuf_add_numeric_range(nvpair_t * nvp,const idm_kv_xlate_t * ikvx,idm_textbuf_t * itb)9607978SPeter.Dunlap@Sun.COM idm_itextbuf_add_numeric_range(nvpair_t *nvp,
9617978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
9627978SPeter.Dunlap@Sun.COM {
9637978SPeter.Dunlap@Sun.COM 	ASSERT(0);
9647978SPeter.Dunlap@Sun.COM 	return (0);
9657978SPeter.Dunlap@Sun.COM }
9667978SPeter.Dunlap@Sun.COM 
9677978SPeter.Dunlap@Sun.COM /* ARGSUSED */
9687978SPeter.Dunlap@Sun.COM static int
idm_itextbuf_add_list_of_values(nvpair_t * nvp,const idm_kv_xlate_t * ikvx,idm_textbuf_t * itb)9697978SPeter.Dunlap@Sun.COM idm_itextbuf_add_list_of_values(nvpair_t *nvp,
9707978SPeter.Dunlap@Sun.COM     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
9717978SPeter.Dunlap@Sun.COM {
9727978SPeter.Dunlap@Sun.COM 	char		*key_name;
9737978SPeter.Dunlap@Sun.COM 	nvpair_t	*vchoice = NULL;
9747978SPeter.Dunlap@Sun.COM 	char		*vchoice_string = NULL;
9757978SPeter.Dunlap@Sun.COM 	int		rc;
9767978SPeter.Dunlap@Sun.COM 
9777978SPeter.Dunlap@Sun.COM 	/* Start with the key name */
9787978SPeter.Dunlap@Sun.COM 	key_name = nvpair_name(nvp);
9797978SPeter.Dunlap@Sun.COM 	textbuf_strcpy(itb, key_name);
9807978SPeter.Dunlap@Sun.COM 
9817978SPeter.Dunlap@Sun.COM 	/* Add separator */
9827978SPeter.Dunlap@Sun.COM 	textbuf_append_char(itb, '=');
9837978SPeter.Dunlap@Sun.COM 
9847978SPeter.Dunlap@Sun.COM 	/* Add value choices */
9857978SPeter.Dunlap@Sun.COM 	vchoice = idm_get_next_listvalue(nvp, NULL);
9867978SPeter.Dunlap@Sun.COM 	while (vchoice != NULL) {
9877978SPeter.Dunlap@Sun.COM 		rc = nvpair_value_string(vchoice, &vchoice_string);
9887978SPeter.Dunlap@Sun.COM 		ASSERT(rc == 0);
9897978SPeter.Dunlap@Sun.COM 		textbuf_strcpy(itb, vchoice_string);
9907978SPeter.Dunlap@Sun.COM 		vchoice = idm_get_next_listvalue(nvp, vchoice);
9917978SPeter.Dunlap@Sun.COM 		if (vchoice != NULL) {
9927978SPeter.Dunlap@Sun.COM 			/* Add ',' between choices */
9937978SPeter.Dunlap@Sun.COM 			textbuf_append_char(itb, ',');
9947978SPeter.Dunlap@Sun.COM 		}
9957978SPeter.Dunlap@Sun.COM 	}
9967978SPeter.Dunlap@Sun.COM 
9977978SPeter.Dunlap@Sun.COM 	/* Add trailing 0x00 */
9987978SPeter.Dunlap@Sun.COM 	textbuf_terminate_kvpair(itb);
9997978SPeter.Dunlap@Sun.COM 
10007978SPeter.Dunlap@Sun.COM 	return (0);
10017978SPeter.Dunlap@Sun.COM }
10027978SPeter.Dunlap@Sun.COM 
10037978SPeter.Dunlap@Sun.COM 
10047978SPeter.Dunlap@Sun.COM static void
textbuf_makeroom(idm_textbuf_t * itb,int size)10057978SPeter.Dunlap@Sun.COM textbuf_makeroom(idm_textbuf_t *itb, int size)
10067978SPeter.Dunlap@Sun.COM {
10077978SPeter.Dunlap@Sun.COM 	char	*new_mem;
10087978SPeter.Dunlap@Sun.COM 	int	new_mem_len;
10097978SPeter.Dunlap@Sun.COM 
10107978SPeter.Dunlap@Sun.COM 	if (itb->itb_mem == NULL) {
10117978SPeter.Dunlap@Sun.COM 		itb->itb_mem_len = MAX(TEXTBUF_CHUNKSIZE, size);
10127978SPeter.Dunlap@Sun.COM 		itb->itb_mem = kmem_alloc(itb->itb_mem_len, KM_SLEEP);
10137978SPeter.Dunlap@Sun.COM 	} else if ((itb->itb_offset + size) > itb->itb_mem_len) {
10147978SPeter.Dunlap@Sun.COM 		new_mem_len = itb->itb_mem_len + MAX(TEXTBUF_CHUNKSIZE, size);
10157978SPeter.Dunlap@Sun.COM 		new_mem = kmem_alloc(new_mem_len, KM_SLEEP);
10167978SPeter.Dunlap@Sun.COM 		bcopy(itb->itb_mem, new_mem, itb->itb_mem_len);
10177978SPeter.Dunlap@Sun.COM 		kmem_free(itb->itb_mem, itb->itb_mem_len);
10187978SPeter.Dunlap@Sun.COM 		itb->itb_mem = new_mem;
10197978SPeter.Dunlap@Sun.COM 		itb->itb_mem_len = new_mem_len;
10207978SPeter.Dunlap@Sun.COM 	}
10217978SPeter.Dunlap@Sun.COM }
10227978SPeter.Dunlap@Sun.COM 
10237978SPeter.Dunlap@Sun.COM static void
textbuf_memcpy(idm_textbuf_t * itb,void * mem,int mem_len)10247978SPeter.Dunlap@Sun.COM textbuf_memcpy(idm_textbuf_t *itb, void *mem, int mem_len)
10257978SPeter.Dunlap@Sun.COM {
10267978SPeter.Dunlap@Sun.COM 	textbuf_makeroom(itb, mem_len);
10277978SPeter.Dunlap@Sun.COM 	(void) memcpy(itb->itb_mem + itb->itb_offset, mem, mem_len);
10287978SPeter.Dunlap@Sun.COM 	itb->itb_offset += mem_len;
10297978SPeter.Dunlap@Sun.COM }
10307978SPeter.Dunlap@Sun.COM 
10317978SPeter.Dunlap@Sun.COM static void
textbuf_strcpy(idm_textbuf_t * itb,char * str)10327978SPeter.Dunlap@Sun.COM textbuf_strcpy(idm_textbuf_t *itb, char *str)
10337978SPeter.Dunlap@Sun.COM {
10347978SPeter.Dunlap@Sun.COM 	textbuf_memcpy(itb, str, strlen(str));
10357978SPeter.Dunlap@Sun.COM }
10367978SPeter.Dunlap@Sun.COM 
10377978SPeter.Dunlap@Sun.COM static void
textbuf_append_char(idm_textbuf_t * itb,char c)10387978SPeter.Dunlap@Sun.COM textbuf_append_char(idm_textbuf_t *itb, char c)
10397978SPeter.Dunlap@Sun.COM {
10407978SPeter.Dunlap@Sun.COM 	textbuf_makeroom(itb, sizeof (char));
10417978SPeter.Dunlap@Sun.COM 	*(itb->itb_mem + itb->itb_offset) = c;
10427978SPeter.Dunlap@Sun.COM 	itb->itb_offset++;
10437978SPeter.Dunlap@Sun.COM }
10447978SPeter.Dunlap@Sun.COM 
10457978SPeter.Dunlap@Sun.COM static void
textbuf_terminate_kvpair(idm_textbuf_t * itb)10467978SPeter.Dunlap@Sun.COM textbuf_terminate_kvpair(idm_textbuf_t *itb)
10477978SPeter.Dunlap@Sun.COM {
10487978SPeter.Dunlap@Sun.COM 	textbuf_append_char(itb, '\0');
10497978SPeter.Dunlap@Sun.COM }
10507978SPeter.Dunlap@Sun.COM 
10517978SPeter.Dunlap@Sun.COM static int
idm_ascii_to_hex(char * enc_hex_byte,uint8_t * bin_val)10527978SPeter.Dunlap@Sun.COM idm_ascii_to_hex(char *enc_hex_byte, uint8_t *bin_val)
10537978SPeter.Dunlap@Sun.COM {
10547978SPeter.Dunlap@Sun.COM 	uint8_t nibble1, nibble2;
10557978SPeter.Dunlap@Sun.COM 	char enc_char = *enc_hex_byte;
10567978SPeter.Dunlap@Sun.COM 
10577978SPeter.Dunlap@Sun.COM 	if (enc_char >= '0' && enc_char <= '9') {
10587978SPeter.Dunlap@Sun.COM 		nibble1 = (enc_char - '0');
10597978SPeter.Dunlap@Sun.COM 	} else if (enc_char >= 'A' && enc_char <= 'F') {
10607978SPeter.Dunlap@Sun.COM 		nibble1 = (0xA + (enc_char - 'A'));
10617978SPeter.Dunlap@Sun.COM 	} else if (enc_char >= 'a' && enc_char <= 'f') {
10627978SPeter.Dunlap@Sun.COM 		nibble1 = (0xA + (enc_char - 'a'));
10637978SPeter.Dunlap@Sun.COM 	} else {
10647978SPeter.Dunlap@Sun.COM 		return (EINVAL);
10657978SPeter.Dunlap@Sun.COM 	}
10667978SPeter.Dunlap@Sun.COM 
10677978SPeter.Dunlap@Sun.COM 	enc_hex_byte++;
10687978SPeter.Dunlap@Sun.COM 	enc_char = *enc_hex_byte;
10697978SPeter.Dunlap@Sun.COM 
10707978SPeter.Dunlap@Sun.COM 	if (enc_char >= '0' && enc_char <= '9') {
10717978SPeter.Dunlap@Sun.COM 		nibble2 = (enc_char - '0');
10727978SPeter.Dunlap@Sun.COM 	} else if (enc_char >= 'A' && enc_char <= 'F') {
10737978SPeter.Dunlap@Sun.COM 		nibble2 = (0xA + (enc_char - 'A'));
10747978SPeter.Dunlap@Sun.COM 	} else if (enc_char >= 'a' && enc_char <= 'f') {
10757978SPeter.Dunlap@Sun.COM 		nibble2 = (0xA + (enc_char - 'a'));
10767978SPeter.Dunlap@Sun.COM 	} else {
10777978SPeter.Dunlap@Sun.COM 		return (EINVAL);
10787978SPeter.Dunlap@Sun.COM 	}
10797978SPeter.Dunlap@Sun.COM 
10807978SPeter.Dunlap@Sun.COM 	*bin_val = (nibble1 << 4) | nibble2;
10817978SPeter.Dunlap@Sun.COM 
10827978SPeter.Dunlap@Sun.COM 	return (0);
10837978SPeter.Dunlap@Sun.COM }
10847978SPeter.Dunlap@Sun.COM 
10857978SPeter.Dunlap@Sun.COM 
idm_base16_str_to_binary(char * hstr,int hstr_len,uint8_t * binary_array,int binary_length)10867978SPeter.Dunlap@Sun.COM static int idm_base16_str_to_binary(char *hstr, int hstr_len,
10877978SPeter.Dunlap@Sun.COM     uint8_t *binary_array, int binary_length)
10887978SPeter.Dunlap@Sun.COM {
10897978SPeter.Dunlap@Sun.COM 	char	tmpstr[2];
10907978SPeter.Dunlap@Sun.COM 	uchar_t *binary_scan;
10917978SPeter.Dunlap@Sun.COM 
10927978SPeter.Dunlap@Sun.COM 	binary_scan = binary_array;
10937978SPeter.Dunlap@Sun.COM 
10947978SPeter.Dunlap@Sun.COM 	/*
10957978SPeter.Dunlap@Sun.COM 	 * If the length of the encoded ascii hex value is a multiple
10967978SPeter.Dunlap@Sun.COM 	 * of two then every two ascii characters correspond to a hex
10977978SPeter.Dunlap@Sun.COM 	 * byte.  If the length of the value is not a multiple of two
10987978SPeter.Dunlap@Sun.COM 	 * then the first character is the first hex byte and then for
10997978SPeter.Dunlap@Sun.COM 	 * the remaining of the string every two ascii characters
11007978SPeter.Dunlap@Sun.COM 	 * correspond to a hex byte
11017978SPeter.Dunlap@Sun.COM 	 */
11027978SPeter.Dunlap@Sun.COM 	if ((hstr_len % 2) != 0) {
11037978SPeter.Dunlap@Sun.COM 
11047978SPeter.Dunlap@Sun.COM 		tmpstr[0] = '0';
11057978SPeter.Dunlap@Sun.COM 		tmpstr[1] = *hstr;
11067978SPeter.Dunlap@Sun.COM 
11077978SPeter.Dunlap@Sun.COM 		if (idm_ascii_to_hex(tmpstr, binary_scan) != 0) {
11087978SPeter.Dunlap@Sun.COM 			return (EINVAL);
11097978SPeter.Dunlap@Sun.COM 		}
11107978SPeter.Dunlap@Sun.COM 
11117978SPeter.Dunlap@Sun.COM 		hstr++;
11127978SPeter.Dunlap@Sun.COM 		binary_scan++;
11137978SPeter.Dunlap@Sun.COM 	}
11147978SPeter.Dunlap@Sun.COM 
11157978SPeter.Dunlap@Sun.COM 	while (binary_scan != binary_array + binary_length) {
11167978SPeter.Dunlap@Sun.COM 		if (idm_ascii_to_hex(hstr, binary_scan) != 0) {
11177978SPeter.Dunlap@Sun.COM 			return (EINVAL);
11187978SPeter.Dunlap@Sun.COM 		}
11197978SPeter.Dunlap@Sun.COM 
11207978SPeter.Dunlap@Sun.COM 		hstr += 2;
11217978SPeter.Dunlap@Sun.COM 		binary_scan++;
11227978SPeter.Dunlap@Sun.COM 	}
11237978SPeter.Dunlap@Sun.COM 
11247978SPeter.Dunlap@Sun.COM 	return (0);
11257978SPeter.Dunlap@Sun.COM }
11267978SPeter.Dunlap@Sun.COM 
11277978SPeter.Dunlap@Sun.COM static size_t
idm_strnlen(const char * str,size_t maxlen)11287978SPeter.Dunlap@Sun.COM idm_strnlen(const char *str, size_t maxlen)
11297978SPeter.Dunlap@Sun.COM {
11307978SPeter.Dunlap@Sun.COM 	const char *ptr;
11317978SPeter.Dunlap@Sun.COM 
11327978SPeter.Dunlap@Sun.COM 	ptr = memchr(str, 0, maxlen);
11337978SPeter.Dunlap@Sun.COM 	if (ptr == NULL)
11347978SPeter.Dunlap@Sun.COM 		return (maxlen);
11357978SPeter.Dunlap@Sun.COM 
11367978SPeter.Dunlap@Sun.COM 	return ((uintptr_t)ptr - (uintptr_t)str);
11377978SPeter.Dunlap@Sun.COM }
11387978SPeter.Dunlap@Sun.COM 
11397978SPeter.Dunlap@Sun.COM 
11407978SPeter.Dunlap@Sun.COM size_t
idm_strcspn(const char * string,const char * charset)11417978SPeter.Dunlap@Sun.COM idm_strcspn(const char *string, const char *charset)
11427978SPeter.Dunlap@Sun.COM {
11437978SPeter.Dunlap@Sun.COM 	const char *p, *q;
11447978SPeter.Dunlap@Sun.COM 
11457978SPeter.Dunlap@Sun.COM 	for (q = string; *q != '\0'; ++q) {
11467978SPeter.Dunlap@Sun.COM 		for (p = charset; *p != '\0' && *p != *q; )
11477978SPeter.Dunlap@Sun.COM 			p++;
11487978SPeter.Dunlap@Sun.COM 		if (*p != '\0') {
11497978SPeter.Dunlap@Sun.COM 			break;
11507978SPeter.Dunlap@Sun.COM 		}
11517978SPeter.Dunlap@Sun.COM 	}
11527978SPeter.Dunlap@Sun.COM 	return ((uintptr_t)q - (uintptr_t)string);
11537978SPeter.Dunlap@Sun.COM }
11547978SPeter.Dunlap@Sun.COM 
11557978SPeter.Dunlap@Sun.COM /*
11567978SPeter.Dunlap@Sun.COM  * We allow a list of choices to be represented as a single nvpair
11577978SPeter.Dunlap@Sun.COM  * (list with one value choice), or as an nvlist with a single nvpair
11587978SPeter.Dunlap@Sun.COM  * (also a list with on value choice), or as an nvlist with multiple
11597978SPeter.Dunlap@Sun.COM  * nvpairs (a list with multiple value choices).  This function implements
11607978SPeter.Dunlap@Sun.COM  * the "get next" functionality regardless of the choice list structure.
11617978SPeter.Dunlap@Sun.COM  *
11627978SPeter.Dunlap@Sun.COM  * nvpair_t's that contain choices are always strings.
11637978SPeter.Dunlap@Sun.COM  */
11647978SPeter.Dunlap@Sun.COM nvpair_t *
idm_get_next_listvalue(nvpair_t * value_list,nvpair_t * curr_nvp)11657978SPeter.Dunlap@Sun.COM idm_get_next_listvalue(nvpair_t *value_list, nvpair_t *curr_nvp)
11667978SPeter.Dunlap@Sun.COM {
11677978SPeter.Dunlap@Sun.COM 	nvpair_t	*result;
11687978SPeter.Dunlap@Sun.COM 	nvlist_t	*nvl;
11697978SPeter.Dunlap@Sun.COM 	int		nvrc;
11707978SPeter.Dunlap@Sun.COM 	data_type_t	nvp_type;
11717978SPeter.Dunlap@Sun.COM 
11727978SPeter.Dunlap@Sun.COM 	nvp_type = nvpair_type(value_list);
11737978SPeter.Dunlap@Sun.COM 
11747978SPeter.Dunlap@Sun.COM 	switch (nvp_type) {
11757978SPeter.Dunlap@Sun.COM 	case DATA_TYPE_NVLIST:
11767978SPeter.Dunlap@Sun.COM 		nvrc = nvpair_value_nvlist(value_list, &nvl);
11777978SPeter.Dunlap@Sun.COM 		ASSERT(nvrc == 0);
11787978SPeter.Dunlap@Sun.COM 		result = nvlist_next_nvpair(nvl, curr_nvp);
11797978SPeter.Dunlap@Sun.COM 		break;
11807978SPeter.Dunlap@Sun.COM 	case DATA_TYPE_STRING:
11817978SPeter.Dunlap@Sun.COM 		/* Single choice */
11827978SPeter.Dunlap@Sun.COM 		if (curr_nvp == NULL) {
11837978SPeter.Dunlap@Sun.COM 			result = value_list;
11847978SPeter.Dunlap@Sun.COM 		} else {
11857978SPeter.Dunlap@Sun.COM 			result = NULL;
11867978SPeter.Dunlap@Sun.COM 		}
11877978SPeter.Dunlap@Sun.COM 		break;
11887978SPeter.Dunlap@Sun.COM 	default:
11897978SPeter.Dunlap@Sun.COM 		ASSERT(0); /* Malformed choice list */
11907978SPeter.Dunlap@Sun.COM 		result = NULL;
11917978SPeter.Dunlap@Sun.COM 		break;
11927978SPeter.Dunlap@Sun.COM 	}
11937978SPeter.Dunlap@Sun.COM 
11947978SPeter.Dunlap@Sun.COM 	return (result);
11957978SPeter.Dunlap@Sun.COM }
11967978SPeter.Dunlap@Sun.COM 
11977978SPeter.Dunlap@Sun.COM kv_status_t
idm_nvstat_to_kvstat(int nvrc)11987978SPeter.Dunlap@Sun.COM idm_nvstat_to_kvstat(int nvrc)
11997978SPeter.Dunlap@Sun.COM {
12007978SPeter.Dunlap@Sun.COM 	kv_status_t result;
12017978SPeter.Dunlap@Sun.COM 	switch (nvrc) {
12027978SPeter.Dunlap@Sun.COM 	case 0:
12037978SPeter.Dunlap@Sun.COM 		result = KV_HANDLED;
12047978SPeter.Dunlap@Sun.COM 		break;
12057978SPeter.Dunlap@Sun.COM 	case ENOMEM:
12067978SPeter.Dunlap@Sun.COM 		result = KV_NO_RESOURCES;
12077978SPeter.Dunlap@Sun.COM 		break;
12087978SPeter.Dunlap@Sun.COM 	case EINVAL:
12097978SPeter.Dunlap@Sun.COM 		result = KV_VALUE_ERROR;
12107978SPeter.Dunlap@Sun.COM 		break;
12117978SPeter.Dunlap@Sun.COM 	case EFAULT:
12127978SPeter.Dunlap@Sun.COM 	case ENOTSUP:
12137978SPeter.Dunlap@Sun.COM 	default:
12147978SPeter.Dunlap@Sun.COM 		result = KV_INTERNAL_ERROR;
12157978SPeter.Dunlap@Sun.COM 		break;
12167978SPeter.Dunlap@Sun.COM 	}
12177978SPeter.Dunlap@Sun.COM 
12187978SPeter.Dunlap@Sun.COM 	return (result);
12197978SPeter.Dunlap@Sun.COM }
12207978SPeter.Dunlap@Sun.COM 
12217978SPeter.Dunlap@Sun.COM void
idm_kvstat_to_error(kv_status_t kvrc,uint8_t * class,uint8_t * detail)12227978SPeter.Dunlap@Sun.COM idm_kvstat_to_error(kv_status_t kvrc, uint8_t *class, uint8_t *detail)
12237978SPeter.Dunlap@Sun.COM {
12247978SPeter.Dunlap@Sun.COM 	switch (kvrc) {
12257978SPeter.Dunlap@Sun.COM 	case KV_HANDLED:
12267978SPeter.Dunlap@Sun.COM 	case KV_HANDLED_NO_TRANSIT:
12277978SPeter.Dunlap@Sun.COM 		*class = ISCSI_STATUS_CLASS_SUCCESS;
12287978SPeter.Dunlap@Sun.COM 		*detail = ISCSI_LOGIN_STATUS_ACCEPT;
12297978SPeter.Dunlap@Sun.COM 		break;
12307978SPeter.Dunlap@Sun.COM 	case KV_UNHANDLED:
12317978SPeter.Dunlap@Sun.COM 	case KV_TARGET_ONLY:
12327978SPeter.Dunlap@Sun.COM 		/* protocol error */
12337978SPeter.Dunlap@Sun.COM 		*class = ISCSI_STATUS_CLASS_INITIATOR_ERR;
12347978SPeter.Dunlap@Sun.COM 		*detail = ISCSI_LOGIN_STATUS_INVALID_REQUEST;
12357978SPeter.Dunlap@Sun.COM 		break;
12367978SPeter.Dunlap@Sun.COM 	case KV_VALUE_ERROR:
12377978SPeter.Dunlap@Sun.COM 		/* invalid value */
12387978SPeter.Dunlap@Sun.COM 		*class = ISCSI_STATUS_CLASS_INITIATOR_ERR;
12397978SPeter.Dunlap@Sun.COM 		*detail = ISCSI_LOGIN_STATUS_INIT_ERR;
12407978SPeter.Dunlap@Sun.COM 		break;
12417978SPeter.Dunlap@Sun.COM 	case KV_NO_RESOURCES:
12427978SPeter.Dunlap@Sun.COM 		/* no memory */
12437978SPeter.Dunlap@Sun.COM 		*class = ISCSI_STATUS_CLASS_TARGET_ERR;
12447978SPeter.Dunlap@Sun.COM 		*detail = ISCSI_LOGIN_STATUS_NO_RESOURCES;
12457978SPeter.Dunlap@Sun.COM 		break;
12467978SPeter.Dunlap@Sun.COM 	case KV_MISSING_FIELDS:
12477978SPeter.Dunlap@Sun.COM 		/* key/value pair(s) missing */
12487978SPeter.Dunlap@Sun.COM 		*class = ISCSI_STATUS_CLASS_INITIATOR_ERR;
12497978SPeter.Dunlap@Sun.COM 		*detail = ISCSI_LOGIN_STATUS_MISSING_FIELDS;
12507978SPeter.Dunlap@Sun.COM 		break;
12517978SPeter.Dunlap@Sun.COM 	case KV_AUTH_FAILED:
12527978SPeter.Dunlap@Sun.COM 		/* authentication failed */
12537978SPeter.Dunlap@Sun.COM 		*class = ISCSI_STATUS_CLASS_INITIATOR_ERR;
12547978SPeter.Dunlap@Sun.COM 		*detail = ISCSI_LOGIN_STATUS_AUTH_FAILED;
12557978SPeter.Dunlap@Sun.COM 		break;
12567978SPeter.Dunlap@Sun.COM 	default:
12577978SPeter.Dunlap@Sun.COM 		/* target error */
12587978SPeter.Dunlap@Sun.COM 		*class = ISCSI_STATUS_CLASS_TARGET_ERR;
12597978SPeter.Dunlap@Sun.COM 		*detail = ISCSI_LOGIN_STATUS_TARGET_ERROR;
12607978SPeter.Dunlap@Sun.COM 		break;
12617978SPeter.Dunlap@Sun.COM 	}
12627978SPeter.Dunlap@Sun.COM }
12637978SPeter.Dunlap@Sun.COM 
12647978SPeter.Dunlap@Sun.COM int
idm_nvlist_add_keyvalue(nvlist_t * nvl,char * key,int keylen,char * value)12657978SPeter.Dunlap@Sun.COM idm_nvlist_add_keyvalue(nvlist_t *nvl,
12667978SPeter.Dunlap@Sun.COM     char *key, int keylen, char *value)
12677978SPeter.Dunlap@Sun.COM {
12687978SPeter.Dunlap@Sun.COM 	const idm_kv_xlate_t *ikvx;
12697978SPeter.Dunlap@Sun.COM 
12707978SPeter.Dunlap@Sun.COM 	ikvx = idm_lookup_kv_xlate(key, keylen);
12717978SPeter.Dunlap@Sun.COM 
12727978SPeter.Dunlap@Sun.COM 	if (ikvx->ik_key_id == KI_MAX_KEY) {
12738435SJames.Moore@Sun.COM 		char *nkey;
12748435SJames.Moore@Sun.COM 		int rc;
12758435SJames.Moore@Sun.COM 		size_t len;
12768435SJames.Moore@Sun.COM 
12778435SJames.Moore@Sun.COM 		/*
12788435SJames.Moore@Sun.COM 		 * key is not a NULL terminated string, so create one
12798435SJames.Moore@Sun.COM 		 */
12808435SJames.Moore@Sun.COM 		len = (size_t)(keylen+1);
12818435SJames.Moore@Sun.COM 		nkey = kmem_zalloc(len, KM_SLEEP);
12828435SJames.Moore@Sun.COM 		(void) strncpy(nkey, key, len-1);
12838435SJames.Moore@Sun.COM 		rc = nvlist_add_string(nvl, nkey, value);
12848435SJames.Moore@Sun.COM 		kmem_free(nkey, len);
12858435SJames.Moore@Sun.COM 		return (rc);
12867978SPeter.Dunlap@Sun.COM 	}
12877978SPeter.Dunlap@Sun.COM 
12887978SPeter.Dunlap@Sun.COM 	return (idm_nvlist_add_kv(nvl, ikvx, value));
12897978SPeter.Dunlap@Sun.COM }
12907978SPeter.Dunlap@Sun.COM 
12917978SPeter.Dunlap@Sun.COM int
idm_nvlist_add_id(nvlist_t * nvl,iscsikey_id_t kv_id,char * value)12927978SPeter.Dunlap@Sun.COM idm_nvlist_add_id(nvlist_t *nvl, iscsikey_id_t kv_id, char *value)
12937978SPeter.Dunlap@Sun.COM {
12947978SPeter.Dunlap@Sun.COM 	int i;
12957978SPeter.Dunlap@Sun.COM 	for (i = 0; i < KI_MAX_KEY; i++) {
12967978SPeter.Dunlap@Sun.COM 		if (idm_kvpair_xlate[i].ik_key_id == kv_id) {
12977978SPeter.Dunlap@Sun.COM 			return
12987978SPeter.Dunlap@Sun.COM 			    (idm_nvlist_add_kv(nvl,
12997978SPeter.Dunlap@Sun.COM 			    &idm_kvpair_xlate[i], value));
13007978SPeter.Dunlap@Sun.COM 		}
13017978SPeter.Dunlap@Sun.COM 	}
13027978SPeter.Dunlap@Sun.COM 	return (EFAULT);
13037978SPeter.Dunlap@Sun.COM }
13047978SPeter.Dunlap@Sun.COM 
13057978SPeter.Dunlap@Sun.COM char *
idm_id_to_name(iscsikey_id_t kv_id)13067978SPeter.Dunlap@Sun.COM idm_id_to_name(iscsikey_id_t kv_id)
13077978SPeter.Dunlap@Sun.COM {
13087978SPeter.Dunlap@Sun.COM 	int i;
13097978SPeter.Dunlap@Sun.COM 	for (i = 0; i < KI_MAX_KEY; i++) {
13107978SPeter.Dunlap@Sun.COM 		if (idm_kvpair_xlate[i].ik_key_id == kv_id) {
13117978SPeter.Dunlap@Sun.COM 			return (idm_kvpair_xlate[i].ik_key_name);
13127978SPeter.Dunlap@Sun.COM 		}
13137978SPeter.Dunlap@Sun.COM 	}
13147978SPeter.Dunlap@Sun.COM 	return (NULL);
13157978SPeter.Dunlap@Sun.COM }
13167978SPeter.Dunlap@Sun.COM 
13177978SPeter.Dunlap@Sun.COM /*
13187978SPeter.Dunlap@Sun.COM  * return the value in a buffer that must be freed by the caller
13197978SPeter.Dunlap@Sun.COM  */
13207978SPeter.Dunlap@Sun.COM char *
idm_nvpair_value_to_textbuf(nvpair_t * nvp)13217978SPeter.Dunlap@Sun.COM idm_nvpair_value_to_textbuf(nvpair_t *nvp)
13227978SPeter.Dunlap@Sun.COM {
13237978SPeter.Dunlap@Sun.COM 	int rv, len;
13247978SPeter.Dunlap@Sun.COM 	idm_textbuf_t itb;
13257978SPeter.Dunlap@Sun.COM 	char *str;
13267978SPeter.Dunlap@Sun.COM 
13277978SPeter.Dunlap@Sun.COM 	bzero(&itb, sizeof (itb));
13287978SPeter.Dunlap@Sun.COM 	rv = idm_itextbuf_add_nvpair(nvp, &itb);
13297978SPeter.Dunlap@Sun.COM 	if (rv != 0)
13307978SPeter.Dunlap@Sun.COM 		return (NULL);
13317978SPeter.Dunlap@Sun.COM 	str = kmem_alloc(itb.itb_mem_len, KM_SLEEP);
13327978SPeter.Dunlap@Sun.COM 	len = idm_strcspn(itb.itb_mem, "=");
13337978SPeter.Dunlap@Sun.COM 	if (len > strlen(itb.itb_mem)) {
13347978SPeter.Dunlap@Sun.COM 		kmem_free(itb.itb_mem, itb.itb_mem_len);
13357978SPeter.Dunlap@Sun.COM 		return (NULL);
13367978SPeter.Dunlap@Sun.COM 	}
13377978SPeter.Dunlap@Sun.COM 	(void) strcpy(str, &itb.itb_mem[len+1]);
13387978SPeter.Dunlap@Sun.COM 	/* free the allocation done in idm_textbuf_add_nvpair */
13397978SPeter.Dunlap@Sun.COM 	kmem_free(itb.itb_mem, itb.itb_mem_len);
13407978SPeter.Dunlap@Sun.COM 	return (str);
13417978SPeter.Dunlap@Sun.COM }
13427978SPeter.Dunlap@Sun.COM 
13437978SPeter.Dunlap@Sun.COM /*
13447978SPeter.Dunlap@Sun.COM  * build an iscsi text buffer - the memory gets freed in
13457978SPeter.Dunlap@Sun.COM  * idm_itextbuf_free
13467978SPeter.Dunlap@Sun.COM  */
13477978SPeter.Dunlap@Sun.COM void *
idm_nvlist_to_itextbuf(nvlist_t * nvl)13487978SPeter.Dunlap@Sun.COM idm_nvlist_to_itextbuf(nvlist_t *nvl)
13497978SPeter.Dunlap@Sun.COM {
13507978SPeter.Dunlap@Sun.COM 	idm_textbuf_t *itb;
13517978SPeter.Dunlap@Sun.COM 	char		*textbuf;
13527978SPeter.Dunlap@Sun.COM 	int		validlen, textbuflen;
13537978SPeter.Dunlap@Sun.COM 
13547978SPeter.Dunlap@Sun.COM 	if (idm_nvlist_to_textbuf(nvl, &textbuf, &textbuflen,
13557978SPeter.Dunlap@Sun.COM 	    &validlen) != IDM_STATUS_SUCCESS) {
13567978SPeter.Dunlap@Sun.COM 		return (NULL);
13577978SPeter.Dunlap@Sun.COM 	}
13587978SPeter.Dunlap@Sun.COM 	itb = kmem_zalloc(sizeof (idm_textbuf_t), KM_SLEEP);
13597978SPeter.Dunlap@Sun.COM 	ASSERT(itb != NULL);
13607978SPeter.Dunlap@Sun.COM 	itb->itb_mem = textbuf;
13617978SPeter.Dunlap@Sun.COM 	itb->itb_mem_len = textbuflen;
13627978SPeter.Dunlap@Sun.COM 	itb->itb_offset = validlen;
13637978SPeter.Dunlap@Sun.COM 	return ((void *)itb);
13647978SPeter.Dunlap@Sun.COM }
13657978SPeter.Dunlap@Sun.COM 
13667978SPeter.Dunlap@Sun.COM /*
1367*9601SJames.Moore@Sun.COM  * Copy as much of the text buffer as will fit in the pdu.
13687978SPeter.Dunlap@Sun.COM  * The first call to this routine should send
13697978SPeter.Dunlap@Sun.COM  * a NULL bufptr. Subsequent calls send in the buffer returned.
13707978SPeter.Dunlap@Sun.COM  * Call this routine until the string returned is NULL
13717978SPeter.Dunlap@Sun.COM  */
13727978SPeter.Dunlap@Sun.COM char *
idm_pdu_init_text_data(idm_pdu_t * pdu,void * arg,int max_xfer_len,char * bufptr,int * transit)13737978SPeter.Dunlap@Sun.COM idm_pdu_init_text_data(idm_pdu_t *pdu, void *arg,
13747978SPeter.Dunlap@Sun.COM     int max_xfer_len, char *bufptr, int *transit)
13757978SPeter.Dunlap@Sun.COM {
13767978SPeter.Dunlap@Sun.COM 	char		*start_ptr, *end_ptr, *ptr;
13777978SPeter.Dunlap@Sun.COM 	idm_textbuf_t	*itb = arg;
13787978SPeter.Dunlap@Sun.COM 	iscsi_hdr_t	*ihp = pdu->isp_hdr;
13797978SPeter.Dunlap@Sun.COM 	int		send = 0;
13807978SPeter.Dunlap@Sun.COM 
13817978SPeter.Dunlap@Sun.COM 	ASSERT(itb != NULL);
13827978SPeter.Dunlap@Sun.COM 	ASSERT(pdu != NULL);
13837978SPeter.Dunlap@Sun.COM 	ASSERT(transit != NULL);
13847978SPeter.Dunlap@Sun.COM 	if (bufptr == NULL) {
13857978SPeter.Dunlap@Sun.COM 		/* first call - check the length */
13867978SPeter.Dunlap@Sun.COM 		if (itb->itb_offset <= max_xfer_len) {
1387*9601SJames.Moore@Sun.COM 			/*
1388*9601SJames.Moore@Sun.COM 			 * the entire text buffer fits in the pdu
1389*9601SJames.Moore@Sun.COM 			 */
1390*9601SJames.Moore@Sun.COM 			bcopy((uint8_t *)itb->itb_mem, pdu->isp_data,
1391*9601SJames.Moore@Sun.COM 			    (size_t)itb->itb_offset);
1392*9601SJames.Moore@Sun.COM 			pdu->isp_datalen = itb->itb_offset;
13937978SPeter.Dunlap@Sun.COM 			ihp->flags &= ~ISCSI_FLAG_TEXT_CONTINUE;
13947978SPeter.Dunlap@Sun.COM 			*transit = 1;
13957978SPeter.Dunlap@Sun.COM 			return (NULL);
13967978SPeter.Dunlap@Sun.COM 		}
13977978SPeter.Dunlap@Sun.COM 		/* we have more data than will fit in one pdu */
13987978SPeter.Dunlap@Sun.COM 		start_ptr = itb->itb_mem;
13997978SPeter.Dunlap@Sun.COM 		end_ptr = &itb->itb_mem[max_xfer_len - 1];
14007978SPeter.Dunlap@Sun.COM 
14017978SPeter.Dunlap@Sun.COM 	} else {
1402*9601SJames.Moore@Sun.COM 		uint_t len;
1403*9601SJames.Moore@Sun.COM 
1404*9601SJames.Moore@Sun.COM 		len =  (uintptr_t)&itb->itb_mem[itb->itb_offset] -
1405*9601SJames.Moore@Sun.COM 		    (uintptr_t)bufptr;
1406*9601SJames.Moore@Sun.COM 		if (len <= max_xfer_len) {
1407*9601SJames.Moore@Sun.COM 			/*
1408*9601SJames.Moore@Sun.COM 			 * the remaining text fits in the pdu
1409*9601SJames.Moore@Sun.COM 			 */
1410*9601SJames.Moore@Sun.COM 			bcopy(bufptr, pdu->isp_data, (size_t)len);
1411*9601SJames.Moore@Sun.COM 			pdu->isp_datalen = len;
14127978SPeter.Dunlap@Sun.COM 			ihp->flags &= ~ISCSI_FLAG_TEXT_CONTINUE;
14137978SPeter.Dunlap@Sun.COM 			*transit = 1;
14147978SPeter.Dunlap@Sun.COM 			return (NULL);
14157978SPeter.Dunlap@Sun.COM 		}
1416*9601SJames.Moore@Sun.COM 		/* we still have more data then will fit in one pdu */
14177978SPeter.Dunlap@Sun.COM 		start_ptr = bufptr;
14187978SPeter.Dunlap@Sun.COM 		end_ptr = &bufptr[max_xfer_len - 1];
14197978SPeter.Dunlap@Sun.COM 	}
14207978SPeter.Dunlap@Sun.COM 	/* break after key, after =, after the value or after '\0' */
14217978SPeter.Dunlap@Sun.COM 	ptr = end_ptr;
14227978SPeter.Dunlap@Sun.COM 	if (end_ptr + 1 <= &itb->itb_mem[itb->itb_offset]) {
14237978SPeter.Dunlap@Sun.COM 		/* if next char is an '=' or '\0' send it */
14247978SPeter.Dunlap@Sun.COM 		if (*(end_ptr + 1) == '=' || *(end_ptr + 1) == '\0') {
14257978SPeter.Dunlap@Sun.COM 			send = 1;
14267978SPeter.Dunlap@Sun.COM 		}
14277978SPeter.Dunlap@Sun.COM 	}
14287978SPeter.Dunlap@Sun.COM 	if (!send) {
14297978SPeter.Dunlap@Sun.COM 		while (*ptr != '\0' && *ptr != '=' && ptr != start_ptr) {
14307978SPeter.Dunlap@Sun.COM 			ptr--;
14317978SPeter.Dunlap@Sun.COM 		}
14327978SPeter.Dunlap@Sun.COM 	}
1433*9601SJames.Moore@Sun.COM 	bcopy(start_ptr, pdu->isp_data,
14347978SPeter.Dunlap@Sun.COM 	    ((uintptr_t)ptr - (uintptr_t)start_ptr) + 1);
1435*9601SJames.Moore@Sun.COM 	pdu->isp_datalen = ((uintptr_t)ptr - (uintptr_t)start_ptr) + 1;
14367978SPeter.Dunlap@Sun.COM 	ihp->flags |= ISCSI_FLAG_TEXT_CONTINUE;
14377978SPeter.Dunlap@Sun.COM 	*transit = 0;
14387978SPeter.Dunlap@Sun.COM 	return (++ptr);
14397978SPeter.Dunlap@Sun.COM }
14407978SPeter.Dunlap@Sun.COM 
14417978SPeter.Dunlap@Sun.COM void
idm_itextbuf_free(void * arg)14427978SPeter.Dunlap@Sun.COM idm_itextbuf_free(void *arg)
14437978SPeter.Dunlap@Sun.COM {
14447978SPeter.Dunlap@Sun.COM 	idm_textbuf_t	*itb = arg;
14457978SPeter.Dunlap@Sun.COM 	ASSERT(itb != NULL);
14467978SPeter.Dunlap@Sun.COM 	kmem_free(itb->itb_mem, itb->itb_mem_len);
14477978SPeter.Dunlap@Sun.COM 	kmem_free(itb, sizeof (idm_textbuf_t));
14487978SPeter.Dunlap@Sun.COM }
14497978SPeter.Dunlap@Sun.COM 
14507978SPeter.Dunlap@Sun.COM /*
14517978SPeter.Dunlap@Sun.COM  * Allocate an nvlist and poputlate with key=value from the pdu list.
14527978SPeter.Dunlap@Sun.COM  * NOTE: caller must free the list
14537978SPeter.Dunlap@Sun.COM  */
14547978SPeter.Dunlap@Sun.COM idm_status_t
idm_pdu_list_to_nvlist(list_t * pdu_list,nvlist_t ** nvlist,uint8_t * error_detail)14557978SPeter.Dunlap@Sun.COM idm_pdu_list_to_nvlist(list_t *pdu_list, nvlist_t **nvlist,
14567978SPeter.Dunlap@Sun.COM     uint8_t *error_detail)
14577978SPeter.Dunlap@Sun.COM {
14587978SPeter.Dunlap@Sun.COM 	idm_pdu_t		*pdu, *next_pdu;
14597978SPeter.Dunlap@Sun.COM 	boolean_t		split_kv = B_FALSE;
14607978SPeter.Dunlap@Sun.COM 	char			*textbuf, *leftover_textbuf = NULL;
14617978SPeter.Dunlap@Sun.COM 	int			textbuflen, leftover_textbuflen = 0;
14627978SPeter.Dunlap@Sun.COM 	char			*split_kvbuf;
14637978SPeter.Dunlap@Sun.COM 	int			split_kvbuflen, cont_fraglen;
14647978SPeter.Dunlap@Sun.COM 	iscsi_login_hdr_t	*lh;
14657978SPeter.Dunlap@Sun.COM 	int			rc;
14667978SPeter.Dunlap@Sun.COM 	int			ret = IDM_STATUS_SUCCESS;
14677978SPeter.Dunlap@Sun.COM 
14687978SPeter.Dunlap@Sun.COM 	*error_detail = ISCSI_LOGIN_STATUS_ACCEPT;
14697978SPeter.Dunlap@Sun.COM 	/* Allocate a new nvlist for request key/value pairs */
14707978SPeter.Dunlap@Sun.COM 	rc = nvlist_alloc(nvlist, NV_UNIQUE_NAME,
14717978SPeter.Dunlap@Sun.COM 	    KM_NOSLEEP);
14727978SPeter.Dunlap@Sun.COM 	if (rc != 0) {
14737978SPeter.Dunlap@Sun.COM 		*error_detail = ISCSI_LOGIN_STATUS_NO_RESOURCES;
14747978SPeter.Dunlap@Sun.COM 		ret = IDM_STATUS_FAIL;
14757978SPeter.Dunlap@Sun.COM 		goto cleanup;
14767978SPeter.Dunlap@Sun.COM 	}
14777978SPeter.Dunlap@Sun.COM 
14787978SPeter.Dunlap@Sun.COM 	/*
14797978SPeter.Dunlap@Sun.COM 	 * A login request can be split across multiple PDU's.  The state
14807978SPeter.Dunlap@Sun.COM 	 * machine has collected all the PDU's that make up this login request
14817978SPeter.Dunlap@Sun.COM 	 * and assembled them on the "icl_pdu_list" queue.  Process each PDU
14827978SPeter.Dunlap@Sun.COM 	 * and convert the text keywords to nvlist form.
14837978SPeter.Dunlap@Sun.COM 	 */
14847978SPeter.Dunlap@Sun.COM 	pdu = list_head(pdu_list);
14857978SPeter.Dunlap@Sun.COM 	while (pdu != NULL) {
14867978SPeter.Dunlap@Sun.COM 		next_pdu = list_next(pdu_list, pdu);
14877978SPeter.Dunlap@Sun.COM 
14887978SPeter.Dunlap@Sun.COM 		lh = (iscsi_login_hdr_t *)pdu->isp_hdr;
14897978SPeter.Dunlap@Sun.COM 
14907978SPeter.Dunlap@Sun.COM 		textbuf = (char *)pdu->isp_data;
14917978SPeter.Dunlap@Sun.COM 		textbuflen = pdu->isp_datalen;
14927978SPeter.Dunlap@Sun.COM 		if (textbuflen == 0) {
14937978SPeter.Dunlap@Sun.COM 			/* This shouldn't really happen but it could.. */
14947978SPeter.Dunlap@Sun.COM 			list_remove(pdu_list, pdu);
14957978SPeter.Dunlap@Sun.COM 			idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
14967978SPeter.Dunlap@Sun.COM 			pdu = next_pdu;
14977978SPeter.Dunlap@Sun.COM 			continue;
14987978SPeter.Dunlap@Sun.COM 		}
14997978SPeter.Dunlap@Sun.COM 
15007978SPeter.Dunlap@Sun.COM 		/*
15017978SPeter.Dunlap@Sun.COM 		 * If we encountered a split key-value pair on the last
15027978SPeter.Dunlap@Sun.COM 		 * PDU then handle it now by grabbing the remainder of the
15037978SPeter.Dunlap@Sun.COM 		 * key-value pair from the next PDU and splicing them
15047978SPeter.Dunlap@Sun.COM 		 * together.  Obviously on the first PDU this will never
15057978SPeter.Dunlap@Sun.COM 		 * happen.
15067978SPeter.Dunlap@Sun.COM 		 */
15077978SPeter.Dunlap@Sun.COM 		if (split_kv) {
15087978SPeter.Dunlap@Sun.COM 			cont_fraglen = idm_textbuf_to_firstfraglen(textbuf,
15097978SPeter.Dunlap@Sun.COM 			    textbuflen);
15107978SPeter.Dunlap@Sun.COM 			if (cont_fraglen == pdu->isp_datalen) {
15117978SPeter.Dunlap@Sun.COM 				/*
15127978SPeter.Dunlap@Sun.COM 				 * This key-value pair spans more than two
15137978SPeter.Dunlap@Sun.COM 				 * PDU's.  We don't handle this.
15147978SPeter.Dunlap@Sun.COM 				 */
15157978SPeter.Dunlap@Sun.COM 				*error_detail = ISCSI_LOGIN_STATUS_TARGET_ERROR;
15167978SPeter.Dunlap@Sun.COM 				ret = IDM_STATUS_FAIL;
15177978SPeter.Dunlap@Sun.COM 				goto cleanup;
15187978SPeter.Dunlap@Sun.COM 			}
15197978SPeter.Dunlap@Sun.COM 
15207978SPeter.Dunlap@Sun.COM 			split_kvbuflen = leftover_textbuflen + cont_fraglen;
15217978SPeter.Dunlap@Sun.COM 			split_kvbuf = kmem_alloc(split_kvbuflen, KM_NOSLEEP);
15227978SPeter.Dunlap@Sun.COM 			if (split_kvbuf == NULL) {
15237978SPeter.Dunlap@Sun.COM 				*error_detail = ISCSI_LOGIN_STATUS_NO_RESOURCES;
15247978SPeter.Dunlap@Sun.COM 				ret = IDM_STATUS_FAIL;
15257978SPeter.Dunlap@Sun.COM 				goto cleanup;
15267978SPeter.Dunlap@Sun.COM 			}
15277978SPeter.Dunlap@Sun.COM 
15287978SPeter.Dunlap@Sun.COM 			bcopy(leftover_textbuf, split_kvbuf,
15297978SPeter.Dunlap@Sun.COM 			    leftover_textbuflen);
15307978SPeter.Dunlap@Sun.COM 			bcopy(textbuf,
15317978SPeter.Dunlap@Sun.COM 			    (uint8_t *)split_kvbuf + leftover_textbuflen,
15327978SPeter.Dunlap@Sun.COM 			    cont_fraglen);
15337978SPeter.Dunlap@Sun.COM 
15347978SPeter.Dunlap@Sun.COM 
15357978SPeter.Dunlap@Sun.COM 			if (idm_textbuf_to_nvlist(*nvlist,
15367978SPeter.Dunlap@Sun.COM 			    &split_kvbuf, &split_kvbuflen) != 0) {
15377978SPeter.Dunlap@Sun.COM 				/*
15387978SPeter.Dunlap@Sun.COM 				 * Need to handle E2BIG case, indicating that
15397978SPeter.Dunlap@Sun.COM 				 * a key-value pair is split across multiple
15407978SPeter.Dunlap@Sun.COM 				 * PDU's.
15417978SPeter.Dunlap@Sun.COM 				 */
15427978SPeter.Dunlap@Sun.COM 				kmem_free(split_kvbuf, split_kvbuflen);
15437978SPeter.Dunlap@Sun.COM 
15447978SPeter.Dunlap@Sun.COM 				*error_detail = ISCSI_LOGIN_STATUS_TARGET_ERROR;
15457978SPeter.Dunlap@Sun.COM 				ret = IDM_STATUS_FAIL;
15467978SPeter.Dunlap@Sun.COM 				goto cleanup;
15477978SPeter.Dunlap@Sun.COM 			}
15487978SPeter.Dunlap@Sun.COM 
15497978SPeter.Dunlap@Sun.COM 			ASSERT(split_kvbuflen != NULL);
15507978SPeter.Dunlap@Sun.COM 			kmem_free(split_kvbuf, split_kvbuflen);
15517978SPeter.Dunlap@Sun.COM 
15527978SPeter.Dunlap@Sun.COM 			/* Now handle the remainder of the PDU as normal */
15537978SPeter.Dunlap@Sun.COM 			textbuf += (cont_fraglen + 1);
15547978SPeter.Dunlap@Sun.COM 			textbuflen -= (cont_fraglen + 1);
15557978SPeter.Dunlap@Sun.COM 		}
15567978SPeter.Dunlap@Sun.COM 
15577978SPeter.Dunlap@Sun.COM 		/*
15587978SPeter.Dunlap@Sun.COM 		 * Convert each key-value pair in the text buffer to nvlist
15597978SPeter.Dunlap@Sun.COM 		 * format.  If the list has already been created the nvpair
15607978SPeter.Dunlap@Sun.COM 		 * elements will be added on to the existing list.  Otherwise
15617978SPeter.Dunlap@Sun.COM 		 * a new nvlist will be created.
15627978SPeter.Dunlap@Sun.COM 		 */
15637978SPeter.Dunlap@Sun.COM 		if (idm_textbuf_to_nvlist(*nvlist,
15647978SPeter.Dunlap@Sun.COM 		    &textbuf, &textbuflen) != 0) {
15657978SPeter.Dunlap@Sun.COM 
15667978SPeter.Dunlap@Sun.COM 			*error_detail = ISCSI_LOGIN_STATUS_TARGET_ERROR;
15677978SPeter.Dunlap@Sun.COM 			ret = IDM_STATUS_FAIL;
15687978SPeter.Dunlap@Sun.COM 			goto cleanup;
15697978SPeter.Dunlap@Sun.COM 		}
15707978SPeter.Dunlap@Sun.COM 
15717978SPeter.Dunlap@Sun.COM 		ASSERT(
15727978SPeter.Dunlap@Sun.COM 		    ((lh->flags & ISCSI_FLAG_LOGIN_CONTINUE) &&
15737978SPeter.Dunlap@Sun.COM 		    (next_pdu != NULL)) ||
15747978SPeter.Dunlap@Sun.COM 		    (!(lh->flags & ISCSI_FLAG_LOGIN_CONTINUE) &&
15757978SPeter.Dunlap@Sun.COM 		    (next_pdu == NULL)));
15767978SPeter.Dunlap@Sun.COM 
15777978SPeter.Dunlap@Sun.COM 		if ((lh->flags & ISCSI_FLAG_LOGIN_CONTINUE) &
15787978SPeter.Dunlap@Sun.COM 		    (textbuflen != 0)) {
15797978SPeter.Dunlap@Sun.COM 			/*
15807978SPeter.Dunlap@Sun.COM 			 * Key-value pair is split over two PDU's.  We
15817978SPeter.Dunlap@Sun.COM 			 * assume it willl never be split over more than
15827978SPeter.Dunlap@Sun.COM 			 * two PDU's.
15837978SPeter.Dunlap@Sun.COM 			 */
15847978SPeter.Dunlap@Sun.COM 			split_kv = B_TRUE;
15857978SPeter.Dunlap@Sun.COM 			leftover_textbuf = textbuf;
15867978SPeter.Dunlap@Sun.COM 			leftover_textbuflen = textbuflen;
15877978SPeter.Dunlap@Sun.COM 		} else {
15887978SPeter.Dunlap@Sun.COM 			split_kv = B_FALSE;
15897978SPeter.Dunlap@Sun.COM 			if (textbuflen != 0) {
15907978SPeter.Dunlap@Sun.COM 				/*
15917978SPeter.Dunlap@Sun.COM 				 * Incomplete keyword but no additional
15927978SPeter.Dunlap@Sun.COM 				 * PDU's.  This is a malformed login
15937978SPeter.Dunlap@Sun.COM 				 * request.
15947978SPeter.Dunlap@Sun.COM 				 */
15957978SPeter.Dunlap@Sun.COM 				*error_detail =
15967978SPeter.Dunlap@Sun.COM 				    ISCSI_LOGIN_STATUS_INVALID_REQUEST;
15977978SPeter.Dunlap@Sun.COM 				ret = IDM_STATUS_FAIL;
15987978SPeter.Dunlap@Sun.COM 				goto cleanup;
15997978SPeter.Dunlap@Sun.COM 			}
16007978SPeter.Dunlap@Sun.COM 		}
16017978SPeter.Dunlap@Sun.COM 
16027978SPeter.Dunlap@Sun.COM 		list_remove(pdu_list, pdu);
16037978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
16047978SPeter.Dunlap@Sun.COM 		pdu = next_pdu;
16057978SPeter.Dunlap@Sun.COM 	}
16067978SPeter.Dunlap@Sun.COM 
16077978SPeter.Dunlap@Sun.COM cleanup:
16087978SPeter.Dunlap@Sun.COM 
16097978SPeter.Dunlap@Sun.COM 	/*
16107978SPeter.Dunlap@Sun.COM 	 * Free any remaining PDUs on the list. This will only
16117978SPeter.Dunlap@Sun.COM 	 * happen if there were errors encountered during
16127978SPeter.Dunlap@Sun.COM 	 * processing of the textbuf.
16137978SPeter.Dunlap@Sun.COM 	 */
16147978SPeter.Dunlap@Sun.COM 	pdu = list_head(pdu_list);
16157978SPeter.Dunlap@Sun.COM 	while (pdu != NULL) {
16167978SPeter.Dunlap@Sun.COM 		next_pdu = list_next(pdu_list, pdu);
16177978SPeter.Dunlap@Sun.COM 		list_remove(pdu_list, pdu);
16187978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
16197978SPeter.Dunlap@Sun.COM 		pdu = next_pdu;
16207978SPeter.Dunlap@Sun.COM 	}
16217978SPeter.Dunlap@Sun.COM 
16227978SPeter.Dunlap@Sun.COM 	/*
16237978SPeter.Dunlap@Sun.COM 	 * If there were no errors, we have a complete nvlist representing
16247978SPeter.Dunlap@Sun.COM 	 * all the iSCSI key-value pairs in the login request PDU's
16257978SPeter.Dunlap@Sun.COM 	 * that make up this request.
16267978SPeter.Dunlap@Sun.COM 	 */
16277978SPeter.Dunlap@Sun.COM 	return (ret);
16287978SPeter.Dunlap@Sun.COM }
1629