comm.c revision 261363
1/* 2 * Copyright (c) 1999-2004, 2009 Proofpoint, Inc. and its suppliers. 3 * All rights reserved. 4 * 5 * By using this file, you agree to the terms and conditions set 6 * forth in the LICENSE file which can be found at the top level of 7 * the sendmail distribution. 8 * 9 */ 10 11#include <sm/gen.h> 12SM_RCSID("@(#)$Id: comm.c,v 8.71 2013/11/22 20:51:36 ca Exp $") 13 14#include "libmilter.h" 15#include <sm/errstring.h> 16#include <sys/uio.h> 17 18static ssize_t retry_writev __P((socket_t, struct iovec *, int, struct timeval *)); 19static size_t Maxdatasize = MILTER_MAX_DATA_SIZE; 20 21/* 22** SMFI_SETMAXDATASIZE -- set limit for milter data read/write. 23** 24** Parameters: 25** sz -- new limit. 26** 27** Returns: 28** old limit 29*/ 30 31size_t 32smfi_setmaxdatasize(sz) 33 size_t sz; 34{ 35 size_t old; 36 37 old = Maxdatasize; 38 Maxdatasize = sz; 39 return old; 40} 41 42/* 43** MI_RD_CMD -- read a command 44** 45** Parameters: 46** sd -- socket descriptor 47** timeout -- maximum time to wait 48** cmd -- single character command read from sd 49** rlen -- pointer to length of result 50** name -- name of milter 51** 52** Returns: 53** buffer with rest of command 54** (malloc()ed here, should be free()d) 55** hack: encode error in cmd 56*/ 57 58char * 59mi_rd_cmd(sd, timeout, cmd, rlen, name) 60 socket_t sd; 61 struct timeval *timeout; 62 char *cmd; 63 size_t *rlen; 64 char *name; 65{ 66 ssize_t len; 67 mi_int32 expl; 68 ssize_t i; 69 FD_RD_VAR(rds, excs); 70 int ret; 71 int save_errno; 72 char *buf; 73 char data[MILTER_LEN_BYTES + 1]; 74 75 *cmd = '\0'; 76 *rlen = 0; 77 78 i = 0; 79 for (;;) 80 { 81 FD_RD_INIT(sd, rds, excs); 82 ret = FD_RD_READY(sd, rds, excs, timeout); 83 if (ret == 0) 84 break; 85 else if (ret < 0) 86 { 87 if (errno == EINTR) 88 continue; 89 break; 90 } 91 if (FD_IS_RD_EXC(sd, rds, excs)) 92 { 93 *cmd = SMFIC_SELECT; 94 return NULL; 95 } 96 97 len = MI_SOCK_READ(sd, data + i, sizeof data - i); 98 if (MI_SOCK_READ_FAIL(len)) 99 { 100 smi_log(SMI_LOG_ERR, 101 "%s, mi_rd_cmd: read returned %d: %s", 102 name, (int) len, sm_errstring(errno)); 103 *cmd = SMFIC_RECVERR; 104 return NULL; 105 } 106 if (len == 0) 107 { 108 *cmd = SMFIC_EOF; 109 return NULL; 110 } 111 if (len >= (ssize_t) sizeof data - i) 112 break; 113 i += len; 114 } 115 if (ret == 0) 116 { 117 *cmd = SMFIC_TIMEOUT; 118 return NULL; 119 } 120 else if (ret < 0) 121 { 122 smi_log(SMI_LOG_ERR, 123 "%s: mi_rd_cmd: %s() returned %d: %s", 124 name, MI_POLLSELECT, ret, sm_errstring(errno)); 125 *cmd = SMFIC_RECVERR; 126 return NULL; 127 } 128 129 *cmd = data[MILTER_LEN_BYTES]; 130 data[MILTER_LEN_BYTES] = '\0'; 131 (void) memcpy((void *) &expl, (void *) &(data[0]), MILTER_LEN_BYTES); 132 expl = ntohl(expl) - 1; 133 if (expl <= 0) 134 return NULL; 135 if (expl > Maxdatasize) 136 { 137 *cmd = SMFIC_TOOBIG; 138 return NULL; 139 } 140#if _FFR_ADD_NULL 141 buf = malloc(expl + 1); 142#else /* _FFR_ADD_NULL */ 143 buf = malloc(expl); 144#endif /* _FFR_ADD_NULL */ 145 if (buf == NULL) 146 { 147 *cmd = SMFIC_MALLOC; 148 return NULL; 149 } 150 151 i = 0; 152 for (;;) 153 { 154 FD_RD_INIT(sd, rds, excs); 155 ret = FD_RD_READY(sd, rds, excs, timeout); 156 if (ret == 0) 157 break; 158 else if (ret < 0) 159 { 160 if (errno == EINTR) 161 continue; 162 break; 163 } 164 if (FD_IS_RD_EXC(sd, rds, excs)) 165 { 166 *cmd = SMFIC_SELECT; 167 free(buf); 168 return NULL; 169 } 170 len = MI_SOCK_READ(sd, buf + i, expl - i); 171 if (MI_SOCK_READ_FAIL(len)) 172 { 173 smi_log(SMI_LOG_ERR, 174 "%s: mi_rd_cmd: read returned %d: %s", 175 name, (int) len, sm_errstring(errno)); 176 ret = -1; 177 break; 178 } 179 if (len == 0) 180 { 181 *cmd = SMFIC_EOF; 182 free(buf); 183 return NULL; 184 } 185 if (len > expl - i) 186 { 187 *cmd = SMFIC_RECVERR; 188 free(buf); 189 return NULL; 190 } 191 if (len >= expl - i) 192 { 193 *rlen = expl; 194#if _FFR_ADD_NULL 195 /* makes life simpler for common string routines */ 196 buf[expl] = '\0'; 197#endif /* _FFR_ADD_NULL */ 198 return buf; 199 } 200 i += len; 201 } 202 203 save_errno = errno; 204 free(buf); 205 206 /* select returned 0 (timeout) or < 0 (error) */ 207 if (ret == 0) 208 { 209 *cmd = SMFIC_TIMEOUT; 210 return NULL; 211 } 212 if (ret < 0) 213 { 214 smi_log(SMI_LOG_ERR, 215 "%s: mi_rd_cmd: %s() returned %d: %s", 216 name, MI_POLLSELECT, ret, sm_errstring(save_errno)); 217 *cmd = SMFIC_RECVERR; 218 return NULL; 219 } 220 *cmd = SMFIC_UNKNERR; 221 return NULL; 222} 223 224/* 225** RETRY_WRITEV -- Keep calling the writev() system call 226** until all the data is written out or an error occurs. 227** 228** Parameters: 229** fd -- socket descriptor 230** iov -- io vector 231** iovcnt -- number of elements in io vector 232** must NOT exceed UIO_MAXIOV. 233** timeout -- maximum time to wait 234** 235** Returns: 236** success: number of bytes written 237** otherwise: MI_FAILURE 238*/ 239 240static ssize_t 241retry_writev(fd, iov, iovcnt, timeout) 242 socket_t fd; 243 struct iovec *iov; 244 int iovcnt; 245 struct timeval *timeout; 246{ 247 int i; 248 ssize_t n, written; 249 FD_WR_VAR(wrs); 250 251 written = 0; 252 for (;;) 253 { 254 while (iovcnt > 0 && iov[0].iov_len == 0) 255 { 256 iov++; 257 iovcnt--; 258 } 259 if (iovcnt <= 0) 260 return written; 261 262 /* 263 ** We don't care much about the timeout here, 264 ** it's very long anyway; correct solution would be 265 ** to take the time before the loop and reduce the 266 ** timeout after each invocation. 267 ** FD_SETSIZE is checked when socket is created. 268 */ 269 270 FD_WR_INIT(fd, wrs); 271 i = FD_WR_READY(fd, wrs, timeout); 272 if (i == 0) 273 return MI_FAILURE; 274 if (i < 0) 275 { 276 if (errno == EINTR || errno == EAGAIN) 277 continue; 278 return MI_FAILURE; 279 } 280 n = writev(fd, iov, iovcnt); 281 if (n == -1) 282 { 283 if (errno == EINTR || errno == EAGAIN) 284 continue; 285 return MI_FAILURE; 286 } 287 288 written += n; 289 for (i = 0; i < iovcnt; i++) 290 { 291 if (iov[i].iov_len > (unsigned int) n) 292 { 293 iov[i].iov_base = (char *)iov[i].iov_base + n; 294 iov[i].iov_len -= (unsigned int) n; 295 break; 296 } 297 n -= (int) iov[i].iov_len; 298 iov[i].iov_len = 0; 299 } 300 if (i == iovcnt) 301 return written; 302 } 303} 304 305/* 306** MI_WR_CMD -- write a cmd to sd 307** 308** Parameters: 309** sd -- socket descriptor 310** timeout -- maximum time to wait 311** cmd -- single character command to write 312** buf -- buffer with further data 313** len -- length of buffer (without cmd!) 314** 315** Returns: 316** MI_SUCCESS/MI_FAILURE 317*/ 318 319int 320mi_wr_cmd(sd, timeout, cmd, buf, len) 321 socket_t sd; 322 struct timeval *timeout; 323 int cmd; 324 char *buf; 325 size_t len; 326{ 327 size_t sl; 328 ssize_t l; 329 mi_int32 nl; 330 int iovcnt; 331 struct iovec iov[2]; 332 char data[MILTER_LEN_BYTES + 1]; 333 334 if (len > Maxdatasize || (len > 0 && buf == NULL)) 335 return MI_FAILURE; 336 337 nl = htonl(len + 1); /* add 1 for the cmd char */ 338 (void) memcpy(data, (void *) &nl, MILTER_LEN_BYTES); 339 data[MILTER_LEN_BYTES] = (char) cmd; 340 sl = MILTER_LEN_BYTES + 1; 341 342 /* set up the vector for the size / command */ 343 iov[0].iov_base = (void *) data; 344 iov[0].iov_len = sl; 345 iovcnt = 1; 346 if (len >= 0 && buf != NULL) 347 { 348 iov[1].iov_base = (void *) buf; 349 iov[1].iov_len = len; 350 iovcnt = 2; 351 } 352 353 l = retry_writev(sd, iov, iovcnt, timeout); 354 if (l == MI_FAILURE) 355 return MI_FAILURE; 356 return MI_SUCCESS; 357} 358