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