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