Beispiel #1
0
/*
 * kgetipnodebyname
 *
 * This function builds a request that will be sent to the iscsi_door.
 * The iSCSI door after receiving the request calls getipnodebyaddr().
 * for more information on the input, output parameter and return value,
 * consult the man page for getipnodebyname().
 *
 * Before calling the iscsi door this function tries to do the conversion
 * locally.  If a name resolution is needed the iscsi door is called.
 *
 * There's some limitations to the information returned by this function.
 * Only one address of the address list returned by getipnodebyname() is
 * returned.  The other parameters of the structure should be ignored.
 */
struct hostent *
kgetipnodebyname(
	const char	*name,
	int		af,
	int		flags,
	int		*error_num
)
{
	door_arg_t		arg;
	mybuffer_t		*buffer;
	size_t			msg_size = ISCSI_DOOR_MAX_DATA_SIZE;
	size_t			hostent_size = ISCSI_DOOR_MAX_DATA_SIZE;
	size_t			buffer_size;
	getipnodebyname_req_t	*req;
	getipnodebyname_cnf_t	*cnf;
	struct hostent		*hptr;


	buffer_size = msg_size + hostent_size + sizeof (mybuffer_t);
	buffer = (mybuffer_t *)kmem_zalloc(buffer_size, KM_SLEEP);

	if (buffer) {

		/*
		 * The buffer was successfully allocated.
		 *
		 *	  Buffer
		 *
		 * +--------------------+ <--- buffer
		 * |	mybuffer_t	|
		 * +--------------------+ <--- hptr
		 * |			|
		 * |			|
		 * |	hostent_size	|
		 * |			|
		 * |			|
		 * |			|
		 * +--------------------+ <--- req, cnf
		 * |			|
		 * |			|
		 * |			|
		 * |	msg_size	|
		 * |			|
		 * |			|
		 * |			|
		 * +--------------------+
		 */
		buffer->signature = ISCSI_DOOR_REQ_SIGNATURE;
		buffer->size = buffer_size;

		hptr = (struct hostent *)((char *)buffer + sizeof (mybuffer_t));
		req = (getipnodebyname_req_t *)((char *)hptr + hostent_size);
		cnf = (getipnodebyname_cnf_t *)((char *)hptr + hostent_size);

		hostent_size -= sizeof (struct hostent);

		/*
		 * We try first locally.  If the conversion cannot be done
		 * by inet_pton the door is called.
		 * The cnf address is used as output buffer.
		 * inet_pton returns '1' if the conversion was successful.
		 */
		switch (af) {
		case AF_INET:
			hptr->h_length = sizeof (struct in_addr);
			break;
		case AF_INET6:
			hptr->h_length = sizeof (struct in6_addr);
			break;
		default:
			kfreehostent(hptr);
			*error_num = NO_RECOVERY;
			return (NULL);
		}
		if ((msg_size < hptr->h_length) ||
		    (hostent_size < sizeof (char *))) {
			kfreehostent(hptr);
			*error_num = NO_RECOVERY;
			return (NULL);
		}
		if (inet_pton(af, (char *)name, cnf) == 1) {
			/*
			 * inet_pton converted the string successfully.
			 */
			hptr->h_addrtype = af;
			hptr->h_addr_list = (char **)((char *)hptr +
			    sizeof (struct hostent));
			*hptr->h_addr_list = (char *)cnf;
			return (hptr);
		}

		/*
		 * The name couldn't ne converted by inet_pton.  The door is
		 * called.
		 */

		/* Header initialization. */
		req->hdr.signature = ISCSI_DOOR_REQ_SIGNATURE;
		req->hdr.version = ISCSI_DOOR_REQ_VERSION_1;
		req->hdr.opcode = ISCSI_DOOR_GETIPNODEBYNAME_REQ;

		/* Body initialization. */
		req->name_length = strlen(name);
		if (req->name_length >
		    (msg_size - sizeof (getipnodebyname_req_t) - 1)) {
			kfreehostent(hptr);
			*error_num = NO_RECOVERY;
			return (NULL);
		}

		req->name_offset = sizeof (getipnodebyname_req_t);
		req->af = af;
		req->flags = flags;
		bcopy(
		    name,
		    ((char *)req + req->name_offset),
		    req->name_length);

		/* Door argument initialization. */
		arg.data_ptr = (char *)req;
		arg.data_size = msg_size;
		arg.desc_num = 0;
		arg.desc_ptr = NULL;
		arg.rbuf = (char *)cnf;
		arg.rsize = msg_size;

		if (iscsi_door_upcall(&arg) == B_FALSE) {
			/* The door call failed */
			kfreehostent(hptr);
			*error_num = NO_RECOVERY;
			return (NULL);
		}

		/*
		 * The door call itself was successful.  The value returned
		 * in arg.rbuf should be cnf, but we never know.
		 */
		cnf = (getipnodebyname_cnf_t *)arg.rbuf;

		if ((cnf == NULL) ||
		    (arg.rsize < sizeof (getipnodebyname_cnf_t)) ||
		    (cnf->hdr.signature != ISCSI_DOOR_REQ_SIGNATURE) ||
		    (cnf->hdr.version != ISCSI_DOOR_REQ_VERSION_1) ||
		    (cnf->hdr.opcode != ISCSI_DOOR_GETIPNODEBYNAME_CNF) ||
		    ((cnf->hdr.status != ISCSI_DOOR_STATUS_SUCCESS) &&
		    (cnf->hdr.status != ISCSI_DOOR_STATUS_MORE))) {
			/* The door didn't like the request */
			kfreehostent(hptr);
			*error_num = NO_RECOVERY;
			return (NULL);
		}

		if (cnf->h_addr_list_length == 0) {
			kfreehostent(hptr);
			*error_num = HOST_NOT_FOUND;
			return (NULL);
		}

		hptr->h_addrtype = cnf->h_addrtype;
		hptr->h_length = cnf->h_addrlen;
		hptr->h_addr_list = (char **)((char *)hptr +
		    sizeof (struct hostent));
		*hptr->h_addr_list = ((char *)cnf + cnf->h_addr_list_offset);
		return (hptr);
	} else {
		*error_num = NO_RECOVERY;
		return (NULL);
	}
}
/*
 * iscsi_create_sendtgts_list -  Based upon the given data, build a
 * linked list of SendTarget information.  The data passed into this
 * function  is expected to be the data portion(s) of SendTarget text
 * response.
 */
