1 /* 2 * Copyright (c) 1999-2002 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.54.2.2 2002/08/16 17:09:13 ca Exp $") 13 14 #include "libmilter.h" 15 #include <sm/errstring.h> 16 17 #define FD_Z FD_ZERO(&readset); \ 18 FD_SET((unsigned int) sd, &readset); \ 19 FD_ZERO(&excset); \ 20 FD_SET((unsigned int) sd, &excset) 21 22 /* 23 ** MI_RD_CMD -- read a command 24 ** 25 ** Parameters: 26 ** sd -- socket descriptor 27 ** timeout -- maximum time to wait 28 ** cmd -- single character command read from sd 29 ** rlen -- pointer to length of result 30 ** name -- name of milter 31 ** 32 ** Returns: 33 ** buffer with rest of command 34 ** (malloc()ed here, should be free()d) 35 ** hack: encode error in cmd 36 */ 37 38 char * 39 mi_rd_cmd(sd, timeout, cmd, rlen, name) 40 socket_t sd; 41 struct timeval *timeout; 42 char *cmd; 43 size_t *rlen; 44 char *name; 45 { 46 ssize_t len; 47 mi_int32 expl; 48 ssize_t i; 49 fd_set readset, excset; 50 int ret; 51 int save_errno; 52 char *buf; 53 char data[MILTER_LEN_BYTES + 1]; 54 55 *cmd = '\0'; 56 *rlen = 0; 57 58 if (sd >= FD_SETSIZE) 59 { 60 smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d", 61 name, sd, FD_SETSIZE); 62 *cmd = SMFIC_SELECT; 63 return NULL; 64 } 65 66 i = 0; 67 for (;;) 68 { 69 FD_Z; 70 ret = select(sd + 1, &readset, NULL, &excset, timeout); 71 if (ret == 0) 72 break; 73 else if (ret < 0) 74 { 75 if (errno == EINTR) 76 continue; 77 break; 78 } 79 if (FD_ISSET(sd, &excset)) 80 { 81 *cmd = SMFIC_SELECT; 82 return NULL; 83 } 84 85 len = MI_SOCK_READ(sd, data + i, sizeof data - i); 86 if (MI_SOCK_READ_FAIL(len)) 87 { 88 smi_log(SMI_LOG_ERR, 89 "%s, mi_rd_cmd: read returned %d: %s", 90 name, len, sm_errstring(errno)); 91 *cmd = SMFIC_RECVERR; 92 return NULL; 93 } 94 if (len == 0) 95 { 96 *cmd = SMFIC_EOF; 97 return NULL; 98 } 99 if (len >= (ssize_t) sizeof data - i) 100 break; 101 i += len; 102 } 103 if (ret == 0) 104 { 105 *cmd = SMFIC_TIMEOUT; 106 return NULL; 107 } 108 else if (ret < 0) 109 { 110 smi_log(SMI_LOG_ERR, 111 "%s: mi_rd_cmd: select returned %d: %s", 112 name, ret, sm_errstring(errno)); 113 *cmd = SMFIC_RECVERR; 114 return NULL; 115 } 116 117 *cmd = data[MILTER_LEN_BYTES]; 118 data[MILTER_LEN_BYTES] = '\0'; 119 (void) memcpy((void *) &expl, (void *) &(data[0]), MILTER_LEN_BYTES); 120 expl = ntohl(expl) - 1; 121 if (expl <= 0) 122 return NULL; 123 if (expl > MILTER_CHUNK_SIZE) 124 { 125 *cmd = SMFIC_TOOBIG; 126 return NULL; 127 } 128 #if _FFR_ADD_NULL 129 buf = malloc(expl + 1); 130 #else /* _FFR_ADD_NULL */ 131 buf = malloc(expl); 132 #endif /* _FFR_ADD_NULL */ 133 if (buf == NULL) 134 { 135 *cmd = SMFIC_MALLOC; 136 return NULL; 137 } 138 139 i = 0; 140 for (;;) 141 { 142 FD_Z; 143 ret = select(sd + 1, &readset, NULL, &excset, timeout); 144 if (ret == 0) 145 break; 146 else if (ret < 0) 147 { 148 if (errno == EINTR) 149 continue; 150 break; 151 } 152 if (FD_ISSET(sd, &excset)) 153 { 154 *cmd = SMFIC_SELECT; 155 free(buf); 156 return NULL; 157 } 158 len = MI_SOCK_READ(sd, buf + i, expl - i); 159 if (MI_SOCK_READ_FAIL(len)) 160 { 161 smi_log(SMI_LOG_ERR, 162 "%s: mi_rd_cmd: read returned %d: %s", 163 name, len, sm_errstring(errno)); 164 ret = -1; 165 break; 166 } 167 if (len == 0) 168 { 169 *cmd = SMFIC_EOF; 170 free(buf); 171 return NULL; 172 } 173 if (len > expl - i) 174 { 175 *cmd = SMFIC_RECVERR; 176 free(buf); 177 return NULL; 178 } 179 if (len >= expl - i) 180 { 181 *rlen = expl; 182 #if _FFR_ADD_NULL 183 /* makes life simpler for common string routines */ 184 buf[expl] = '\0'; 185 #endif /* _FFR_ADD_NULL */ 186 return buf; 187 } 188 i += len; 189 } 190 191 save_errno = errno; 192 free(buf); 193 194 /* select returned 0 (timeout) or < 0 (error) */ 195 if (ret == 0) 196 { 197 *cmd = SMFIC_TIMEOUT; 198 return NULL; 199 } 200 if (ret < 0) 201 { 202 smi_log(SMI_LOG_ERR, 203 "%s: mi_rd_cmd: select returned %d: %s", 204 name, ret, sm_errstring(save_errno)); 205 *cmd = SMFIC_RECVERR; 206 return NULL; 207 } 208 *cmd = SMFIC_UNKNERR; 209 return NULL; 210 } 211 /* 212 ** MI_WR_CMD -- write a cmd to sd 213 ** 214 ** Parameters: 215 ** sd -- socket descriptor 216 ** timeout -- maximum time to wait (currently unused) 217 ** cmd -- single character command to write 218 ** buf -- buffer with further data 219 ** len -- length of buffer (without cmd!) 220 ** 221 ** Returns: 222 ** MI_SUCCESS/MI_FAILURE 223 */ 224 225 /* 226 ** we don't care much about the timeout here, it's very long anyway 227 ** FD_SETSIZE is only checked in mi_rd_cmd. 228 ** XXX l == 0 ? 229 */ 230 231 #define MI_WR(data) \ 232 while (sl > 0) \ 233 { \ 234 FD_ZERO(&wrtset); \ 235 FD_SET((unsigned int) sd, &wrtset); \ 236 ret = select(sd + 1, NULL, &wrtset, NULL, timeout); \ 237 if (ret == 0) \ 238 return MI_FAILURE; \ 239 if (ret < 0) \ 240 { \ 241 if (errno == EINTR) \ 242 continue; \ 243 else \ 244 return MI_FAILURE; \ 245 } \ 246 l = MI_SOCK_WRITE(sd, (void *) ((data) + i), sl); \ 247 if (l < 0) \ 248 { \ 249 if (errno == EINTR) \ 250 continue; \ 251 else \ 252 return MI_FAILURE; \ 253 } \ 254 i += l; \ 255 sl -= l; \ 256 } 257 258 int 259 mi_wr_cmd(sd, timeout, cmd, buf, len) 260 socket_t sd; 261 struct timeval *timeout; 262 int cmd; 263 char *buf; 264 size_t len; 265 { 266 size_t sl, i; 267 ssize_t l; 268 mi_int32 nl; 269 int ret; 270 fd_set wrtset; 271 char data[MILTER_LEN_BYTES + 1]; 272 273 if (len > MILTER_CHUNK_SIZE) 274 return MI_FAILURE; 275 nl = htonl(len + 1); /* add 1 for the cmd char */ 276 (void) memcpy(data, (void *) &nl, MILTER_LEN_BYTES); 277 data[MILTER_LEN_BYTES] = (char) cmd; 278 i = 0; 279 sl = MILTER_LEN_BYTES + 1; 280 281 /* use writev() instead to send the whole stuff at once? */ 282 283 MI_WR(data); 284 if (len > 0 && buf == NULL) 285 return MI_FAILURE; 286 if (len == 0 || buf == NULL) 287 return MI_SUCCESS; 288 i = 0; 289 sl = len; 290 MI_WR(buf); 291 return MI_SUCCESS; 292 } 293