17c478bd9Sstevel@tonic-gate /* 2*e9af4bc0SJohn Beck * Copyright (c) 1999-2004, 2009 Sendmail, Inc. and its suppliers. 37c478bd9Sstevel@tonic-gate * All rights reserved. 47c478bd9Sstevel@tonic-gate * 57c478bd9Sstevel@tonic-gate * By using this file, you agree to the terms and conditions set 67c478bd9Sstevel@tonic-gate * forth in the LICENSE file which can be found at the top level of 77c478bd9Sstevel@tonic-gate * the sendmail distribution. 87c478bd9Sstevel@tonic-gate * 97c478bd9Sstevel@tonic-gate */ 107c478bd9Sstevel@tonic-gate 117c478bd9Sstevel@tonic-gate #include <sm/gen.h> 12*e9af4bc0SJohn Beck SM_RCSID("@(#)$Id: comm.c,v 8.70 2009/12/16 16:33:48 ca Exp $") 137c478bd9Sstevel@tonic-gate 147c478bd9Sstevel@tonic-gate #include "libmilter.h" 157c478bd9Sstevel@tonic-gate #include <sm/errstring.h> 167c478bd9Sstevel@tonic-gate #include <sys/uio.h> 177c478bd9Sstevel@tonic-gate 187c478bd9Sstevel@tonic-gate static ssize_t retry_writev __P((socket_t, struct iovec *, int, struct timeval *)); 197c478bd9Sstevel@tonic-gate static size_t Maxdatasize = MILTER_MAX_DATA_SIZE; 207c478bd9Sstevel@tonic-gate 217c478bd9Sstevel@tonic-gate /* 227c478bd9Sstevel@tonic-gate ** SMFI_SETMAXDATASIZE -- set limit for milter data read/write. 237c478bd9Sstevel@tonic-gate ** 247c478bd9Sstevel@tonic-gate ** Parameters: 257c478bd9Sstevel@tonic-gate ** sz -- new limit. 267c478bd9Sstevel@tonic-gate ** 277c478bd9Sstevel@tonic-gate ** Returns: 287c478bd9Sstevel@tonic-gate ** old limit 297c478bd9Sstevel@tonic-gate */ 307c478bd9Sstevel@tonic-gate 317c478bd9Sstevel@tonic-gate size_t 327c478bd9Sstevel@tonic-gate smfi_setmaxdatasize(sz) 337c478bd9Sstevel@tonic-gate size_t sz; 347c478bd9Sstevel@tonic-gate { 357c478bd9Sstevel@tonic-gate size_t old; 367c478bd9Sstevel@tonic-gate 377c478bd9Sstevel@tonic-gate old = Maxdatasize; 387c478bd9Sstevel@tonic-gate Maxdatasize = sz; 397c478bd9Sstevel@tonic-gate return old; 407c478bd9Sstevel@tonic-gate } 417c478bd9Sstevel@tonic-gate 427c478bd9Sstevel@tonic-gate /* 437c478bd9Sstevel@tonic-gate ** MI_RD_CMD -- read a command 447c478bd9Sstevel@tonic-gate ** 457c478bd9Sstevel@tonic-gate ** Parameters: 467c478bd9Sstevel@tonic-gate ** sd -- socket descriptor 477c478bd9Sstevel@tonic-gate ** timeout -- maximum time to wait 487c478bd9Sstevel@tonic-gate ** cmd -- single character command read from sd 497c478bd9Sstevel@tonic-gate ** rlen -- pointer to length of result 507c478bd9Sstevel@tonic-gate ** name -- name of milter 517c478bd9Sstevel@tonic-gate ** 527c478bd9Sstevel@tonic-gate ** Returns: 537c478bd9Sstevel@tonic-gate ** buffer with rest of command 547c478bd9Sstevel@tonic-gate ** (malloc()ed here, should be free()d) 557c478bd9Sstevel@tonic-gate ** hack: encode error in cmd 567c478bd9Sstevel@tonic-gate */ 577c478bd9Sstevel@tonic-gate 587c478bd9Sstevel@tonic-gate char * 597c478bd9Sstevel@tonic-gate mi_rd_cmd(sd, timeout, cmd, rlen, name) 607c478bd9Sstevel@tonic-gate socket_t sd; 617c478bd9Sstevel@tonic-gate struct timeval *timeout; 627c478bd9Sstevel@tonic-gate char *cmd; 637c478bd9Sstevel@tonic-gate size_t *rlen; 647c478bd9Sstevel@tonic-gate char *name; 657c478bd9Sstevel@tonic-gate { 667c478bd9Sstevel@tonic-gate ssize_t len; 677c478bd9Sstevel@tonic-gate mi_int32 expl; 687c478bd9Sstevel@tonic-gate ssize_t i; 697c478bd9Sstevel@tonic-gate FD_RD_VAR(rds, excs); 707c478bd9Sstevel@tonic-gate int ret; 717c478bd9Sstevel@tonic-gate int save_errno; 727c478bd9Sstevel@tonic-gate char *buf; 737c478bd9Sstevel@tonic-gate char data[MILTER_LEN_BYTES + 1]; 747c478bd9Sstevel@tonic-gate 757c478bd9Sstevel@tonic-gate *cmd = '\0'; 767c478bd9Sstevel@tonic-gate *rlen = 0; 777c478bd9Sstevel@tonic-gate 787c478bd9Sstevel@tonic-gate i = 0; 797c478bd9Sstevel@tonic-gate for (;;) 807c478bd9Sstevel@tonic-gate { 817c478bd9Sstevel@tonic-gate FD_RD_INIT(sd, rds, excs); 827c478bd9Sstevel@tonic-gate ret = FD_RD_READY(sd, rds, excs, timeout); 837c478bd9Sstevel@tonic-gate if (ret == 0) 847c478bd9Sstevel@tonic-gate break; 857c478bd9Sstevel@tonic-gate else if (ret < 0) 867c478bd9Sstevel@tonic-gate { 877c478bd9Sstevel@tonic-gate if (errno == EINTR) 887c478bd9Sstevel@tonic-gate continue; 897c478bd9Sstevel@tonic-gate break; 907c478bd9Sstevel@tonic-gate } 917c478bd9Sstevel@tonic-gate if (FD_IS_RD_EXC(sd, rds, excs)) 927c478bd9Sstevel@tonic-gate { 937c478bd9Sstevel@tonic-gate *cmd = SMFIC_SELECT; 947c478bd9Sstevel@tonic-gate return NULL; 957c478bd9Sstevel@tonic-gate } 967c478bd9Sstevel@tonic-gate 977c478bd9Sstevel@tonic-gate len = MI_SOCK_READ(sd, data + i, sizeof data - i); 987c478bd9Sstevel@tonic-gate if (MI_SOCK_READ_FAIL(len)) 997c478bd9Sstevel@tonic-gate { 1007c478bd9Sstevel@tonic-gate smi_log(SMI_LOG_ERR, 1017c478bd9Sstevel@tonic-gate "%s, mi_rd_cmd: read returned %d: %s", 1027c478bd9Sstevel@tonic-gate name, (int) len, sm_errstring(errno)); 1037c478bd9Sstevel@tonic-gate *cmd = SMFIC_RECVERR; 1047c478bd9Sstevel@tonic-gate return NULL; 1057c478bd9Sstevel@tonic-gate } 1067c478bd9Sstevel@tonic-gate if (len == 0) 1077c478bd9Sstevel@tonic-gate { 1087c478bd9Sstevel@tonic-gate *cmd = SMFIC_EOF; 1097c478bd9Sstevel@tonic-gate return NULL; 1107c478bd9Sstevel@tonic-gate } 1117c478bd9Sstevel@tonic-gate if (len >= (ssize_t) sizeof data - i) 1127c478bd9Sstevel@tonic-gate break; 1137c478bd9Sstevel@tonic-gate i += len; 1147c478bd9Sstevel@tonic-gate } 1157c478bd9Sstevel@tonic-gate if (ret == 0) 1167c478bd9Sstevel@tonic-gate { 1177c478bd9Sstevel@tonic-gate *cmd = SMFIC_TIMEOUT; 1187c478bd9Sstevel@tonic-gate return NULL; 1197c478bd9Sstevel@tonic-gate } 1207c478bd9Sstevel@tonic-gate else if (ret < 0) 1217c478bd9Sstevel@tonic-gate { 1227c478bd9Sstevel@tonic-gate smi_log(SMI_LOG_ERR, 123*e9af4bc0SJohn Beck "%s: mi_rd_cmd: %s() returned %d: %s", 124*e9af4bc0SJohn Beck name, MI_POLLSELECT, ret, sm_errstring(errno)); 1257c478bd9Sstevel@tonic-gate *cmd = SMFIC_RECVERR; 1267c478bd9Sstevel@tonic-gate return NULL; 1277c478bd9Sstevel@tonic-gate } 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate *cmd = data[MILTER_LEN_BYTES]; 1307c478bd9Sstevel@tonic-gate data[MILTER_LEN_BYTES] = '\0'; 1317c478bd9Sstevel@tonic-gate (void) memcpy((void *) &expl, (void *) &(data[0]), MILTER_LEN_BYTES); 1327c478bd9Sstevel@tonic-gate expl = ntohl(expl) - 1; 1337c478bd9Sstevel@tonic-gate if (expl <= 0) 1347c478bd9Sstevel@tonic-gate return NULL; 1357c478bd9Sstevel@tonic-gate if (expl > Maxdatasize) 1367c478bd9Sstevel@tonic-gate { 1377c478bd9Sstevel@tonic-gate *cmd = SMFIC_TOOBIG; 1387c478bd9Sstevel@tonic-gate return NULL; 1397c478bd9Sstevel@tonic-gate } 1407c478bd9Sstevel@tonic-gate #if _FFR_ADD_NULL 1417c478bd9Sstevel@tonic-gate buf = malloc(expl + 1); 1427c478bd9Sstevel@tonic-gate #else /* _FFR_ADD_NULL */ 1437c478bd9Sstevel@tonic-gate buf = malloc(expl); 1447c478bd9Sstevel@tonic-gate #endif /* _FFR_ADD_NULL */ 1457c478bd9Sstevel@tonic-gate if (buf == NULL) 1467c478bd9Sstevel@tonic-gate { 1477c478bd9Sstevel@tonic-gate *cmd = SMFIC_MALLOC; 1487c478bd9Sstevel@tonic-gate return NULL; 1497c478bd9Sstevel@tonic-gate } 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate i = 0; 1527c478bd9Sstevel@tonic-gate for (;;) 1537c478bd9Sstevel@tonic-gate { 1547c478bd9Sstevel@tonic-gate FD_RD_INIT(sd, rds, excs); 1557c478bd9Sstevel@tonic-gate ret = FD_RD_READY(sd, rds, excs, timeout); 1567c478bd9Sstevel@tonic-gate if (ret == 0) 1577c478bd9Sstevel@tonic-gate break; 1587c478bd9Sstevel@tonic-gate else if (ret < 0) 1597c478bd9Sstevel@tonic-gate { 1607c478bd9Sstevel@tonic-gate if (errno == EINTR) 1617c478bd9Sstevel@tonic-gate continue; 1627c478bd9Sstevel@tonic-gate break; 1637c478bd9Sstevel@tonic-gate } 1647c478bd9Sstevel@tonic-gate if (FD_IS_RD_EXC(sd, rds, excs)) 1657c478bd9Sstevel@tonic-gate { 1667c478bd9Sstevel@tonic-gate *cmd = SMFIC_SELECT; 1677c478bd9Sstevel@tonic-gate free(buf); 1687c478bd9Sstevel@tonic-gate return NULL; 1697c478bd9Sstevel@tonic-gate } 1707c478bd9Sstevel@tonic-gate len = MI_SOCK_READ(sd, buf + i, expl - i); 1717c478bd9Sstevel@tonic-gate if (MI_SOCK_READ_FAIL(len)) 1727c478bd9Sstevel@tonic-gate { 1737c478bd9Sstevel@tonic-gate smi_log(SMI_LOG_ERR, 1747c478bd9Sstevel@tonic-gate "%s: mi_rd_cmd: read returned %d: %s", 1757c478bd9Sstevel@tonic-gate name, (int) len, sm_errstring(errno)); 1767c478bd9Sstevel@tonic-gate ret = -1; 1777c478bd9Sstevel@tonic-gate break; 1787c478bd9Sstevel@tonic-gate } 1797c478bd9Sstevel@tonic-gate if (len == 0) 1807c478bd9Sstevel@tonic-gate { 1817c478bd9Sstevel@tonic-gate *cmd = SMFIC_EOF; 1827c478bd9Sstevel@tonic-gate free(buf); 1837c478bd9Sstevel@tonic-gate return NULL; 1847c478bd9Sstevel@tonic-gate } 1857c478bd9Sstevel@tonic-gate if (len > expl - i) 1867c478bd9Sstevel@tonic-gate { 1877c478bd9Sstevel@tonic-gate *cmd = SMFIC_RECVERR; 1887c478bd9Sstevel@tonic-gate free(buf); 1897c478bd9Sstevel@tonic-gate return NULL; 1907c478bd9Sstevel@tonic-gate } 1917c478bd9Sstevel@tonic-gate if (len >= expl - i) 1927c478bd9Sstevel@tonic-gate { 1937c478bd9Sstevel@tonic-gate *rlen = expl; 1947c478bd9Sstevel@tonic-gate #if _FFR_ADD_NULL 1957c478bd9Sstevel@tonic-gate /* makes life simpler for common string routines */ 1967c478bd9Sstevel@tonic-gate buf[expl] = '\0'; 1977c478bd9Sstevel@tonic-gate #endif /* _FFR_ADD_NULL */ 1987c478bd9Sstevel@tonic-gate return buf; 1997c478bd9Sstevel@tonic-gate } 2007c478bd9Sstevel@tonic-gate i += len; 2017c478bd9Sstevel@tonic-gate } 2027c478bd9Sstevel@tonic-gate 2037c478bd9Sstevel@tonic-gate save_errno = errno; 2047c478bd9Sstevel@tonic-gate free(buf); 2057c478bd9Sstevel@tonic-gate 2067c478bd9Sstevel@tonic-gate /* select returned 0 (timeout) or < 0 (error) */ 2077c478bd9Sstevel@tonic-gate if (ret == 0) 2087c478bd9Sstevel@tonic-gate { 2097c478bd9Sstevel@tonic-gate *cmd = SMFIC_TIMEOUT; 2107c478bd9Sstevel@tonic-gate return NULL; 2117c478bd9Sstevel@tonic-gate } 2127c478bd9Sstevel@tonic-gate if (ret < 0) 2137c478bd9Sstevel@tonic-gate { 2147c478bd9Sstevel@tonic-gate smi_log(SMI_LOG_ERR, 215*e9af4bc0SJohn Beck "%s: mi_rd_cmd: %s() returned %d: %s", 216*e9af4bc0SJohn Beck name, MI_POLLSELECT, ret, sm_errstring(save_errno)); 2177c478bd9Sstevel@tonic-gate *cmd = SMFIC_RECVERR; 2187c478bd9Sstevel@tonic-gate return NULL; 2197c478bd9Sstevel@tonic-gate } 2207c478bd9Sstevel@tonic-gate *cmd = SMFIC_UNKNERR; 2217c478bd9Sstevel@tonic-gate return NULL; 2227c478bd9Sstevel@tonic-gate } 2237c478bd9Sstevel@tonic-gate 2247c478bd9Sstevel@tonic-gate /* 2257c478bd9Sstevel@tonic-gate ** RETRY_WRITEV -- Keep calling the writev() system call 2267c478bd9Sstevel@tonic-gate ** until all the data is written out or an error occurs. 2277c478bd9Sstevel@tonic-gate ** 2287c478bd9Sstevel@tonic-gate ** Parameters: 2297c478bd9Sstevel@tonic-gate ** fd -- socket descriptor 2307c478bd9Sstevel@tonic-gate ** iov -- io vector 2317c478bd9Sstevel@tonic-gate ** iovcnt -- number of elements in io vector 2327c478bd9Sstevel@tonic-gate ** must NOT exceed UIO_MAXIOV. 2337c478bd9Sstevel@tonic-gate ** timeout -- maximum time to wait 2347c478bd9Sstevel@tonic-gate ** 2357c478bd9Sstevel@tonic-gate ** Returns: 2367c478bd9Sstevel@tonic-gate ** success: number of bytes written 2377c478bd9Sstevel@tonic-gate ** otherwise: MI_FAILURE 2387c478bd9Sstevel@tonic-gate */ 2397c478bd9Sstevel@tonic-gate 2407c478bd9Sstevel@tonic-gate static ssize_t 2417c478bd9Sstevel@tonic-gate retry_writev(fd, iov, iovcnt, timeout) 2427c478bd9Sstevel@tonic-gate socket_t fd; 2437c478bd9Sstevel@tonic-gate struct iovec *iov; 2447c478bd9Sstevel@tonic-gate int iovcnt; 2457c478bd9Sstevel@tonic-gate struct timeval *timeout; 2467c478bd9Sstevel@tonic-gate { 2477c478bd9Sstevel@tonic-gate int i; 2487c478bd9Sstevel@tonic-gate ssize_t n, written; 2497c478bd9Sstevel@tonic-gate FD_WR_VAR(wrs); 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate written = 0; 2527c478bd9Sstevel@tonic-gate for (;;) 2537c478bd9Sstevel@tonic-gate { 2547c478bd9Sstevel@tonic-gate while (iovcnt > 0 && iov[0].iov_len == 0) 2557c478bd9Sstevel@tonic-gate { 2567c478bd9Sstevel@tonic-gate iov++; 2577c478bd9Sstevel@tonic-gate iovcnt--; 2587c478bd9Sstevel@tonic-gate } 2597c478bd9Sstevel@tonic-gate if (iovcnt <= 0) 2607c478bd9Sstevel@tonic-gate return written; 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate /* 2637c478bd9Sstevel@tonic-gate ** We don't care much about the timeout here, 2647c478bd9Sstevel@tonic-gate ** it's very long anyway; correct solution would be 2657c478bd9Sstevel@tonic-gate ** to take the time before the loop and reduce the 2667c478bd9Sstevel@tonic-gate ** timeout after each invocation. 2677c478bd9Sstevel@tonic-gate ** FD_SETSIZE is checked when socket is created. 2687c478bd9Sstevel@tonic-gate */ 2697c478bd9Sstevel@tonic-gate 2707c478bd9Sstevel@tonic-gate FD_WR_INIT(fd, wrs); 2717c478bd9Sstevel@tonic-gate i = FD_WR_READY(fd, wrs, timeout); 2727c478bd9Sstevel@tonic-gate if (i == 0) 2737c478bd9Sstevel@tonic-gate return MI_FAILURE; 2747c478bd9Sstevel@tonic-gate if (i < 0) 2757c478bd9Sstevel@tonic-gate { 2767c478bd9Sstevel@tonic-gate if (errno == EINTR || errno == EAGAIN) 2777c478bd9Sstevel@tonic-gate continue; 2787c478bd9Sstevel@tonic-gate return MI_FAILURE; 2797c478bd9Sstevel@tonic-gate } 2807c478bd9Sstevel@tonic-gate n = writev(fd, iov, iovcnt); 2817c478bd9Sstevel@tonic-gate if (n == -1) 2827c478bd9Sstevel@tonic-gate { 2837c478bd9Sstevel@tonic-gate if (errno == EINTR || errno == EAGAIN) 2847c478bd9Sstevel@tonic-gate continue; 2857c478bd9Sstevel@tonic-gate return MI_FAILURE; 2867c478bd9Sstevel@tonic-gate } 2877c478bd9Sstevel@tonic-gate 2887c478bd9Sstevel@tonic-gate written += n; 2897c478bd9Sstevel@tonic-gate for (i = 0; i < iovcnt; i++) 2907c478bd9Sstevel@tonic-gate { 2917c478bd9Sstevel@tonic-gate if (iov[i].iov_len > (unsigned int) n) 2927c478bd9Sstevel@tonic-gate { 2937c478bd9Sstevel@tonic-gate iov[i].iov_base = (char *)iov[i].iov_base + n; 2947c478bd9Sstevel@tonic-gate iov[i].iov_len -= (unsigned int) n; 2957c478bd9Sstevel@tonic-gate break; 2967c478bd9Sstevel@tonic-gate } 2977c478bd9Sstevel@tonic-gate n -= (int) iov[i].iov_len; 2987c478bd9Sstevel@tonic-gate iov[i].iov_len = 0; 2997c478bd9Sstevel@tonic-gate } 3007c478bd9Sstevel@tonic-gate if (i == iovcnt) 3017c478bd9Sstevel@tonic-gate return written; 3027c478bd9Sstevel@tonic-gate } 3037c478bd9Sstevel@tonic-gate } 3047c478bd9Sstevel@tonic-gate 3057c478bd9Sstevel@tonic-gate /* 3067c478bd9Sstevel@tonic-gate ** MI_WR_CMD -- write a cmd to sd 3077c478bd9Sstevel@tonic-gate ** 3087c478bd9Sstevel@tonic-gate ** Parameters: 3097c478bd9Sstevel@tonic-gate ** sd -- socket descriptor 3107c478bd9Sstevel@tonic-gate ** timeout -- maximum time to wait 3117c478bd9Sstevel@tonic-gate ** cmd -- single character command to write 3127c478bd9Sstevel@tonic-gate ** buf -- buffer with further data 3137c478bd9Sstevel@tonic-gate ** len -- length of buffer (without cmd!) 3147c478bd9Sstevel@tonic-gate ** 3157c478bd9Sstevel@tonic-gate ** Returns: 3167c478bd9Sstevel@tonic-gate ** MI_SUCCESS/MI_FAILURE 3177c478bd9Sstevel@tonic-gate */ 3187c478bd9Sstevel@tonic-gate 3197c478bd9Sstevel@tonic-gate int 3207c478bd9Sstevel@tonic-gate mi_wr_cmd(sd, timeout, cmd, buf, len) 3217c478bd9Sstevel@tonic-gate socket_t sd; 3227c478bd9Sstevel@tonic-gate struct timeval *timeout; 3237c478bd9Sstevel@tonic-gate int cmd; 3247c478bd9Sstevel@tonic-gate char *buf; 3257c478bd9Sstevel@tonic-gate size_t len; 3267c478bd9Sstevel@tonic-gate { 327*e9af4bc0SJohn Beck size_t sl; 3287c478bd9Sstevel@tonic-gate ssize_t l; 3297c478bd9Sstevel@tonic-gate mi_int32 nl; 3307c478bd9Sstevel@tonic-gate int iovcnt; 3317c478bd9Sstevel@tonic-gate struct iovec iov[2]; 3327c478bd9Sstevel@tonic-gate char data[MILTER_LEN_BYTES + 1]; 3337c478bd9Sstevel@tonic-gate 3347c478bd9Sstevel@tonic-gate if (len > Maxdatasize || (len > 0 && buf == NULL)) 3357c478bd9Sstevel@tonic-gate return MI_FAILURE; 3367c478bd9Sstevel@tonic-gate 3377c478bd9Sstevel@tonic-gate nl = htonl(len + 1); /* add 1 for the cmd char */ 3387c478bd9Sstevel@tonic-gate (void) memcpy(data, (void *) &nl, MILTER_LEN_BYTES); 3397c478bd9Sstevel@tonic-gate data[MILTER_LEN_BYTES] = (char) cmd; 3407c478bd9Sstevel@tonic-gate sl = MILTER_LEN_BYTES + 1; 3417c478bd9Sstevel@tonic-gate 3427c478bd9Sstevel@tonic-gate /* set up the vector for the size / command */ 3437c478bd9Sstevel@tonic-gate iov[0].iov_base = (void *) data; 3447c478bd9Sstevel@tonic-gate iov[0].iov_len = sl; 3457c478bd9Sstevel@tonic-gate iovcnt = 1; 3467c478bd9Sstevel@tonic-gate if (len >= 0 && buf != NULL) 3477c478bd9Sstevel@tonic-gate { 3487c478bd9Sstevel@tonic-gate iov[1].iov_base = (void *) buf; 3497c478bd9Sstevel@tonic-gate iov[1].iov_len = len; 3507c478bd9Sstevel@tonic-gate iovcnt = 2; 3517c478bd9Sstevel@tonic-gate } 3527c478bd9Sstevel@tonic-gate 3537c478bd9Sstevel@tonic-gate l = retry_writev(sd, iov, iovcnt, timeout); 3547c478bd9Sstevel@tonic-gate if (l == MI_FAILURE) 3557c478bd9Sstevel@tonic-gate return MI_FAILURE; 3567c478bd9Sstevel@tonic-gate return MI_SUCCESS; 3577c478bd9Sstevel@tonic-gate } 358