Home | History | Annotate | Download | only in iscsitgtd
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <sys/types.h>
     30 #include <sys/socket.h>
     31 #include <netinet/in.h>
     32 #include <arpa/inet.h>
     33 #include <stdio.h>
     34 #include <unistd.h>
     35 #include <stdlib.h>
     36 #include <string.h>
     37 #include <strings.h>
     38 #include <syslog.h>
     39 #include <ctype.h>
     40 #include <pthread.h>
     41 #include <netdb.h>
     42 #include <libintl.h>
     43 #include <errno.h>
     44 #include <assert.h>
     45 #include <fcntl.h>
     46 #include <sys/byteorder.h>
     47 
     48 #include "isns_protocol.h"
     49 #include "isns_client.h"
     50 #include "queue.h"
     51 
     52 extern target_queue_t	*mgmtq;
     53 
     54 static	uint16_t	xid = 0;
     55 static	pthread_mutex_t	xid_lock = PTHREAD_MUTEX_INITIALIZER;
     56 
     57 /*
     58  * more than 1 processes can accessing get_xid
     59  */
     60 static uint16_t
     61 get_xid()
     62 {
     63 	uint16_t	tmp;
     64 
     65 	(void) pthread_mutex_lock(&xid_lock);
     66 	tmp = xid++;
     67 	(void) pthread_mutex_unlock(&xid_lock);
     68 	return (tmp);
     69 }
     70 
     71 void
     72 ntoh_tlv(isns_tlv_t *tlv)
     73 {
     74 	uint32_t	val;
     75 
     76 	tlv->attr_id = ntohl(tlv->attr_id);
     77 	tlv->attr_len = ntohl(tlv->attr_len);
     78 
     79 	switch (tlv->attr_id) {
     80 		case ISNS_DELIMITER_ATTR_ID:
     81 			break;
     82 		case ISNS_ISCSI_NAME_ATTR_ID:
     83 		case ISNS_EID_ATTR_ID:
     84 		case ISNS_ISCSI_ALIAS_ATTR_ID:
     85 		case ISNS_PORTAL_NAME_ATTR_ID:
     86 		case ISNS_PG_ISCSI_NAME_ATTR_ID:
     87 			break;
     88 
     89 		case ISNS_PORTAL_IP_ADDR_ATTR_ID:
     90 		case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID:
     91 			bcopy(tlv->attr_value, &val, 4);
     92 			*tlv->attr_value = ntohl(val);
     93 			break;
     94 
     95 		default:
     96 			switch (tlv->attr_len) {
     97 				case 4:
     98 					val = ntohl(
     99 					    (uint32_t)(*tlv->attr_value));
    100 					bcopy(&val, tlv->attr_value, 4);
    101 					break;
    102 				default:
    103 					break;
    104 			}
    105 			break;
    106 	}
    107 }
    108 
    109 /*
    110  * print_ntoh_tlv print network byte order tag-length-value attribute
    111  */
    112 void
    113 print_ntoh_tlv(isns_tlv_t *tlv)
    114 {
    115 	uint32_t	tag, len, val, pf_type;
    116 	char		buf[256];
    117 	struct sockaddr_in6	sin6;
    118 
    119 	tag = ntohl(tlv->attr_id);
    120 	len = ntohl(tlv->attr_len);
    121 
    122 	if (len == 0) {
    123 		queue_prt(mgmtq, Q_ISNS_DBG, "Zero length tag: %d\n", tag);
    124 		return;
    125 	}
    126 
    127 	switch (tag) {
    128 		case ISNS_DELIMITER_ATTR_ID:
    129 			break;
    130 		case ISNS_ISCSI_NAME_ATTR_ID:
    131 		case ISNS_EID_ATTR_ID:
    132 		case ISNS_ISCSI_ALIAS_ATTR_ID:
    133 		case ISNS_PORTAL_NAME_ATTR_ID:
    134 		case ISNS_PG_ISCSI_NAME_ATTR_ID:
    135 			queue_prt(mgmtq, Q_ISNS_DBG,
    136 			    "Tag %d: Value: %s\n", tag, tlv->attr_value);
    137 			break;
    138 
    139 		case ISNS_PORTAL_IP_ADDR_ATTR_ID:
    140 		case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID:
    141 			bcopy(tlv->attr_value, &pf_type, 4);
    142 			pf_type = ntohl(pf_type);
    143 			pf_type = (pf_type == sizeof (in6_addr_t))
    144 			    ? PF_INET6 : PF_INET;
    145 			switch (pf_type) {
    146 				case PF_INET:
    147 					/* RFC2372 IPv4 mapped IPv6 address */
    148 					if (inet_ntop(pf_type,
    149 					    (void *)&tlv->attr_value[12],
    150 					    buf, 256) == NULL) {
    151 						syslog(LOG_ERR,
    152 						    "inet_ntop failed");
    153 						break;
    154 					}
    155 					queue_prt(mgmtq, Q_ISNS_DBG,
    156 					    "IP_ADDR %s\n", buf);
    157 					break;
    158 				case PF_INET6:
    159 					bcopy(tlv->attr_value, &sin6,
    160 					    sizeof (struct sockaddr_in6));
    161 					(void) inet_ntop(pf_type,
    162 					    (void *)&sin6.sin6_addr,
    163 					    buf, 256);
    164 					break;
    165 				default:
    166 					queue_prt(mgmtq, Q_ISNS_DBG,
    167 					    "unknown pf_type\n");
    168 					break;
    169 			}
    170 			break;
    171 
    172 		default:
    173 			switch (len) {
    174 				case 4:
    175 					bcopy(tlv->attr_value, &val, 4);
    176 					val = ntohl(val);
    177 					queue_prt(mgmtq, Q_ISNS_DBG,
    178 					    "Tag: %d Value: %ld\n", tag, val);
    179 					break;
    180 				default:
    181 					break;
    182 			}
    183 			break;
    184 	}
    185 }
    186 
    187 void
    188 print_attr(isns_tlv_t *attr, void *pval, uint32_t ival)
    189 {
    190 	uint32_t	tag = ntohl(attr->attr_id);
    191 	uint32_t	len = ntohl(attr->attr_len);
    192 	uint32_t	pf_type;
    193 	char		buf[256];
    194 
    195 	queue_prt(mgmtq, Q_ISNS_DBG, "Tag: %d Length: %d\n", tag, len);
    196 	switch (tag) {
    197 		case ISNS_DELIMITER_ATTR_ID:
    198 			break;
    199 
    200 		case ISNS_ISCSI_NAME_ATTR_ID:
    201 		case ISNS_EID_ATTR_ID:
    202 		case ISNS_ISCSI_ALIAS_ATTR_ID:
    203 		case ISNS_PORTAL_NAME_ATTR_ID:
    204 		case ISNS_PG_ISCSI_NAME_ATTR_ID:
    205 			if (len && pval != NULL) {
    206 				queue_prt(mgmtq, Q_ISNS_DBG, "Value: %s\n",
    207 				    attr->attr_value);
    208 			}
    209 			break;
    210 
    211 		case ISNS_PORTAL_IP_ADDR_ATTR_ID:
    212 		case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID:
    213 			if (len) {
    214 				pf_type = (ival == sizeof (in6_addr_t))
    215 				    ? PF_INET6 : PF_INET;
    216 				(void) inet_ntop(pf_type, pval, buf, 256);
    217 				queue_prt(mgmtq, Q_ISNS_DBG, "IP_ADDR %s\n",
    218 				    buf);
    219 			}
    220 			break;
    221 
    222 		default:
    223 		switch (len) {
    224 			case 4:
    225 				queue_prt(mgmtq, Q_ISNS_DBG,
    226 				    "Value: %d\n",
    227 				    ntohl(*attr->attr_value));
    228 				break;
    229 			default:
    230 				break;
    231 		}
    232 		break;
    233 	}
    234 }
    235 
    236 void
    237 print_isns_hdr(isns_hdr_t *hdr)
    238 {
    239 	queue_prt(mgmtq, Q_ISNS_DBG, "hdr->version %d\n", hdr->version);
    240 	queue_prt(mgmtq, Q_ISNS_DBG, "hdr->func_id %x\n", hdr->func_id);
    241 	queue_prt(mgmtq, Q_ISNS_DBG, "hdr->pdu_len %d\n", hdr->pdu_len);
    242 	queue_prt(mgmtq, Q_ISNS_DBG, "hdr->flags %x\n", hdr->flags);
    243 	queue_prt(mgmtq, Q_ISNS_DBG, "hdr->xid %d\n", hdr->xid);
    244 	queue_prt(mgmtq, Q_ISNS_DBG, "hdr->seqid %d\n", hdr->seqid);
    245 }
    246 
    247 void
    248 ntoh_isns_hdr(isns_hdr_t *hdr)
    249 {
    250 	hdr->version = ntohs(hdr->version);
    251 	hdr->func_id = ntohs(hdr->func_id);
    252 	hdr->pdu_len = ntohs(hdr->pdu_len);
    253 	hdr->flags = ntohs(hdr->flags);
    254 	hdr->xid = ntohs(hdr->xid);
    255 	hdr->seqid = ntohs(hdr->seqid);
    256 }
    257 
    258 int
    259 isns_append_attr(isns_pdu_t *pdu, uint32_t tag,  uint32_t len,
    260 	void *pval, uint32_t ival)
    261 {
    262 	uint32_t	val;
    263 	uint32_t	pad_len;
    264 	uint16_t	pdu_len;
    265 	isns_tlv_t	*attr;
    266 	char		*tlv;
    267 
    268 	if (pdu == NULL) {
    269 		syslog(LOG_ALERT, "NULL PDU\n");
    270 		return (-1);
    271 	}
    272 
    273 	/* get current pdu payload length */
    274 	pdu_len = ntohs(pdu->payload_len);
    275 
    276 	/* pad 4 bytes alignment */
    277 	pad_len = PAD4(len);
    278 
    279 	if ((pdu_len + pad_len) > MAX_PDU_PAYLOAD_SZ) {
    280 		syslog(LOG_ALERT, "Exceeded PDU size\n");
    281 		return (-1);
    282 	}
    283 
    284 	if ((attr = (isns_tlv_t *)malloc(ISNS_ATTR_SZ(pad_len))) == NULL) {
    285 		syslog(LOG_ALERT, "Malloc error");
    286 		return (-1);
    287 	}
    288 	bzero(attr, ISNS_ATTR_SZ(pad_len));
    289 	attr->attr_id = htonl(tag);
    290 	attr->attr_len = htonl(pad_len);
    291 
    292 	switch (tag) {
    293 		case ISNS_DELIMITER_ATTR_ID:
    294 			break;
    295 
    296 		case ISNS_ISCSI_NAME_ATTR_ID:
    297 		case ISNS_EID_ATTR_ID:
    298 		case ISNS_ISCSI_ALIAS_ATTR_ID:
    299 		case ISNS_PORTAL_NAME_ATTR_ID:
    300 		case ISNS_PG_ISCSI_NAME_ATTR_ID:
    301 			if (len && pval != NULL) {
    302 				bcopy(pval, attr->attr_value, len);
    303 			}
    304 			break;
    305 
    306 		case ISNS_PORTAL_IP_ADDR_ATTR_ID:
    307 		case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID:
    308 			if (len && ival == sizeof (in_addr_t)) {
    309 				/* IPv4 */
    310 				attr->attr_value[10] = 0xFF;
    311 				attr->attr_value[11] = 0xFF;
    312 				bcopy(pval, ((attr->attr_value) + 12), ival);
    313 			} else if (len && ival == sizeof (in6_addr_t)) {
    314 				/* IPv6 */
    315 				bcopy(pval, attr->attr_value, ival);
    316 			}
    317 			break;
    318 
    319 		default:
    320 			switch (len) {
    321 				case 4:
    322 					val = htonl(ival);
    323 					bcopy(&val, attr->attr_value, 4);
    324 					break;
    325 				default:
    326 					break;
    327 			}
    328 			break;
    329 	}
    330 
    331 	/* copy attribute to pdu */
    332 	tlv = (char *)pdu + ISNSP_HEADER_SIZE + pdu_len;
    333 	bcopy(attr, tlv, ISNS_ATTR_SZ(pad_len));
    334 	pdu->payload_len = htons(pdu_len + ISNS_ATTR_SZ(pad_len));
    335 
    336 	/* debug only */
    337 	print_ntoh_tlv(attr);
    338 
    339 	free(attr);
    340 	return (0);
    341 }
    342 
    343 int
    344 isns_create_pdu(uint16_t func_id, uint32_t flags, isns_pdu_t **pdu)
    345 {
    346 	size_t	pdu_sz = MAX_PDU_SZ;
    347 
    348 	if ((*pdu = (isns_pdu_t *)malloc(pdu_sz)) == NULL) {
    349 		syslog(LOG_ERR, "isns_create_pdu malloc failure");
    350 		return (-1);
    351 	}
    352 
    353 	bzero(*pdu, pdu_sz);
    354 	(*pdu)->payload_len = 0;
    355 	(*pdu)->seq = 0;
    356 	(*pdu)->xid = htons(get_xid());
    357 	(*pdu)->version = htons((uint16_t)ISNSP_VERSION);
    358 	(*pdu)->func_id = htons((uint16_t)(func_id));
    359 	(*pdu)->flags = htons((uint16_t)(flags | ISNS_FLAG_CLIENT |
    360 	    ISNS_FLAG_FIRST_PDU | ISNS_FLAG_LAST_PDU));
    361 	return (0);
    362 }
    363 
    364 void
    365 isns_free_pdu(void *pdu)
    366 {
    367 	free(pdu);
    368 }
    369 
    370 /*
    371  * Desc: Open connection to the isns server
    372  * Args: isns server name or isns server ip-addr
    373  * Return: -1 if open failed, descriptor to socket if open succeeded
    374  */
    375 int
    376 isns_open(char *server)
    377 {
    378 	struct addrinfo		hints, *ai, *aip;
    379 	struct sockaddr		*sa;
    380 	struct sockaddr_in	sin;
    381 	struct sockaddr_in6	sin6;
    382 	size_t	sa_len;
    383 	int	so;
    384 	int	ret;
    385 	fd_set rfdset;
    386 	fd_set wfdset;
    387 	fd_set errfdset;
    388 	struct timeval timeout;
    389 	Boolean_t shouldsockblock = False;
    390 	int socket_ready = 0;
    391 	timeout.tv_sec = 5;   /* 5 Secs Timeout */
    392 	timeout.tv_usec = 0;   /* 0 uSecs Timeout */
    393 
    394 	if (server == NULL) {
    395 		syslog(LOG_ERR, "ISNS server ID required");
    396 		return (-1);
    397 	}
    398 
    399 	bzero(&hints, sizeof (struct addrinfo));
    400 	hints.ai_family = AF_UNSPEC;
    401 	if ((ret = getaddrinfo(server, NULL, NULL, &ai)) != 0) {
    402 		syslog(LOG_ALERT, "getaddrinfo failed on server %s: %s", server,
    403 		    gai_strerror(ret));
    404 		return (-1);
    405 	}
    406 
    407 	aip = ai;
    408 	do {
    409 		so = socket(aip->ai_family, SOCK_STREAM, 0);
    410 		if (so != -1) {
    411 
    412 			/* set it to non blocking so connect wont hang */
    413 			if (setsocknonblocking(so) == -1) {
    414 				(void) close(so);
    415 				continue;
    416 			}
    417 
    418 			sa_len = aip->ai_addrlen;
    419 			switch (aip->ai_family) {
    420 				case PF_INET:
    421 					bzero(&sin,
    422 					    sizeof (struct sockaddr_in));
    423 					sa = (struct sockaddr *)&sin;
    424 					bcopy(aip->ai_addr, sa, sa_len);
    425 					sin.sin_port = htons(
    426 					    ISNS_DEFAULT_SERVER_PORT);
    427 					break;
    428 				case PF_INET6:
    429 					bzero(&sin6,
    430 					    sizeof (struct sockaddr_in6));
    431 					sa = (struct sockaddr *)&sin6;
    432 					bcopy(aip->ai_addr, sa, sa_len);
    433 					sin6.sin6_port =
    434 					    htons(ISNS_DEFAULT_SERVER_PORT);
    435 					break;
    436 				default:
    437 					syslog(LOG_ALERT, "Bad protocol");
    438 					(void) close(so);
    439 					continue;
    440 			}
    441 
    442 			ret = connect(so, sa, sa_len);
    443 			if (ret == 0) {
    444 				/*
    445 				 * connection succeeded with out
    446 				 * blocking
    447 				 */
    448 				shouldsockblock = True;
    449 			}
    450 
    451 			if (ret < 0) {
    452 				if (errno == EINPROGRESS) {
    453 					FD_ZERO(&rfdset);
    454 					FD_ZERO(&wfdset);
    455 					FD_ZERO(&errfdset);
    456 					FD_SET(so, &rfdset);
    457 					FD_SET(so, &wfdset);
    458 					FD_SET(so, &errfdset);
    459 					socket_ready =
    460 					    select(so + 1, &rfdset, &wfdset,
    461 					    &errfdset, &timeout);
    462 					if (socket_ready < 0) {
    463 						syslog(LOG_ALERT,
    464 						    "failed to connect with"
    465 						" isns, err=%d", errno);
    466 						(void) close(so);
    467 					} else if (socket_ready == 0) {
    468 						syslog(LOG_ALERT,
    469 						    "time out failed"
    470 						    " to connect with isns");
    471 						(void) close(so);
    472 					} else { /* Socket is ready */
    473 						/*
    474 						 * Check if socket is ready
    475 						 */
    476 						if (is_socket_ready(so,
    477 						    &rfdset, &wfdset,
    478 						    &errfdset) == True)
    479 							shouldsockblock = True;
    480 						else
    481 							(void) close(so);
    482 					}
    483 				} else {
    484 					syslog(LOG_WARNING,
    485 					    "Connect failed no progress");
    486 					(void) close(so);
    487 				}
    488 			}
    489 
    490 			if (shouldsockblock == True) {
    491 				if (-1 == setsockblocking(so)) {
    492 					(void) close(so);
    493 					shouldsockblock = False;
    494 				} else {
    495 					freeaddrinfo(ai);
    496 					return (so);
    497 				}
    498 			}
    499 
    500 		}
    501 	} while ((aip = aip->ai_next) != NULL);
    502 
    503 	if (ai != NULL)
    504 		freeaddrinfo(ai);
    505 	return (-1);
    506 }
    507 
    508 /*
    509  * According to:
    510  * UNIX Network Programming Volume 1, Third Edition:
    511  * The Sockets Networking APIBOOK:
    512  *
    513  * When the connection completes successfully, the descriptor becomes
    514  * writable (p. 531 of TCPv2).
    515  * When the connection establishment encounters an error, the descriptor
    516  * becomes both readable and writable (p. 530 of TCPv2).
    517  */
    518 Boolean_t
    519 is_socket_ready(int so, fd_set *rfdset, fd_set *wfdset,
    520 		    fd_set *errfdset)
    521 {
    522 	if ((FD_ISSET(so, wfdset) &&
    523 	    FD_ISSET(so, rfdset)) ||
    524 	    FD_ISSET(so, errfdset)) {
    525 		return (False);
    526 	} else {
    527 		return (True);
    528 	}
    529 }
    530 
    531 int
    532 setsocknonblocking(int so)
    533 {
    534 	int flags;
    535 	/* set it to non blocking */
    536 	if (-1 == (flags = fcntl(so, F_GETFL, 0))) {
    537 		syslog(LOG_WARNING,
    538 		    "Failed to get socket flags. Blocking..");
    539 		return (-1);
    540 	}
    541 
    542 	if (fcntl(so, F_SETFL, flags | O_NONBLOCK) == -1) {
    543 		syslog(LOG_WARNING,
    544 		    "Failed to set socket in non blocking mode");
    545 		return (-1);
    546 	}
    547 	return (0);
    548 }
    549 
    550 int
    551 setsockblocking(int so)
    552 {
    553 	int flags;
    554 	/* set it to non blocking */
    555 	if (-1 == (flags = fcntl(so, F_GETFL, 0))) {
    556 		syslog(LOG_WARNING, " Failed to get flags on socket..");
    557 		return (-1);
    558 	}
    559 
    560 	flags &= ~O_NONBLOCK;
    561 	if (fcntl(so, F_SETFL, flags) == -1) {
    562 		syslog(LOG_WARNING, " failed to set socket to blocking");
    563 		return (-1);
    564 	}
    565 	return (0);
    566 }
    567 
    568 
    569 void
    570 isns_close(int so)
    571 {
    572 	if (so) {
    573 		(void) close(so);
    574 	}
    575 }
    576 
    577 /*
    578  * isns_send allocated pdu, caller needs to free pdu when done processing
    579  */
    580 int
    581 isns_send(int so, isns_pdu_t *pdu)
    582 {
    583 	size_t	len;
    584 
    585 	assert(pdu != NULL);
    586 
    587 	len = ISNSP_HEADER_SIZE + ntohs(pdu->payload_len);
    588 	if (send(so, pdu, len, 0) == -1) {
    589 		syslog(LOG_ALERT, "isns_send failure");
    590 		return (-1);
    591 	}
    592 	return (0);
    593 }
    594 
    595 /*
    596  * Desc: isns_recv malloc memory for the isns response message, user needs
    597  *	to isns_free() memory after process the response message.  The
    598  *	isns header is converted to host byte order, the remaining TLV
    599  *	attributes can be converted using ntoh_tlv()
    600  */
    601 int
    602 isns_recv(int so, isns_rsp_t **pdu)
    603 {
    604 	isns_hdr_t	hdr, hdr1;
    605 	isns_rsp_t	*rsp;
    606 	uint8_t		*ptr;
    607 	size_t		total_pdu_len;
    608 	int		len;
    609 	uint16_t	xid_x, func_id_x, seqid_x;
    610 	boolean_t	done;
    611 
    612 	*pdu = NULL;
    613 	total_pdu_len = 0;
    614 	seqid_x = 0;
    615 	done = FALSE;
    616 
    617 	do {
    618 		/* read pdu header 1st */
    619 		do {
    620 			len = recv(so, &hdr1, ISNSP_HEADER_SIZE, MSG_WAITALL);
    621 		} while ((len == -1) && (errno == EINTR));
    622 
    623 		if (len != ISNSP_HEADER_SIZE) {
    624 			syslog(LOG_ALERT, "isns_recv fail to read header");
    625 			return (-1);
    626 		}
    627 
    628 		/* normalize the pdu header for processing */
    629 		bcopy(&hdr1, &hdr, sizeof (isns_hdr_t));
    630 		ntoh_isns_hdr(&hdr);
    631 
    632 		if (IS_1ST_PDU(hdr.flags)) {
    633 			if (hdr.seqid != 0) {
    634 				syslog(LOG_ALERT, "ISNS out of sequence");
    635 				return (-1);
    636 			}
    637 			xid_x = hdr.xid;
    638 			func_id_x = hdr.func_id;
    639 		}
    640 
    641 		if (IS_LAST_PDU(hdr.flags)) {
    642 			done = TRUE;
    643 		}
    644 
    645 		/* verify seq, xid, func_id */
    646 		if (seqid_x != hdr.seqid) {
    647 			syslog(LOG_ALERT, "ISNS out of sequence");
    648 			return (-1);
    649 		}
    650 		if (xid_x != hdr.xid || func_id_x != hdr.func_id) {
    651 			syslog(LOG_ALERT, "Non matching xid or func_id");
    652 			return (-1);
    653 		}
    654 
    655 		++seqid_x;	/* next expected seqid */
    656 
    657 		/* malloc size + previous payload length */
    658 		if ((ptr = malloc(ISNSP_HEADER_SIZE + hdr.pdu_len
    659 		    + total_pdu_len)) == NULL) {
    660 			syslog(LOG_ALERT, "Malloc failure");
    661 			return (-1);
    662 		}
    663 		bzero(ptr, ISNSP_HEADER_SIZE + hdr.pdu_len);
    664 
    665 		if (hdr.seqid == 0) {
    666 			*pdu = (void *)ptr;
    667 			bcopy(&hdr1, ptr, ISNSP_HEADER_SIZE);
    668 			ptr += ISNSP_HEADER_SIZE;
    669 			if ((len = recv(so, ptr, hdr.pdu_len, MSG_WAITALL))
    670 			    != hdr.pdu_len) {
    671 				syslog(LOG_ERR,
    672 				    "isns_recv fail to read 1st payload");
    673 				free(*pdu);
    674 				*pdu = NULL;
    675 				return (-1);
    676 			}
    677 		} else {
    678 			/* merge the pdu */
    679 			bcopy(*pdu, ptr, ISNSP_HEADER_SIZE + total_pdu_len);
    680 			free(*pdu);
    681 			*pdu = (void *)ptr;
    682 			ptr += (ISNSP_HEADER_SIZE + total_pdu_len);
    683 			if (recv(so, ptr, hdr.pdu_len, MSG_WAITALL)
    684 			    != hdr.pdu_len) {
    685 				syslog(LOG_ERR,
    686 				    "isns_recv fail to read payload");
    687 				free(*pdu);
    688 				*pdu = NULL;
    689 				return (-1);
    690 			}
    691 		}
    692 		total_pdu_len += hdr.pdu_len;
    693 
    694 	} while (done == FALSE);
    695 
    696 	/* normalize the response status */
    697 	rsp = (isns_rsp_t *)*pdu;
    698 	rsp->pdu_len = htons(total_pdu_len);
    699 	ntoh_isns_hdr((isns_hdr_t *)rsp);
    700 
    701 	return (0);
    702 }
    703