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