164562Sgshapiro/* 2261194Sgshapiro * Copyright (c) 1999-2004, 2009 Proofpoint, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 464562Sgshapiro * 564562Sgshapiro * By using this file, you agree to the terms and conditions set 664562Sgshapiro * forth in the LICENSE file which can be found at the top level of 764562Sgshapiro * the sendmail distribution. 864562Sgshapiro * 964562Sgshapiro */ 1064562Sgshapiro 1190792Sgshapiro#include <sm/gen.h> 12266527SgshapiroSM_RCSID("@(#)$Id: comm.c,v 8.71 2013-11-22 20:51:36 ca Exp $") 1364562Sgshapiro 1464562Sgshapiro#include "libmilter.h" 1590792Sgshapiro#include <sm/errstring.h> 16132943Sgshapiro#include <sys/uio.h> 1764562Sgshapiro 18141858Sgshapirostatic ssize_t retry_writev __P((socket_t, struct iovec *, int, struct timeval *)); 19132943Sgshapirostatic size_t Maxdatasize = MILTER_MAX_DATA_SIZE; 20132943Sgshapiro 2164562Sgshapiro/* 22132943Sgshapiro** SMFI_SETMAXDATASIZE -- set limit for milter data read/write. 23132943Sgshapiro** 24132943Sgshapiro** Parameters: 25132943Sgshapiro** sz -- new limit. 26132943Sgshapiro** 27132943Sgshapiro** Returns: 28132943Sgshapiro** old limit 29132943Sgshapiro*/ 30132943Sgshapiro 31132943Sgshapirosize_t 32132943Sgshapirosmfi_setmaxdatasize(sz) 33132943Sgshapiro size_t sz; 34132943Sgshapiro{ 35132943Sgshapiro size_t old; 36132943Sgshapiro 37132943Sgshapiro old = Maxdatasize; 38132943Sgshapiro Maxdatasize = sz; 39132943Sgshapiro return old; 40132943Sgshapiro} 41132943Sgshapiro 42132943Sgshapiro/* 4364562Sgshapiro** MI_RD_CMD -- read a command 4464562Sgshapiro** 4564562Sgshapiro** Parameters: 4664562Sgshapiro** sd -- socket descriptor 4764562Sgshapiro** timeout -- maximum time to wait 4864562Sgshapiro** cmd -- single character command read from sd 4964562Sgshapiro** rlen -- pointer to length of result 5064562Sgshapiro** name -- name of milter 5164562Sgshapiro** 5264562Sgshapiro** Returns: 5364562Sgshapiro** buffer with rest of command 5464562Sgshapiro** (malloc()ed here, should be free()d) 5564562Sgshapiro** hack: encode error in cmd 5664562Sgshapiro*/ 5764562Sgshapiro 5864562Sgshapirochar * 5964562Sgshapiromi_rd_cmd(sd, timeout, cmd, rlen, name) 6064562Sgshapiro socket_t sd; 6164562Sgshapiro struct timeval *timeout; 6264562Sgshapiro char *cmd; 6364562Sgshapiro size_t *rlen; 6464562Sgshapiro char *name; 6564562Sgshapiro{ 6664562Sgshapiro ssize_t len; 6764562Sgshapiro mi_int32 expl; 6864562Sgshapiro ssize_t i; 69111823Sgshapiro FD_RD_VAR(rds, excs); 7064562Sgshapiro int ret; 7164562Sgshapiro int save_errno; 7264562Sgshapiro char *buf; 7364562Sgshapiro char data[MILTER_LEN_BYTES + 1]; 7464562Sgshapiro 7564562Sgshapiro *cmd = '\0'; 7664562Sgshapiro *rlen = 0; 7771345Sgshapiro 7864562Sgshapiro i = 0; 79102528Sgshapiro for (;;) 8064562Sgshapiro { 81111823Sgshapiro FD_RD_INIT(sd, rds, excs); 82111823Sgshapiro ret = FD_RD_READY(sd, rds, excs, timeout); 83102528Sgshapiro if (ret == 0) 84102528Sgshapiro break; 85102528Sgshapiro else if (ret < 0) 86102528Sgshapiro { 87102528Sgshapiro if (errno == EINTR) 88102528Sgshapiro continue; 89102528Sgshapiro break; 90102528Sgshapiro } 91111823Sgshapiro if (FD_IS_RD_EXC(sd, rds, excs)) 9264562Sgshapiro { 9364562Sgshapiro *cmd = SMFIC_SELECT; 9464562Sgshapiro return NULL; 9564562Sgshapiro } 9690792Sgshapiro 9790792Sgshapiro len = MI_SOCK_READ(sd, data + i, sizeof data - i); 9890792Sgshapiro if (MI_SOCK_READ_FAIL(len)) 9964562Sgshapiro { 10064562Sgshapiro smi_log(SMI_LOG_ERR, 10164562Sgshapiro "%s, mi_rd_cmd: read returned %d: %s", 102110560Sgshapiro name, (int) len, sm_errstring(errno)); 10364562Sgshapiro *cmd = SMFIC_RECVERR; 10464562Sgshapiro return NULL; 10564562Sgshapiro } 10664562Sgshapiro if (len == 0) 10764562Sgshapiro { 10864562Sgshapiro *cmd = SMFIC_EOF; 10964562Sgshapiro return NULL; 11064562Sgshapiro } 11164562Sgshapiro if (len >= (ssize_t) sizeof data - i) 11264562Sgshapiro break; 11364562Sgshapiro i += len; 11464562Sgshapiro } 11564562Sgshapiro if (ret == 0) 11664562Sgshapiro { 11764562Sgshapiro *cmd = SMFIC_TIMEOUT; 11864562Sgshapiro return NULL; 11964562Sgshapiro } 12064562Sgshapiro else if (ret < 0) 12164562Sgshapiro { 12264562Sgshapiro smi_log(SMI_LOG_ERR, 123203004Sgshapiro "%s: mi_rd_cmd: %s() returned %d: %s", 124203004Sgshapiro name, MI_POLLSELECT, ret, sm_errstring(errno)); 12564562Sgshapiro *cmd = SMFIC_RECVERR; 12664562Sgshapiro return NULL; 12764562Sgshapiro } 12864562Sgshapiro 12964562Sgshapiro *cmd = data[MILTER_LEN_BYTES]; 13064562Sgshapiro data[MILTER_LEN_BYTES] = '\0'; 13164562Sgshapiro (void) memcpy((void *) &expl, (void *) &(data[0]), MILTER_LEN_BYTES); 13264562Sgshapiro expl = ntohl(expl) - 1; 13364562Sgshapiro if (expl <= 0) 13464562Sgshapiro return NULL; 135132943Sgshapiro if (expl > Maxdatasize) 13664562Sgshapiro { 13764562Sgshapiro *cmd = SMFIC_TOOBIG; 13864562Sgshapiro return NULL; 13964562Sgshapiro } 14090792Sgshapiro#if _FFR_ADD_NULL 14190792Sgshapiro buf = malloc(expl + 1); 142363466Sgshapiro#else 14364562Sgshapiro buf = malloc(expl); 144363466Sgshapiro#endif 14564562Sgshapiro if (buf == NULL) 14664562Sgshapiro { 14764562Sgshapiro *cmd = SMFIC_MALLOC; 14864562Sgshapiro return NULL; 14964562Sgshapiro } 15064562Sgshapiro 15164562Sgshapiro i = 0; 152102528Sgshapiro for (;;) 15364562Sgshapiro { 154111823Sgshapiro FD_RD_INIT(sd, rds, excs); 155111823Sgshapiro ret = FD_RD_READY(sd, rds, excs, timeout); 156102528Sgshapiro if (ret == 0) 157102528Sgshapiro break; 158102528Sgshapiro else if (ret < 0) 159102528Sgshapiro { 160102528Sgshapiro if (errno == EINTR) 161102528Sgshapiro continue; 162102528Sgshapiro break; 163102528Sgshapiro } 164111823Sgshapiro if (FD_IS_RD_EXC(sd, rds, excs)) 16564562Sgshapiro { 16664562Sgshapiro *cmd = SMFIC_SELECT; 16764562Sgshapiro free(buf); 16864562Sgshapiro return NULL; 16964562Sgshapiro } 17090792Sgshapiro len = MI_SOCK_READ(sd, buf + i, expl - i); 17190792Sgshapiro if (MI_SOCK_READ_FAIL(len)) 17264562Sgshapiro { 17364562Sgshapiro smi_log(SMI_LOG_ERR, 17464562Sgshapiro "%s: mi_rd_cmd: read returned %d: %s", 175110560Sgshapiro name, (int) len, sm_errstring(errno)); 17664562Sgshapiro ret = -1; 17764562Sgshapiro break; 17864562Sgshapiro } 17964562Sgshapiro if (len == 0) 18064562Sgshapiro { 18164562Sgshapiro *cmd = SMFIC_EOF; 18264562Sgshapiro free(buf); 18364562Sgshapiro return NULL; 18464562Sgshapiro } 18564562Sgshapiro if (len > expl - i) 18664562Sgshapiro { 18764562Sgshapiro *cmd = SMFIC_RECVERR; 18864562Sgshapiro free(buf); 18964562Sgshapiro return NULL; 19064562Sgshapiro } 19164562Sgshapiro if (len >= expl - i) 19264562Sgshapiro { 19364562Sgshapiro *rlen = expl; 19490792Sgshapiro#if _FFR_ADD_NULL 19590792Sgshapiro /* makes life simpler for common string routines */ 19690792Sgshapiro buf[expl] = '\0'; 197363466Sgshapiro#endif 19864562Sgshapiro return buf; 19964562Sgshapiro } 20064562Sgshapiro i += len; 20164562Sgshapiro } 20264562Sgshapiro 20364562Sgshapiro save_errno = errno; 20464562Sgshapiro free(buf); 20564562Sgshapiro 20664562Sgshapiro /* select returned 0 (timeout) or < 0 (error) */ 20764562Sgshapiro if (ret == 0) 20864562Sgshapiro { 20964562Sgshapiro *cmd = SMFIC_TIMEOUT; 21064562Sgshapiro return NULL; 21164562Sgshapiro } 21264562Sgshapiro if (ret < 0) 21364562Sgshapiro { 21464562Sgshapiro smi_log(SMI_LOG_ERR, 215203004Sgshapiro "%s: mi_rd_cmd: %s() returned %d: %s", 216203004Sgshapiro name, MI_POLLSELECT, ret, sm_errstring(save_errno)); 21764562Sgshapiro *cmd = SMFIC_RECVERR; 21864562Sgshapiro return NULL; 21964562Sgshapiro } 22064562Sgshapiro *cmd = SMFIC_UNKNERR; 22164562Sgshapiro return NULL; 22264562Sgshapiro} 223132943Sgshapiro 22490792Sgshapiro/* 225132943Sgshapiro** RETRY_WRITEV -- Keep calling the writev() system call 226132943Sgshapiro** until all the data is written out or an error occurs. 227132943Sgshapiro** 228132943Sgshapiro** Parameters: 229132943Sgshapiro** fd -- socket descriptor 230132943Sgshapiro** iov -- io vector 231132943Sgshapiro** iovcnt -- number of elements in io vector 232132943Sgshapiro** must NOT exceed UIO_MAXIOV. 233132943Sgshapiro** timeout -- maximum time to wait 234132943Sgshapiro** 235132943Sgshapiro** Returns: 236132943Sgshapiro** success: number of bytes written 237132943Sgshapiro** otherwise: MI_FAILURE 238132943Sgshapiro*/ 239132943Sgshapiro 240132943Sgshapirostatic ssize_t 241132943Sgshapiroretry_writev(fd, iov, iovcnt, timeout) 242132943Sgshapiro socket_t fd; 243132943Sgshapiro struct iovec *iov; 244132943Sgshapiro int iovcnt; 245132943Sgshapiro struct timeval *timeout; 246132943Sgshapiro{ 247132943Sgshapiro int i; 248132943Sgshapiro ssize_t n, written; 249132943Sgshapiro FD_WR_VAR(wrs); 250132943Sgshapiro 251132943Sgshapiro written = 0; 252132943Sgshapiro for (;;) 253132943Sgshapiro { 254132943Sgshapiro while (iovcnt > 0 && iov[0].iov_len == 0) 255132943Sgshapiro { 256132943Sgshapiro iov++; 257132943Sgshapiro iovcnt--; 258132943Sgshapiro } 259132943Sgshapiro if (iovcnt <= 0) 260132943Sgshapiro return written; 261132943Sgshapiro 262132943Sgshapiro /* 263132943Sgshapiro ** We don't care much about the timeout here, 264132943Sgshapiro ** it's very long anyway; correct solution would be 265132943Sgshapiro ** to take the time before the loop and reduce the 266132943Sgshapiro ** timeout after each invocation. 267132943Sgshapiro ** FD_SETSIZE is checked when socket is created. 268132943Sgshapiro */ 269132943Sgshapiro 270132943Sgshapiro FD_WR_INIT(fd, wrs); 271132943Sgshapiro i = FD_WR_READY(fd, wrs, timeout); 272132943Sgshapiro if (i == 0) 273132943Sgshapiro return MI_FAILURE; 274132943Sgshapiro if (i < 0) 275132943Sgshapiro { 276132943Sgshapiro if (errno == EINTR || errno == EAGAIN) 277132943Sgshapiro continue; 278132943Sgshapiro return MI_FAILURE; 279132943Sgshapiro } 280132943Sgshapiro n = writev(fd, iov, iovcnt); 281132943Sgshapiro if (n == -1) 282132943Sgshapiro { 283132943Sgshapiro if (errno == EINTR || errno == EAGAIN) 284132943Sgshapiro continue; 285132943Sgshapiro return MI_FAILURE; 286132943Sgshapiro } 287132943Sgshapiro 288132943Sgshapiro written += n; 289132943Sgshapiro for (i = 0; i < iovcnt; i++) 290132943Sgshapiro { 291132943Sgshapiro if (iov[i].iov_len > (unsigned int) n) 292132943Sgshapiro { 293132943Sgshapiro iov[i].iov_base = (char *)iov[i].iov_base + n; 294132943Sgshapiro iov[i].iov_len -= (unsigned int) n; 295132943Sgshapiro break; 296132943Sgshapiro } 297132943Sgshapiro n -= (int) iov[i].iov_len; 298132943Sgshapiro iov[i].iov_len = 0; 299132943Sgshapiro } 300132943Sgshapiro if (i == iovcnt) 301132943Sgshapiro return written; 302132943Sgshapiro } 303132943Sgshapiro} 304132943Sgshapiro 305132943Sgshapiro/* 30664562Sgshapiro** MI_WR_CMD -- write a cmd to sd 30764562Sgshapiro** 30864562Sgshapiro** Parameters: 30964562Sgshapiro** sd -- socket descriptor 310132943Sgshapiro** timeout -- maximum time to wait 31164562Sgshapiro** cmd -- single character command to write 31264562Sgshapiro** buf -- buffer with further data 31364562Sgshapiro** len -- length of buffer (without cmd!) 31464562Sgshapiro** 31564562Sgshapiro** Returns: 31664562Sgshapiro** MI_SUCCESS/MI_FAILURE 31764562Sgshapiro*/ 31864562Sgshapiro 31964562Sgshapiroint 32064562Sgshapiromi_wr_cmd(sd, timeout, cmd, buf, len) 32164562Sgshapiro socket_t sd; 32264562Sgshapiro struct timeval *timeout; 32364562Sgshapiro int cmd; 32464562Sgshapiro char *buf; 32564562Sgshapiro size_t len; 32664562Sgshapiro{ 327203004Sgshapiro size_t sl; 32864562Sgshapiro ssize_t l; 32964562Sgshapiro mi_int32 nl; 330132943Sgshapiro int iovcnt; 331132943Sgshapiro struct iovec iov[2]; 33264562Sgshapiro char data[MILTER_LEN_BYTES + 1]; 33364562Sgshapiro 334132943Sgshapiro if (len > Maxdatasize || (len > 0 && buf == NULL)) 33564562Sgshapiro return MI_FAILURE; 336132943Sgshapiro 33764562Sgshapiro nl = htonl(len + 1); /* add 1 for the cmd char */ 33864562Sgshapiro (void) memcpy(data, (void *) &nl, MILTER_LEN_BYTES); 33964562Sgshapiro data[MILTER_LEN_BYTES] = (char) cmd; 34064562Sgshapiro sl = MILTER_LEN_BYTES + 1; 34164562Sgshapiro 342132943Sgshapiro /* set up the vector for the size / command */ 343132943Sgshapiro iov[0].iov_base = (void *) data; 344132943Sgshapiro iov[0].iov_len = sl; 345132943Sgshapiro iovcnt = 1; 346132943Sgshapiro if (len >= 0 && buf != NULL) 347132943Sgshapiro { 348132943Sgshapiro iov[1].iov_base = (void *) buf; 349132943Sgshapiro iov[1].iov_len = len; 350132943Sgshapiro iovcnt = 2; 351132943Sgshapiro } 352168515Sgshapiro 353132943Sgshapiro l = retry_writev(sd, iov, iovcnt, timeout); 354132943Sgshapiro if (l == MI_FAILURE) 35564562Sgshapiro return MI_FAILURE; 35664562Sgshapiro return MI_SUCCESS; 35764562Sgshapiro} 358