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