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