static iscsi_status_t
iscsi_create_sendtgts_list(iscsi_conn_t *icp, char *data, int data_len,
    iscsi_sendtgts_list_t *stl)
{
	char			*line = NULL;
	boolean_t		targetname_added = B_FALSE;
	iscsi_sendtgts_entry_t	*curr_ste = NULL,
	    *prev_ste = NULL;
	struct hostent		*hptr;
	int			error_num;

	/* initialize number of targets found */
	stl->stl_out_cnt = 0;

	if (data_len == 0)
		return (ISCSI_STATUS_SUCCESS);

	while ((line = iscsi_get_next_text(data, data_len, line)) != NULL) {
		if (strncmp(TARGETNAME, line, strlen(TARGETNAME)) == 0) {
			/* check if this is first targetname */
			if (prev_ste != NULL) {
				stl->stl_out_cnt++;
			}
			if (stl->stl_out_cnt >= stl->stl_in_cnt) {
				/*
				 * continue processing the data so that
				 * the total number of targets are known
				 * and the caller can retry with the correct
				 * number of entries in the list
				 */
				continue;
			}
			curr_ste = &(stl->stl_list[stl->stl_out_cnt]);

			/*
			 * This entry will use the IP address and port
			 * that was passed into this routine. If the next
			 * line that we receive is a TargetAddress we will
			 * know to modify this entry with the new IP address,
			 * port and portal group tag. If this state flag
			 * is not set we'll just create a new entry using
			 * only the previous entries targetname.
			 */
			(void) strncpy((char *)curr_ste->ste_name,
			    line + strlen(TARGETNAME),
			    sizeof (curr_ste->ste_name));

			if (icp->conn_base_addr.sin.sa_family == AF_INET) {

				struct sockaddr_in *addr_in =
				    &icp->conn_base_addr.sin4;
				curr_ste->ste_ipaddr.a_addr.i_insize =
				    sizeof (struct in_addr);
				bcopy(&addr_in->sin_addr.s_addr,
				    &curr_ste->ste_ipaddr.a_addr.i_addr,
				    sizeof (struct in_addr));
				curr_ste->ste_ipaddr.a_port =
				    htons(addr_in->sin_port);

			} else {

				struct sockaddr_in6 *addr_in6 =
				    &icp->conn_base_addr.sin6;
				curr_ste->ste_ipaddr.a_addr.i_insize =
				    sizeof (struct in6_addr);
				bcopy(&addr_in6->sin6_addr.s6_addr,
				    &curr_ste->ste_ipaddr.a_addr.i_addr,
				    sizeof (struct in6_addr));
				curr_ste->ste_ipaddr.a_port =
				    htons(addr_in6->sin6_port);
			}
			curr_ste->ste_tpgt = -1;

			targetname_added = B_TRUE;

		} else if (strncmp(TARGETADDRESS, line,
		    strlen(TARGETADDRESS)) == 0) {

			char *in_str,
			    *tmp_buf,
			    *addr_str,
			    *port_str,
			    *tpgt_str;
			int type,
			    tmp_buf_len;
			long result;

			/*
			 * If TARGETADDRESS is first line a SendTarget response
			 * (i.e. no TARGETNAME lines preceding), treat as
			 * an error.  To check this an assumption is made that
			 * at least one sendtarget_entry_t should exist prior
			 * to entering this code.
			 */
			if (prev_ste == NULL) {
				cmn_err(CE_NOTE, "SendTargets protocol error: "
				    "TARGETADDRESS first");
				return (ISCSI_STATUS_PROTOCOL_ERROR);
			}

			/*
			 * If we can't find an '=' then the sendtargets
			 * response if invalid per spec.  Return empty list.
			 */
			in_str = strchr(line, '=');
			if (in_str == NULL) {
				return (ISCSI_STATUS_PROTOCOL_ERROR);
			}

			/* move past the '=' */
			in_str++;

			/* Copy  addr, port, and tpgt into temporary buffer */
			tmp_buf_len = strlen(in_str) + 1;
			tmp_buf = kmem_zalloc(tmp_buf_len, KM_SLEEP);
			(void) strncpy(tmp_buf, in_str, tmp_buf_len);

			/*
			 * Parse the addr, port, and tpgt from
			 * sendtarget response
			 */
			if (parse_addr_port_tpgt(tmp_buf, &addr_str, &type,
			    &port_str, &tpgt_str) == B_FALSE) {
				/* Unable to extract addr */
				kmem_free(tmp_buf, tmp_buf_len);
				return (ISCSI_STATUS_PROTOCOL_ERROR);
			}

			/* Now convert string addr to binary */
			hptr = kgetipnodebyname(addr_str, type,
			    AI_ALL, &error_num);
			if (!hptr) {
				/* Unable to get valid address */
				kmem_free(tmp_buf, tmp_buf_len);
				return (ISCSI_STATUS_PROTOCOL_ERROR);
			}

			/* Check if space for response */
			if (targetname_added == B_FALSE) {
				stl->stl_out_cnt++;
				if (stl->stl_out_cnt >= stl->stl_in_cnt) {
					/*
					 * continue processing the data so that
					 * the total number of targets are
					 * known and the caller can retry with
					 * the correct number of entries in
					 * the list
					 */
					kfreehostent(hptr);
					kmem_free(tmp_buf, tmp_buf_len);
					continue;
				}
				curr_ste = &(stl->stl_list[stl->stl_out_cnt]);
				(void) strcpy((char *)curr_ste->ste_name,
				    (char *)prev_ste->ste_name);
			}

			curr_ste->ste_ipaddr.a_addr.i_insize = hptr->h_length;
			bcopy(*hptr->h_addr_list,
			    &(curr_ste->ste_ipaddr.a_addr.i_addr),
			    curr_ste->ste_ipaddr.a_addr.i_insize);
			kfreehostent(hptr);

			if (port_str != NULL) {
				(void) ddi_strtol(port_str, NULL, 0, &result);
				curr_ste->ste_ipaddr.a_port = (short)result;
			} else {
				curr_ste->ste_ipaddr.a_port = ISCSI_LISTEN_PORT;
			}

			if (tpgt_str != NULL) {
				(void) ddi_strtol(tpgt_str, NULL, 0, &result);
				curr_ste->ste_tpgt = (short)result;
			} else {
				cmn_err(CE_NOTE, "SendTargets protocol error: "
				    "TPGT not specified");
				kmem_free(tmp_buf, tmp_buf_len);
				return (ISCSI_STATUS_PROTOCOL_ERROR);
			}

			kmem_free(tmp_buf, tmp_buf_len);

			targetname_added = B_FALSE;

		} else if (strlen(line) != 0) {
			/*
			 * Any other string besides an empty string
			 * is a protocol error
			 */
			cmn_err(CE_NOTE, "SendTargets protocol error: "
			    "unexpected response");
			return (ISCSI_STATUS_PROTOCOL_ERROR);
		}

		prev_ste = curr_ste;
	}

	/*
	 * If target found increment out count one more time because
	 * this is the total number of entries in the list not an index
	 * like it was used above
	 */
	if (prev_ste != NULL) {
		stl->stl_out_cnt++;
	}

	return (ISCSI_STATUS_SUCCESS);
}