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