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