1 /* 2 * Copyright (c) 1999-2001 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.48 2001/11/07 17:43:04 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 FD_Z; 67 i = 0; 68 while ((ret = select(sd + 1, &readset, NULL, &excset, timeout)) >= 1) 69 { 70 if (FD_ISSET(sd, &excset)) 71 { 72 *cmd = SMFIC_SELECT; 73 return NULL; 74 } 75 76 len = MI_SOCK_READ(sd, data + i, sizeof data - i); 77 if (MI_SOCK_READ_FAIL(len)) 78 { 79 smi_log(SMI_LOG_ERR, 80 "%s, mi_rd_cmd: read returned %d: %s", 81 name, len, sm_errstring(errno)); 82 *cmd = SMFIC_RECVERR; 83 return NULL; 84 } 85 if (len == 0) 86 { 87 *cmd = SMFIC_EOF; 88 return NULL; 89 } 90 if (len >= (ssize_t) sizeof data - i) 91 break; 92 i += len; 93 FD_Z; 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 FD_Z; 133 while ((ret = select(sd + 1, &readset, NULL, &excset, timeout)) == 1) 134 { 135 if (FD_ISSET(sd, &excset)) 136 { 137 *cmd = SMFIC_SELECT; 138 free(buf); 139 return NULL; 140 } 141 len = MI_SOCK_READ(sd, buf + i, expl - i); 142 if (MI_SOCK_READ_FAIL(len)) 143 { 144 smi_log(SMI_LOG_ERR, 145 "%s: mi_rd_cmd: read returned %d: %s", 146 name, len, sm_errstring(errno)); 147 ret = -1; 148 break; 149 } 150 if (len == 0) 151 { 152 *cmd = SMFIC_EOF; 153 free(buf); 154 return NULL; 155 } 156 if (len > expl - i) 157 { 158 *cmd = SMFIC_RECVERR; 159 free(buf); 160 return NULL; 161 } 162 if (len >= expl - i) 163 { 164 *rlen = expl; 165 #if _FFR_ADD_NULL 166 /* makes life simpler for common string routines */ 167 buf[expl] = '\0'; 168 #endif /* _FFR_ADD_NULL */ 169 return buf; 170 } 171 i += len; 172 FD_Z; 173 } 174 175 save_errno = errno; 176 free(buf); 177 178 /* select returned 0 (timeout) or < 0 (error) */ 179 if (ret == 0) 180 { 181 *cmd = SMFIC_TIMEOUT; 182 return NULL; 183 } 184 if (ret < 0) 185 { 186 smi_log(SMI_LOG_ERR, 187 "%s: mi_rd_cmd: select returned %d: %s", 188 name, ret, sm_errstring(save_errno)); 189 *cmd = SMFIC_RECVERR; 190 return NULL; 191 } 192 *cmd = SMFIC_UNKNERR; 193 return NULL; 194 } 195 /* 196 ** MI_WR_CMD -- write a cmd to sd 197 ** 198 ** Parameters: 199 ** sd -- socket descriptor 200 ** timeout -- maximum time to wait (currently unused) 201 ** cmd -- single character command to write 202 ** buf -- buffer with further data 203 ** len -- length of buffer (without cmd!) 204 ** 205 ** Returns: 206 ** MI_SUCCESS/MI_FAILURE 207 */ 208 209 int 210 mi_wr_cmd(sd, timeout, cmd, buf, len) 211 socket_t sd; 212 struct timeval *timeout; 213 int cmd; 214 char *buf; 215 size_t len; 216 { 217 size_t sl, i; 218 ssize_t l; 219 mi_int32 nl; 220 int ret; 221 fd_set wrtset; 222 char data[MILTER_LEN_BYTES + 1]; 223 224 if (len > MILTER_CHUNK_SIZE) 225 return MI_FAILURE; 226 nl = htonl(len + 1); /* add 1 for the cmd char */ 227 (void) memcpy(data, (void *) &nl, MILTER_LEN_BYTES); 228 data[MILTER_LEN_BYTES] = (char) cmd; 229 i = 0; 230 sl = MILTER_LEN_BYTES + 1; 231 232 do 233 { 234 FD_ZERO(&wrtset); 235 FD_SET((unsigned int) sd, &wrtset); 236 if ((ret = select(sd + 1, NULL, &wrtset, NULL, timeout)) == 0) 237 return MI_FAILURE; 238 } while (ret < 0 && errno == EINTR); 239 if (ret < 0) 240 return MI_FAILURE; 241 242 /* use writev() instead to send the whole stuff at once? */ 243 while ((l = MI_SOCK_WRITE(sd, (void *) (data + i), 244 sl - i)) < (ssize_t) sl) 245 { 246 if (l < 0) 247 return MI_FAILURE; 248 i += l; 249 sl -= l; 250 } 251 252 if (len > 0 && buf == NULL) 253 return MI_FAILURE; 254 if (len == 0 || buf == NULL) 255 return MI_SUCCESS; 256 i = 0; 257 sl = len; 258 do 259 { 260 FD_ZERO(&wrtset); 261 FD_SET((unsigned int) sd, &wrtset); 262 if ((ret = select(sd + 1, NULL, &wrtset, NULL, timeout)) == 0) 263 return MI_FAILURE; 264 } while (ret < 0 && errno == EINTR); 265 if (ret < 0) 266 return MI_FAILURE; 267 while ((l = MI_SOCK_WRITE(sd, (void *) (buf + i), 268 sl - i)) < (ssize_t) sl) 269 { 270 if (l < 0) 271 return MI_FAILURE; 272 i += l; 273 sl -= l; 274 } 275 return MI_SUCCESS; 276 } 277