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: smfi.c,v 8.57 2001/11/20 18:47:49 ca Exp $") 13 #include <sm/varargs.h> 14 #include "libmilter.h" 15 16 /* for smfi_set{ml}reply, let's be generous. 256/16 should be sufficient */ 17 #define MAXREPLYLEN 980 /* max. length of a reply string */ 18 #define MAXREPLIES 32 /* max. number of reply strings */ 19 20 /* 21 ** SMFI_ADDHEADER -- send a new header to the MTA 22 ** 23 ** Parameters: 24 ** ctx -- Opaque context structure 25 ** headerf -- Header field name 26 ** headerv -- Header field value 27 ** 28 ** Returns: 29 ** MI_SUCCESS/MI_FAILURE 30 */ 31 32 int 33 smfi_addheader(ctx, headerf, headerv) 34 SMFICTX *ctx; 35 char *headerf; 36 char *headerv; 37 { 38 /* do we want to copy the stuff or have a special mi_wr_cmd call? */ 39 size_t len, l1, l2; 40 int r; 41 char *buf; 42 struct timeval timeout; 43 44 if (headerf == NULL || *headerf == '\0' || headerv == NULL) 45 return MI_FAILURE; 46 if (!mi_sendok(ctx, SMFIF_ADDHDRS)) 47 return MI_FAILURE; 48 timeout.tv_sec = ctx->ctx_timeout; 49 timeout.tv_usec = 0; 50 l1 = strlen(headerf); 51 l2 = strlen(headerv); 52 len = l1 + l2 + 2; 53 buf = malloc(len); 54 if (buf == NULL) 55 return MI_FAILURE; 56 (void) memcpy(buf, headerf, l1 + 1); 57 (void) memcpy(buf + l1 + 1, headerv, l2 + 1); 58 r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDHEADER, buf, len); 59 free(buf); 60 return r; 61 } 62 63 /* 64 ** SMFI_CHGHEADER -- send a changed header to the MTA 65 ** 66 ** Parameters: 67 ** ctx -- Opaque context structure 68 ** headerf -- Header field name 69 ** hdridx -- Header index value 70 ** headerv -- Header field value 71 ** 72 ** Returns: 73 ** MI_SUCCESS/MI_FAILURE 74 */ 75 76 int 77 smfi_chgheader(ctx, headerf, hdridx, headerv) 78 SMFICTX *ctx; 79 char *headerf; 80 mi_int32 hdridx; 81 char *headerv; 82 { 83 /* do we want to copy the stuff or have a special mi_wr_cmd call? */ 84 size_t len, l1, l2; 85 int r; 86 mi_int32 v; 87 char *buf; 88 struct timeval timeout; 89 90 if (headerf == NULL || *headerf == '\0') 91 return MI_FAILURE; 92 if (hdridx < 0) 93 return MI_FAILURE; 94 if (!mi_sendok(ctx, SMFIF_CHGHDRS)) 95 return MI_FAILURE; 96 timeout.tv_sec = ctx->ctx_timeout; 97 timeout.tv_usec = 0; 98 if (headerv == NULL) 99 headerv = ""; 100 l1 = strlen(headerf); 101 l2 = strlen(headerv); 102 len = l1 + l2 + 2 + MILTER_LEN_BYTES; 103 buf = malloc(len); 104 if (buf == NULL) 105 return MI_FAILURE; 106 v = htonl(hdridx); 107 (void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES); 108 (void) memcpy(buf + MILTER_LEN_BYTES, headerf, l1 + 1); 109 (void) memcpy(buf + MILTER_LEN_BYTES + l1 + 1, headerv, l2 + 1); 110 r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_CHGHEADER, buf, len); 111 free(buf); 112 return r; 113 } 114 /* 115 ** SMFI_ADDRCPT -- send an additional recipient to the MTA 116 ** 117 ** Parameters: 118 ** ctx -- Opaque context structure 119 ** rcpt -- recipient address 120 ** 121 ** Returns: 122 ** MI_SUCCESS/MI_FAILURE 123 */ 124 125 int 126 smfi_addrcpt(ctx, rcpt) 127 SMFICTX *ctx; 128 char *rcpt; 129 { 130 size_t len; 131 struct timeval timeout; 132 133 if (rcpt == NULL || *rcpt == '\0') 134 return MI_FAILURE; 135 if (!mi_sendok(ctx, SMFIF_ADDRCPT)) 136 return MI_FAILURE; 137 timeout.tv_sec = ctx->ctx_timeout; 138 timeout.tv_usec = 0; 139 len = strlen(rcpt) + 1; 140 return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len); 141 } 142 /* 143 ** SMFI_DELRCPT -- send a recipient to be removed to the MTA 144 ** 145 ** Parameters: 146 ** ctx -- Opaque context structure 147 ** rcpt -- recipient address 148 ** 149 ** Returns: 150 ** MI_SUCCESS/MI_FAILURE 151 */ 152 153 int 154 smfi_delrcpt(ctx, rcpt) 155 SMFICTX *ctx; 156 char *rcpt; 157 { 158 size_t len; 159 struct timeval timeout; 160 161 if (rcpt == NULL || *rcpt == '\0') 162 return MI_FAILURE; 163 if (!mi_sendok(ctx, SMFIF_DELRCPT)) 164 return MI_FAILURE; 165 timeout.tv_sec = ctx->ctx_timeout; 166 timeout.tv_usec = 0; 167 len = strlen(rcpt) + 1; 168 return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len); 169 } 170 /* 171 ** SMFI_REPLACEBODY -- send a body chunk to the MTA 172 ** 173 ** Parameters: 174 ** ctx -- Opaque context structure 175 ** bodyp -- body chunk 176 ** bodylen -- length of body chunk 177 ** 178 ** Returns: 179 ** MI_SUCCESS/MI_FAILURE 180 */ 181 182 int 183 smfi_replacebody(ctx, bodyp, bodylen) 184 SMFICTX *ctx; 185 unsigned char *bodyp; 186 int bodylen; 187 { 188 int len, off, r; 189 struct timeval timeout; 190 191 if (bodylen < 0 || 192 (bodyp == NULL && bodylen > 0)) 193 return MI_FAILURE; 194 if (!mi_sendok(ctx, SMFIF_CHGBODY)) 195 return MI_FAILURE; 196 timeout.tv_sec = ctx->ctx_timeout; 197 timeout.tv_usec = 0; 198 199 /* split body chunk if necessary */ 200 off = 0; 201 while (bodylen > 0) 202 { 203 len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE : 204 bodylen; 205 if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY, 206 (char *) (bodyp + off), len)) != MI_SUCCESS) 207 return r; 208 off += len; 209 bodylen -= len; 210 } 211 return MI_SUCCESS; 212 } 213 #if _FFR_QUARANTINE 214 /* 215 ** SMFI_QUARANTINE -- quarantine an envelope 216 ** 217 ** Parameters: 218 ** ctx -- Opaque context structure 219 ** reason -- why? 220 ** 221 ** Returns: 222 ** MI_SUCCESS/MI_FAILURE 223 */ 224 225 int 226 smfi_quarantine(ctx, reason) 227 SMFICTX *ctx; 228 char *reason; 229 { 230 size_t len; 231 int r; 232 char *buf; 233 struct timeval timeout; 234 235 if (reason == NULL || *reason == '\0') 236 return MI_FAILURE; 237 if (!mi_sendok(ctx, SMFIF_QUARANTINE)) 238 return MI_FAILURE; 239 timeout.tv_sec = ctx->ctx_timeout; 240 timeout.tv_usec = 0; 241 len = strlen(reason) + 1; 242 buf = malloc(len); 243 if (buf == NULL) 244 return MI_FAILURE; 245 (void) memcpy(buf, reason, len); 246 r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len); 247 free(buf); 248 return r; 249 } 250 #endif /* _FFR_QUARANTINE */ 251 252 /* 253 ** MYISENHSC -- check whether a string contains an enhanced status code 254 ** 255 ** Parameters: 256 ** s -- string with possible enhanced status code. 257 ** delim -- delim for enhanced status code. 258 ** 259 ** Returns: 260 ** 0 -- no enhanced status code. 261 ** >4 -- length of enhanced status code. 262 ** 263 ** Side Effects: 264 ** none. 265 */ 266 static int 267 myisenhsc(s, delim) 268 const char *s; 269 int delim; 270 { 271 int l, h; 272 273 if (s == NULL) 274 return 0; 275 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) 276 return 0; 277 h = 0; 278 l = 2; 279 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 280 ++h; 281 if (h == 0 || s[l + h] != '.') 282 return 0; 283 l += h + 1; 284 h = 0; 285 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) 286 ++h; 287 if (h == 0 || s[l + h] != delim) 288 return 0; 289 return l + h; 290 } 291 292 /* 293 ** SMFI_SETREPLY -- set the reply code for the next reply to the MTA 294 ** 295 ** Parameters: 296 ** ctx -- Opaque context structure 297 ** rcode -- The three-digit (RFC 821) SMTP reply code. 298 ** xcode -- The extended (RFC 2034) reply code. 299 ** message -- The text part of the SMTP reply. 300 ** 301 ** Returns: 302 ** MI_SUCCESS/MI_FAILURE 303 */ 304 305 int 306 smfi_setreply(ctx, rcode, xcode, message) 307 SMFICTX *ctx; 308 char *rcode; 309 char *xcode; 310 char *message; 311 { 312 size_t len; 313 char *buf; 314 315 if (rcode == NULL || ctx == NULL) 316 return MI_FAILURE; 317 318 /* ### <sp> \0 */ 319 len = strlen(rcode) + 2; 320 if (len != 5) 321 return MI_FAILURE; 322 if ((rcode[0] != '4' && rcode[0] != '5') || 323 !isascii(rcode[1]) || !isdigit(rcode[1]) || 324 !isascii(rcode[2]) || !isdigit(rcode[2])) 325 return MI_FAILURE; 326 if (xcode != NULL) 327 { 328 if (!myisenhsc(xcode, '\0')) 329 return MI_FAILURE; 330 len += strlen(xcode) + 1; 331 } 332 if (message != NULL) 333 { 334 size_t ml; 335 336 /* XXX check also for unprintable chars? */ 337 if (strpbrk(message, "\r\n") != NULL) 338 return MI_FAILURE; 339 ml = strlen(message); 340 if (ml > MAXREPLYLEN) 341 return MI_FAILURE; 342 len += ml + 1; 343 } 344 buf = malloc(len); 345 if (buf == NULL) 346 return MI_FAILURE; /* oops */ 347 (void) sm_strlcpy(buf, rcode, len); 348 (void) sm_strlcat(buf, " ", len); 349 if (xcode != NULL) 350 (void) sm_strlcat(buf, xcode, len); 351 if (message != NULL) 352 { 353 if (xcode != NULL) 354 (void) sm_strlcat(buf, " ", len); 355 (void) sm_strlcat(buf, message, len); 356 } 357 if (ctx->ctx_reply != NULL) 358 free(ctx->ctx_reply); 359 ctx->ctx_reply = buf; 360 return MI_SUCCESS; 361 } 362 363 #if _FFR_MULTILINE 364 /* 365 ** SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA 366 ** 367 ** Parameters: 368 ** ctx -- Opaque context structure 369 ** rcode -- The three-digit (RFC 821) SMTP reply code. 370 ** xcode -- The extended (RFC 2034) reply code. 371 ** txt, ... -- The text part of the SMTP reply, 372 ** MUST be terminated with NULL. 373 ** 374 ** Returns: 375 ** MI_SUCCESS/MI_FAILURE 376 */ 377 378 int 379 #if SM_VA_STD 380 smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...) 381 #else /* SM_VA_STD */ 382 smfi_setmlreply(ctx, rcode, xcode, va_alist) 383 SMFICTX *ctx; 384 const char *rcode; 385 const char *xcode; 386 va_dcl 387 #endif /* SM_VA_STD */ 388 { 389 size_t len; 390 size_t rlen; 391 int args; 392 char *buf, *txt; 393 const char *xc; 394 char repl[16]; 395 SM_VA_LOCAL_DECL 396 397 if (rcode == NULL || ctx == NULL) 398 return MI_FAILURE; 399 400 /* ### <sp> */ 401 len = strlen(rcode) + 1; 402 if (len != 4) 403 return MI_FAILURE; 404 if ((rcode[0] != '4' && rcode[0] != '5') || 405 !isascii(rcode[1]) || !isdigit(rcode[1]) || 406 !isascii(rcode[2]) || !isdigit(rcode[2])) 407 return MI_FAILURE; 408 if (xcode != NULL) 409 { 410 if (!myisenhsc(xcode, '\0')) 411 return MI_FAILURE; 412 xc = xcode; 413 } 414 else 415 { 416 if (rcode[0] == '4') 417 xc = "4.0.0"; 418 else 419 xc = "5.0.0"; 420 } 421 422 /* add trailing space */ 423 len += strlen(xc) + 1; 424 rlen = len; 425 args = 0; 426 SM_VA_START(ap, xcode); 427 while ((txt = SM_VA_ARG(ap, char *)) != NULL) 428 { 429 size_t tl; 430 431 tl = strlen(txt); 432 if (tl > MAXREPLYLEN) 433 return MI_FAILURE; 434 435 /* this text, reply codes, \r\n */ 436 len += tl + 2 + rlen; 437 if (++args > MAXREPLIES) 438 return MI_FAILURE; 439 440 /* XXX check also for unprintable chars? */ 441 if (strpbrk(txt, "\r\n") != NULL) 442 return MI_FAILURE; 443 } 444 SM_VA_END(ap); 445 446 /* trailing '\0' */ 447 ++len; 448 buf = malloc(len); 449 if (buf == NULL) 450 return MI_FAILURE; /* oops */ 451 (void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc); 452 (void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-", 453 xc, " "); 454 SM_VA_START(ap, xcode); 455 txt = SM_VA_ARG(ap, char *); 456 if (txt != NULL) 457 { 458 (void) sm_strlcat2(buf, " ", txt, len); 459 while ((txt = SM_VA_ARG(ap, char *)) != NULL) 460 { 461 if (--args <= 1) 462 repl[3] = ' '; 463 (void) sm_strlcat2(buf, "\r\n", repl, len); 464 (void) sm_strlcat(buf, txt, len); 465 } 466 } 467 if (ctx->ctx_reply != NULL) 468 free(ctx->ctx_reply); 469 ctx->ctx_reply = buf; 470 SM_VA_END(ap); 471 return MI_SUCCESS; 472 } 473 #endif /* _FFR_MULTILINE */ 474 475 /* 476 ** SMFI_SETPRIV -- set private data 477 ** 478 ** Parameters: 479 ** ctx -- Opaque context structure 480 ** privatedata -- pointer to private data 481 ** 482 ** Returns: 483 ** MI_SUCCESS/MI_FAILURE 484 */ 485 486 int 487 smfi_setpriv(ctx, privatedata) 488 SMFICTX *ctx; 489 void *privatedata; 490 { 491 if (ctx == NULL) 492 return MI_FAILURE; 493 ctx->ctx_privdata = privatedata; 494 return MI_SUCCESS; 495 } 496 /* 497 ** SMFI_GETPRIV -- get private data 498 ** 499 ** Parameters: 500 ** ctx -- Opaque context structure 501 ** 502 ** Returns: 503 ** pointer to private data 504 */ 505 506 void * 507 smfi_getpriv(ctx) 508 SMFICTX *ctx; 509 { 510 if (ctx == NULL) 511 return NULL; 512 return ctx->ctx_privdata; 513 } 514 /* 515 ** SMFI_GETSYMVAL -- get the value of a macro 516 ** 517 ** See explanation in mfapi.h about layout of the structures. 518 ** 519 ** Parameters: 520 ** ctx -- Opaque context structure 521 ** symname -- name of macro 522 ** 523 ** Returns: 524 ** value of macro (NULL in case of failure) 525 */ 526 527 char * 528 smfi_getsymval(ctx, symname) 529 SMFICTX *ctx; 530 char *symname; 531 { 532 int i; 533 char **s; 534 char one[2]; 535 char braces[4]; 536 537 if (ctx == NULL || symname == NULL || *symname == '\0') 538 return NULL; 539 540 if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}') 541 { 542 one[0] = symname[1]; 543 one[1] = '\0'; 544 } 545 else 546 one[0] = '\0'; 547 if (strlen(symname) == 1) 548 { 549 braces[0] = '{'; 550 braces[1] = *symname; 551 braces[2] = '}'; 552 braces[3] = '\0'; 553 } 554 else 555 braces[0] = '\0'; 556 557 /* search backwards through the macro array */ 558 for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i) 559 { 560 if ((s = ctx->ctx_mac_ptr[i]) == NULL || 561 ctx->ctx_mac_buf[i] == NULL) 562 continue; 563 while (s != NULL && *s != NULL) 564 { 565 if (strcmp(*s, symname) == 0) 566 return *++s; 567 if (one[0] != '\0' && strcmp(*s, one) == 0) 568 return *++s; 569 if (braces[0] != '\0' && strcmp(*s, braces) == 0) 570 return *++s; 571 ++s; /* skip over macro value */ 572 ++s; /* points to next macro name */ 573 } 574 } 575 return NULL; 576 } 577