1 /*
2 * Copyright (c) 1998-2007, 2009 Proofpoint, Inc. and its suppliers.
3 * All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
5 * Copyright (c) 1988, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14 #include <sendmail.h>
15
16 SM_RCSID("@(#)$Id: util.c,v 8.427 2013-11-22 20:51:57 ca Exp $")
17
18 #include <sm/sendmail.h>
19 #include <sm/xtrap.h>
20 #if USE_EAI
21 # include <sm/ixlen.h>
22 #endif
23
24 /*
25 ** NEWSTR -- Create a copy of a C string
26 **
27 ** Parameters:
28 ** s -- the string to copy. [A]
29 **
30 ** Returns:
31 ** pointer to newly allocated string.
32 */
33
34 char *
35 #if SM_HEAP_CHECK > 2
36 newstr_tagged(s, tag, line, group)
37 const char *s;
38 char *tag;
39 int line;
40 int group;
41 #else
42 newstr(s)
43 const char *s;
44 # define tag "newstr"
45 # define line 0
46 # define group 0
47 #endif
48 {
49 size_t l;
50 char *n;
51
52 l = strlen(s);
53 SM_ASSERT(l + 1 > l);
54 n = sm_malloc_tagged_x(l + 1, tag, line, group);
55 sm_strlcpy(n, s, l + 1);
56 return n;
57 }
58 #if SM_HEAP_CHECK <= 2
59 # undef tag
60 # undef line
61 # undef group
62 #endif
63
64 /*
65 ** ADDQUOTES -- Adds quotes & quote bits to a string.
66 **
67 ** Runs through a string and adds backslashes and quote bits.
68 **
69 ** Parameters:
70 ** s -- the string to modify. [A]
71 ** rpool -- resource pool from which to allocate result
72 **
73 ** Returns:
74 ** pointer to quoted string.
75 */
76
77 char *
addquotes(s,rpool)78 addquotes(s, rpool)
79 char *s;
80 SM_RPOOL_T *rpool;
81 {
82 int len = 0;
83 char c;
84 char *p = s, *q, *r;
85
86 if (s == NULL)
87 return NULL;
88
89 /* Find length of quoted string */
90 while ((c = *p++) != '\0')
91 {
92 len++;
93 if (c == '\\' || c == '"')
94 len++;
95 }
96
97 q = r = sm_rpool_malloc_x(rpool, len + 3);
98 p = s;
99
100 /* add leading quote */
101 *q++ = '"';
102 while ((c = *p++) != '\0')
103 {
104 /* quote \ or " */
105 if (c == '\\' || c == '"')
106 *q++ = '\\';
107 *q++ = c;
108 }
109 *q++ = '"';
110 *q = '\0';
111 return r;
112 }
113
114 /*
115 ** STRIPBACKSLASH -- Strip all leading backslashes from a string, provided
116 ** the following character is alpha-numerical.
117 ** This is done in place.
118 **
119 ** XXX: This may be a problem for EAI?
120 **
121 ** Parameters:
122 ** s -- the string to strip.
123 **
124 ** Returns:
125 ** none.
126 */
127
128 void
stripbackslash(s)129 stripbackslash(s)
130 char *s;
131 {
132 char *p, *q, c;
133
134 if (SM_IS_EMPTY(s))
135 return;
136 p = q = s;
137 while (*p == '\\' && (p[1] == '\\' || (isascii(p[1]) && isalnum(p[1]))))
138 p++;
139 do
140 {
141 c = *q++ = *p++;
142 } while (c != '\0');
143 }
144
145 /*
146 ** RFC822_STRING -- Checks string for proper RFC822 string quoting.
147 **
148 ** Runs through a string and verifies RFC822 special characters
149 ** are only found inside comments, quoted strings, or backslash
150 ** escaped. Also verified balanced quotes and parenthesis.
151 **
152 ** XXX: This may be a problem for EAI? MustQuoteChars is used.
153 ** If this returns false, current callers just invoke addquotes().
154 **
155 ** Parameters:
156 ** s -- the string to modify. [A]
157 **
158 ** Returns:
159 ** true iff the string is RFC822 compliant, false otherwise.
160 */
161
162 bool
rfc822_string(s)163 rfc822_string(s)
164 char *s;
165 {
166 bool quoted = false;
167 int commentlev = 0;
168 char *c = s;
169
170 if (s == NULL)
171 return false;
172
173 while (*c != '\0')
174 {
175 /* escaped character */
176 if (*c == '\\')
177 {
178 c++;
179 if (*c == '\0')
180 return false;
181 }
182 else if (commentlev == 0 && *c == '"')
183 quoted = !quoted;
184 else if (!quoted)
185 {
186 if (*c == ')')
187 {
188 /* unbalanced ')' */
189 if (commentlev == 0)
190 return false;
191 else
192 commentlev--;
193 }
194 else if (*c == '(')
195 commentlev++;
196 else if (commentlev == 0 &&
197 strchr(MustQuoteChars, *c) != NULL)
198 return false;
199 }
200 c++;
201 }
202
203 /* unbalanced '"' or '(' */
204 return !quoted && commentlev == 0;
205 }
206
207 /*
208 ** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
209 **
210 ** Arbitrarily shorten (in place) an RFC822 string and rebalance
211 ** comments and quotes.
212 **
213 ** Parameters:
214 ** string -- the string to shorten [A]
215 ** length -- the maximum size, 0 if no maximum
216 **
217 ** Returns:
218 ** true if string is changed, false otherwise
219 **
220 ** Side Effects:
221 ** Changes string in place, possibly resulting
222 ** in a shorter string.
223 */
224
225 bool
shorten_rfc822_string(string,length)226 shorten_rfc822_string(string, length)
227 char *string;
228 size_t length;
229 {
230 bool backslash = false;
231 bool modified = false;
232 bool quoted = false;
233 size_t slen;
234 int parencount = 0;
235 char *ptr = string;
236
237 /*
238 ** If have to rebalance an already short enough string,
239 ** need to do it within allocated space.
240 */
241
242 slen = strlen(string);
243 if (length == 0 || slen < length)
244 length = slen;
245
246 while (*ptr != '\0')
247 {
248 if (backslash)
249 {
250 backslash = false;
251 goto increment;
252 }
253
254 if (*ptr == '\\')
255 backslash = true;
256 else if (*ptr == '(')
257 {
258 if (!quoted)
259 parencount++;
260 }
261 else if (*ptr == ')')
262 {
263 if (--parencount < 0)
264 parencount = 0;
265 }
266
267 /* Inside a comment, quotes don't matter */
268 if (parencount <= 0 && *ptr == '"')
269 quoted = !quoted;
270
271 increment:
272 /* Check for sufficient space for next character */
273 if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) +
274 parencount +
275 (quoted ? 1 : 0)))
276 {
277 /* Not enough, backtrack */
278 if (*ptr == '\\')
279 backslash = false;
280 else if (*ptr == '(' && !quoted)
281 parencount--;
282 else if (*ptr == '"' && parencount == 0)
283 quoted = false;
284 break;
285 }
286 ptr++;
287 }
288
289 /* Rebalance */
290 while (parencount-- > 0)
291 {
292 if (*ptr != ')')
293 {
294 modified = true;
295 *ptr = ')';
296 }
297 ptr++;
298 }
299 if (quoted)
300 {
301 if (*ptr != '"')
302 {
303 modified = true;
304 *ptr = '"';
305 }
306 ptr++;
307 }
308 if (*ptr != '\0')
309 {
310 modified = true;
311 *ptr = '\0';
312 }
313 return modified;
314 }
315
316 /*
317 ** FIND_CHARACTER -- find an unquoted character in an RFC822 string
318 **
319 ** Find an unquoted, non-commented character in an RFC822
320 ** string and return a pointer to its location in the string.
321 **
322 ** Parameters:
323 ** string -- the string to search [A]
324 ** character -- the character to find
325 **
326 ** Returns:
327 ** pointer to the character, or
328 ** a pointer to the end of the line if character is not found
329 */
330
331 char *
find_character(string,character)332 find_character(string, character)
333 char *string;
334 int character;
335 {
336 bool backslash = false;
337 bool quoted = false;
338 int parencount = 0;
339
340 while (string != NULL && *string != '\0')
341 {
342 if (backslash)
343 {
344 backslash = false;
345 if (!quoted && character == '\\' && *string == '\\')
346 break;
347 string++;
348 continue;
349 }
350 switch (*string)
351 {
352 case '\\':
353 backslash = true;
354 break;
355
356 case '(':
357 if (!quoted)
358 parencount++;
359 break;
360
361 case ')':
362 if (--parencount < 0)
363 parencount = 0;
364 break;
365 }
366
367 /* Inside a comment, nothing matters */
368 if (parencount > 0)
369 {
370 string++;
371 continue;
372 }
373
374 if (*string == '"')
375 quoted = !quoted;
376 else if (*string == character && !quoted)
377 break;
378 string++;
379 }
380
381 /* Return pointer to the character */
382 return string;
383 }
384
385 /*
386 ** CHECK_BODYTYPE -- check bodytype parameter
387 **
388 ** Parameters:
389 ** bodytype -- bodytype parameter
390 **
391 ** Returns:
392 ** BODYTYPE_* according to parameter
393 **
394 */
395
396 int
check_bodytype(bodytype)397 check_bodytype(bodytype)
398 char *bodytype;
399 {
400 /* check body type for legality */
401 if (bodytype == NULL)
402 return BODYTYPE_NONE;
403 if (SM_STRCASEEQ(bodytype, "7bit"))
404 return BODYTYPE_7BIT;
405 if (SM_STRCASEEQ(bodytype, "8bitmime"))
406 return BODYTYPE_8BITMIME;
407 return BODYTYPE_ILLEGAL;
408 }
409
410 /*
411 ** TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..."
412 **
413 ** Parameters:
414 ** str -- string to truncate [A]
415 ** len -- maximum length (including '\0') (0 for unlimited)
416 ** delim -- delimiter character
417 **
418 ** Returns:
419 ** None.
420 */
421
422 void
truncate_at_delim(str,len,delim)423 truncate_at_delim(str, len, delim)
424 char *str;
425 size_t len;
426 int delim;
427 {
428 char *p;
429
430 if (str == NULL || len == 0 || strlen(str) < len)
431 return;
432
433 *(str + len - 1) = '\0';
434 while ((p = strrchr(str, delim)) != NULL)
435 {
436 *p = '\0';
437 if (p - str + 4 < len)
438 {
439 *p++ = (char) delim;
440 *p = '\0';
441 (void) sm_strlcat(str, "...", len);
442 return;
443 }
444 }
445
446 /* Couldn't find a place to append "..." */
447 if (len > 3)
448 (void) sm_strlcpy(str, "...", len);
449 else
450 str[0] = '\0';
451 }
452
453 /*
454 ** XALLOC -- Allocate memory, raise an exception on error
455 **
456 ** Parameters:
457 ** sz -- size of area to allocate.
458 **
459 ** Returns:
460 ** pointer to data region.
461 **
462 ** Exceptions:
463 ** SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory
464 **
465 ** Side Effects:
466 ** Memory is allocated.
467 */
468
469 char *
470 #if SM_HEAP_CHECK
xalloc_tagged(sz,file,line)471 xalloc_tagged(sz, file, line)
472 register int sz;
473 char *file;
474 int line;
475 #else /* SM_HEAP_CHECK */
476 xalloc(sz)
477 register int sz;
478 #endif /* SM_HEAP_CHECK */
479 {
480 register char *p;
481
482 SM_REQUIRE(sz >= 0);
483
484 /* some systems can't handle size zero mallocs */
485 if (sz <= 0)
486 sz = 1;
487
488 /* scaffolding for testing error handling code */
489 sm_xtrap_raise_x(&SmHeapOutOfMemory);
490
491 p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group());
492 if (p == NULL)
493 {
494 sm_exc_raise_x(&SmHeapOutOfMemory);
495 }
496 return p;
497 }
498
499 /*
500 ** COPYPLIST -- copy list of pointers.
501 **
502 ** This routine is the equivalent of strdup for lists of
503 ** pointers.
504 **
505 ** Parameters:
506 ** list -- list of pointers to copy.
507 ** Must be NULL terminated.
508 ** copycont -- if true, copy the contents of the vector
509 ** (which must be a string) also.
510 ** rpool -- resource pool from which to allocate storage,
511 ** or NULL
512 **
513 ** Returns:
514 ** a copy of 'list'.
515 */
516
517 char **
copyplist(list,copycont,rpool)518 copyplist(list, copycont, rpool)
519 char **list;
520 bool copycont;
521 SM_RPOOL_T *rpool;
522 {
523 register char **vp;
524 register char **newvp;
525
526 for (vp = list; *vp != NULL; vp++)
527 continue;
528
529 vp++;
530
531 /*
532 ** Hack: rpool is NULL if invoked from readcf(),
533 ** so "ignore" the allocation by setting group to 0.
534 */
535
536 newvp = (char **) sm_rpool_malloc_tagged_x(rpool,
537 (vp - list) * sizeof(*vp), "copyplist", 0,
538 NULL == rpool ? 0 : 1);
539 memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof(*vp));
540
541 if (copycont)
542 {
543 for (vp = newvp; *vp != NULL; vp++)
544 *vp = sm_rpool_strdup_tagged_x(rpool, *vp,
545 "copyplist", 0, NULL == rpool ? 0 : 1);
546 }
547
548 return newvp;
549 }
550
551 /*
552 ** COPYQUEUE -- copy address queue.
553 **
554 ** This routine is the equivalent of strdup for address queues;
555 ** addresses marked as QS_IS_DEAD() aren't copied
556 **
557 ** Parameters:
558 ** addr -- list of address structures to copy.
559 ** rpool -- resource pool from which to allocate storage
560 **
561 ** Returns:
562 ** a copy of 'addr'.
563 */
564
565 ADDRESS *
copyqueue(addr,rpool)566 copyqueue(addr, rpool)
567 ADDRESS *addr;
568 SM_RPOOL_T *rpool;
569 {
570 register ADDRESS *newaddr;
571 ADDRESS *ret;
572 register ADDRESS **tail = &ret;
573
574 while (addr != NULL)
575 {
576 if (!QS_IS_DEAD(addr->q_state))
577 {
578 newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool,
579 sizeof(*newaddr));
580 STRUCTCOPY(*addr, *newaddr);
581 *tail = newaddr;
582 tail = &newaddr->q_next;
583 }
584 addr = addr->q_next;
585 }
586 *tail = NULL;
587
588 return ret;
589 }
590
591 /*
592 ** LOG_SENDMAIL_PID -- record sendmail pid and command line.
593 **
594 ** Parameters:
595 ** e -- the current envelope.
596 **
597 ** Returns:
598 ** none.
599 **
600 ** Side Effects:
601 ** writes pidfile, logs command line.
602 ** keeps file open and locked to prevent overwrite of active file
603 */
604
605 static SM_FILE_T *Pidf = NULL;
606
607 void
log_sendmail_pid(e)608 log_sendmail_pid(e)
609 ENVELOPE *e;
610 {
611 long sff;
612 char pidpath[MAXPATHLEN];
613 extern char *CommandLineArgs;
614
615 /* write the pid to the log file for posterity */
616 sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT|SFF_NBLOCK;
617 if (TrustedUid != 0 && RealUid == TrustedUid)
618 sff |= SFF_OPENASROOT;
619 expand(PidFile, pidpath, sizeof(pidpath), e);
620 Pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, FileMode, sff);
621 if (Pidf == NULL)
622 {
623 if (errno == EWOULDBLOCK)
624 sm_syslog(LOG_ERR, NOQID,
625 "unable to write pid to %s: file in use by another process",
626 pidpath);
627 else
628 sm_syslog(LOG_ERR, NOQID,
629 "unable to write pid to %s: %s",
630 pidpath, sm_errstring(errno));
631 }
632 else
633 {
634 PidFilePid = getpid();
635
636 /* write the process id on line 1 */
637 (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%ld\n",
638 (long) PidFilePid);
639
640 /* line 2 contains all command line flags */
641 (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%s\n",
642 CommandLineArgs);
643
644 /* flush */
645 (void) sm_io_flush(Pidf, SM_TIME_DEFAULT);
646
647 /*
648 ** Leave pid file open until process ends
649 ** so it's not overwritten by another
650 ** process.
651 */
652 }
653 if (LogLevel > 9)
654 sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs);
655 }
656
657 /*
658 ** CLOSE_SENDMAIL_PID -- close sendmail pid file
659 **
660 ** Parameters:
661 ** none.
662 **
663 ** Returns:
664 ** none.
665 */
666
667 void
close_sendmail_pid()668 close_sendmail_pid()
669 {
670 if (Pidf == NULL)
671 return;
672
673 (void) sm_io_close(Pidf, SM_TIME_DEFAULT);
674 Pidf = NULL;
675 }
676
677 /*
678 ** SET_DELIVERY_MODE -- set and record the delivery mode
679 **
680 ** Parameters:
681 ** mode -- delivery mode
682 ** e -- the current envelope.
683 **
684 ** Returns:
685 ** none.
686 **
687 ** Side Effects:
688 ** sets {deliveryMode} macro
689 */
690
691 void
set_delivery_mode(mode,e)692 set_delivery_mode(mode, e)
693 int mode;
694 ENVELOPE *e;
695 {
696 char buf[2];
697
698 e->e_sendmode = (char) mode;
699 buf[0] = (char) mode;
700 buf[1] = '\0';
701 macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf);
702 }
703
704 /*
705 ** SET_OP_MODE -- set and record the op mode
706 **
707 ** Parameters:
708 ** mode -- op mode
709 ** e -- the current envelope.
710 **
711 ** Returns:
712 ** none.
713 **
714 ** Side Effects:
715 ** sets {opMode} macro
716 */
717
718 void
set_op_mode(mode)719 set_op_mode(mode)
720 int mode;
721 {
722 char buf[2];
723 extern ENVELOPE BlankEnvelope;
724
725 OpMode = (char) mode;
726 buf[0] = (char) mode;
727 buf[1] = '\0';
728 macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf);
729 }
730
731 /*
732 ** PRINTAV -- print argument vector.
733 **
734 ** Parameters:
735 ** fp -- output file pointer.
736 ** av -- argument vector.
737 **
738 ** Returns:
739 ** none.
740 **
741 ** Side Effects:
742 ** prints av.
743 */
744
745 void
printav(fp,av)746 printav(fp, av)
747 SM_FILE_T *fp;
748 char **av;
749 {
750 while (*av != NULL)
751 {
752 #if _FFR_8BITENVADDR
753 if (tTd(0, 9))
754 {
755 char *cp;
756 unsigned char v;
757
758 for (cp = *av++; *cp != '\0'; cp++) {
759 v = (unsigned char)(*cp & 0x00ff);
760 (void) sm_io_putc(fp, SM_TIME_DEFAULT, v);
761 # if 0
762 if (isascii(v) && isprint(v))
763 (void) sm_io_putc(fp, SM_TIME_DEFAULT,
764 v);
765 else
766 (void) sm_io_fprintf(fp,
767 SM_TIME_DEFAULT, "\\x%hhx", v);
768 # endif
769 }
770 if (*av != NULL)
771 (void) sm_io_putc(fp, SM_TIME_DEFAULT, ' ');
772 continue;
773 }
774 #endif /* _FFR_8BITENVADDR */
775 if (tTd(0, 44))
776 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
777 "\n\t%08lx=", (unsigned long) *av);
778 else
779 (void) sm_io_putc(fp, SM_TIME_DEFAULT, ' ');
780 if (tTd(0, 99))
781 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
782 "%s", str2prt(*av++));
783 else
784 xputs(fp, *av++);
785 }
786
787 /* don't print this if invoked directly (not via xputs())? */
788 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\n');
789 }
790
791 /*
792 ** XPUTS -- put string doing control escapes.
793 **
794 ** Parameters:
795 ** fp -- output file pointer.
796 ** s -- string to put. [A]
797 **
798 ** Returns:
799 ** none.
800 **
801 ** Side Effects:
802 ** output to stdout
803 */
804
805 void
xputs(fp,s)806 xputs(fp, s)
807 SM_FILE_T *fp;
808 const char *s;
809 {
810 int c;
811 struct metamac *mp;
812 bool shiftout = false;
813 extern struct metamac MetaMacros[];
814 static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI",
815 "@(#)$Debug: ANSI - enable reverse video in debug output $");
816 #if _FFR_8BITENVADDR
817 if (tTd(0, 9))
818 {
819 char *av[2];
820
821 av[0] = (char *) s;
822 av[1] = NULL;
823 printav(fp, av);
824 return;
825 }
826 #endif
827
828 /*
829 ** TermEscape is set here, rather than in main(),
830 ** because ANSI mode can be turned on or off at any time
831 ** if we are in -bt rule testing mode.
832 */
833
834 if (sm_debug_unknown(&DebugANSI))
835 {
836 if (sm_debug_active(&DebugANSI, 1))
837 {
838 TermEscape.te_rv_on = "\033[7m";
839 TermEscape.te_normal = "\033[0m";
840 }
841 else
842 {
843 TermEscape.te_rv_on = "";
844 TermEscape.te_normal = "";
845 }
846 }
847
848 if (s == NULL)
849 {
850 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s<null>%s",
851 TermEscape.te_rv_on, TermEscape.te_normal);
852 return;
853 }
854 while ((c = (*s++ & 0377)) != '\0')
855 {
856 if (shiftout)
857 {
858 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
859 TermEscape.te_normal);
860 shiftout = false;
861 }
862 if (!isascii(c) && !tTd(84, 1))
863 {
864 if (c == MATCHREPL)
865 {
866 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
867 "%s$",
868 TermEscape.te_rv_on);
869 shiftout = true;
870 if (*s == '\0')
871 continue;
872 c = *s++ & 0377;
873 goto printchar;
874 }
875 if (c == MACROEXPAND || c == MACRODEXPAND)
876 {
877 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
878 "%s$",
879 TermEscape.te_rv_on);
880 if (c == MACRODEXPAND)
881 (void) sm_io_putc(fp,
882 SM_TIME_DEFAULT, '&');
883 shiftout = true;
884 if (*s == '\0')
885 continue;
886 if (strchr("=~&?", *s) != NULL)
887 (void) sm_io_putc(fp,
888 SM_TIME_DEFAULT,
889 *s++);
890 if (bitset(0200, *s))
891 (void) sm_io_fprintf(fp,
892 SM_TIME_DEFAULT,
893 "{%s}",
894 macname(bitidx(*s++)));
895 else
896 (void) sm_io_fprintf(fp,
897 SM_TIME_DEFAULT,
898 "%c",
899 *s++);
900 continue;
901 }
902 for (mp = MetaMacros; mp->metaname != '\0'; mp++)
903 {
904 if (bitidx(mp->metaval) == c)
905 {
906 (void) sm_io_fprintf(fp,
907 SM_TIME_DEFAULT,
908 "%s$%c",
909 TermEscape.te_rv_on,
910 mp->metaname);
911 shiftout = true;
912 break;
913 }
914 }
915 if (c == MATCHCLASS || c == MATCHNCLASS)
916 {
917 if (bitset(0200, *s))
918 (void) sm_io_fprintf(fp,
919 SM_TIME_DEFAULT,
920 "{%s}",
921 macname(bitidx(*s++)));
922 else if (*s != '\0')
923 (void) sm_io_fprintf(fp,
924 SM_TIME_DEFAULT,
925 "%c",
926 *s++);
927 }
928 if (mp->metaname != '\0')
929 continue;
930
931 /* unrecognized meta character */
932 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%sM-",
933 TermEscape.te_rv_on);
934 shiftout = true;
935 c &= 0177;
936 }
937 printchar:
938 if (isascii(c) && isprint(c))
939 {
940 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c);
941 continue;
942 }
943
944 /* wasn't a meta-macro -- find another way to print it */
945 switch (c)
946 {
947 case '\n':
948 c = 'n';
949 break;
950
951 case '\r':
952 c = 'r';
953 break;
954
955 case '\t':
956 c = 't';
957 break;
958 }
959 if (!shiftout)
960 {
961 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
962 TermEscape.te_rv_on);
963 shiftout = true;
964 }
965 if (isascii(c) && isprint(c))
966 {
967 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\\');
968 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c);
969 }
970 else if (tTd(84, 2))
971 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %o ", c);
972 else if (tTd(84, 1))
973 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %#x ", c);
974 else if (!isascii(c) && !tTd(84, 1))
975 {
976 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '^');
977 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c ^ 0100);
978 }
979 }
980 if (shiftout)
981 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
982 TermEscape.te_normal);
983 (void) sm_io_flush(fp, SM_TIME_DEFAULT);
984 }
985
986 /*
987 ** MAKELOWER_A -- Translate a line into lower case
988 **
989 ** Parameters:
990 ** pp -- pointer to the string to translate (modified in place if possible). [A]
991 ** rpool -- rpool to use to reallocate string if needed
992 ** (if NULL: uses sm_malloc() internally)
993 **
994 ** Returns:
995 ** lower cased string
996 **
997 ** Side Effects:
998 ** String pointed to by pp is translated to lower case if possible.
999 */
1000
1001 char *
makelower_a(pp,rpool)1002 makelower_a(pp, rpool)
1003 char **pp;
1004 SM_RPOOL_T *rpool;
1005 {
1006 char c;
1007 char *orig, *p;
1008
1009 SM_REQUIRE(pp != NULL);
1010 p = *pp;
1011 if (p == NULL)
1012 return p;
1013 orig = p;
1014
1015 #if USE_EAI
1016 if (!addr_is_ascii(p))
1017 {
1018 char *new;
1019
1020 new = sm_lowercase(p);
1021 if (new == p)
1022 return p;
1023 *pp = sm_rpool_strdup_tagged_x(rpool, new, "makelower", 0, 1);
1024 return *pp;
1025 }
1026 #endif /* USE_EAI */
1027 for (; (c = *p) != '\0'; p++)
1028 if (isascii(c) && isupper(c))
1029 *p = tolower(c);
1030 return orig;
1031 }
1032
1033 #if 0
1034 makelower: Optimization for EAI case?
1035
1036 unsigned char ch;
1037
1038 while ((ch = (unsigned char)*str) != '\0' && ch < 127)
1039 {
1040 if (isascii(c) && isupper(c))
1041 *str = tolower(ch);
1042 ++str;
1043 }
1044 if ('\0' == ch)
1045 return orig;
1046 handle UTF-8 case: invoke sm_lowercase() etc
1047 #endif /* 0 */
1048
1049 /*
1050 ** MAKELOWER_BUF -- Translate a line into lower case
1051 **
1052 ** Parameters:
1053 ** str -- string to translate. [A]
1054 ** buf -- where to place lower case version.
1055 ** buflen -- size of buf
1056 **
1057 ** Returns:
1058 ** nothing
1059 **
1060 ** Side Effects:
1061 ** String pointed to by str is translated to lower case if possible.
1062 **
1063 ** Note:
1064 ** if str is lower cased in place and str == buf (same pointer),
1065 ** then it is not explicitly copied.
1066 */
1067
1068 void
makelower_buf(str,buf,buflen)1069 makelower_buf(str, buf, buflen)
1070 char *str;
1071 char *buf;
1072 int buflen;
1073 {
1074 char *lower;
1075
1076 SM_REQUIRE(buf != NULL);
1077 if (str == NULL)
1078 return;
1079 lower = makelower_a(&str, NULL);
1080 if (lower != str || str != buf)
1081 {
1082 sm_strlcpy(buf, lower, buflen);
1083 if (lower != str)
1084 SM_FREE(lower);
1085 }
1086 return;
1087 }
1088
1089 /*
1090 ** FIXCRLF -- fix CRLF in line.
1091 **
1092 ** XXX: Could this be a problem for EAI? That is, can there
1093 ** be a string with \n and the previous octet is \n
1094 ** but is part of a UTF8 "char"?
1095 **
1096 ** Looks for the CRLF combination and turns it into the
1097 ** UNIX canonical LF character. It only takes one line,
1098 ** i.e., it is assumed that the first LF found is the end
1099 ** of the line.
1100 **
1101 ** Parameters:
1102 ** line -- the line to fix. [A]
1103 ** stripnl -- if true, strip the newline also.
1104 **
1105 ** Returns:
1106 ** none.
1107 **
1108 ** Side Effects:
1109 ** line is changed in place.
1110 */
1111
1112 void
fixcrlf(line,stripnl)1113 fixcrlf(line, stripnl)
1114 char *line;
1115 bool stripnl;
1116 {
1117 register char *p;
1118
1119 p = strchr(line, '\n');
1120 if (p == NULL)
1121 return;
1122 if (p > line && p[-1] == '\r')
1123 p--;
1124 if (!stripnl)
1125 *p++ = '\n';
1126 *p = '\0';
1127 }
1128
1129 /*
1130 ** PUTLINE -- put a line like fputs obeying SMTP conventions
1131 **
1132 ** This routine always guarantees outputting a newline (or CRLF,
1133 ** as appropriate) at the end of the string.
1134 **
1135 ** Parameters:
1136 ** l -- line to put. (should be [x])
1137 ** mci -- the mailer connection information.
1138 **
1139 ** Returns:
1140 ** true iff line was written successfully
1141 **
1142 ** Side Effects:
1143 ** output of l to mci->mci_out.
1144 */
1145
1146 bool
putline(l,mci)1147 putline(l, mci)
1148 register char *l;
1149 register MCI *mci;
1150 {
1151 return putxline(l, strlen(l), mci, PXLF_MAPFROM);
1152 }
1153
1154 /*
1155 ** PUTXLINE -- putline with flags bits.
1156 **
1157 ** This routine always guarantees outputting a newline (or CRLF,
1158 ** as appropriate) at the end of the string.
1159 **
1160 ** Parameters:
1161 ** l -- line to put. (should be [x])
1162 ** len -- the length of the line.
1163 ** mci -- the mailer connection information.
1164 ** pxflags -- flag bits:
1165 ** PXLF_MAPFROM -- map From_ to >From_.
1166 ** PXLF_STRIP8BIT -- strip 8th bit.
1167 ** PXLF_HEADER -- map bare newline in header to newline space.
1168 ** PXLF_NOADDEOL -- don't add an EOL if one wasn't present.
1169 ** PXLF_STRIPMQUOTE -- strip METAQUOTE bytes.
1170 **
1171 ** Returns:
1172 ** true iff line was written successfully
1173 **
1174 ** Side Effects:
1175 ** output of l to mci->mci_out.
1176 */
1177
1178 #define PUTX(limit) \
1179 do \
1180 { \
1181 quotenext = false; \
1182 while (l < limit) \
1183 { \
1184 unsigned char c = (unsigned char) *l++; \
1185 \
1186 if (bitset(PXLF_STRIPMQUOTE, pxflags) && \
1187 !quotenext && c == METAQUOTE) \
1188 { \
1189 quotenext = true; \
1190 continue; \
1191 } \
1192 quotenext = false; \
1193 if (strip8bit) \
1194 c &= 0177; \
1195 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, \
1196 c) == SM_IO_EOF) \
1197 { \
1198 dead = true; \
1199 break; \
1200 } \
1201 if (TrafficLogFile != NULL) \
1202 (void) sm_io_putc(TrafficLogFile, \
1203 SM_TIME_DEFAULT, \
1204 c); \
1205 } \
1206 } while (0)
1207
1208 bool
putxline(l,len,mci,pxflags)1209 putxline(l, len, mci, pxflags)
1210 register char *l;
1211 size_t len;
1212 register MCI *mci;
1213 int pxflags;
1214 {
1215 register char *p, *end;
1216 int slop;
1217 bool dead, quotenext, strip8bit;
1218
1219 /* strip out 0200 bits -- these can look like TELNET protocol */
1220 strip8bit = bitset(MCIF_7BIT, mci->mci_flags) ||
1221 bitset(PXLF_STRIP8BIT, pxflags);
1222 dead = false;
1223 slop = 0;
1224
1225 end = l + len;
1226 do
1227 {
1228 bool noeol = false;
1229
1230 /* find the end of the line */
1231 p = memchr(l, '\n', end - l);
1232 if (p == NULL)
1233 {
1234 p = end;
1235 noeol = true;
1236 }
1237
1238 if (TrafficLogFile != NULL)
1239 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1240 "%05d >>> ", (int) CurrentPid);
1241
1242 /* check for line overflow */
1243 while (mci->mci_mailer->m_linelimit > 0 &&
1244 (p - l + slop) > mci->mci_mailer->m_linelimit)
1245 {
1246 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
1247
1248 if (l[0] == '.' && slop == 0 &&
1249 bitnset(M_XDOT, mci->mci_mailer->m_flags))
1250 {
1251 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1252 '.') == SM_IO_EOF)
1253 dead = true;
1254 if (TrafficLogFile != NULL)
1255 (void) sm_io_putc(TrafficLogFile,
1256 SM_TIME_DEFAULT, '.');
1257 }
1258 else if (l[0] == 'F' && slop == 0 &&
1259 bitset(PXLF_MAPFROM, pxflags) &&
1260 strncmp(l, "From ", 5) == 0 &&
1261 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
1262 {
1263 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1264 '>') == SM_IO_EOF)
1265 dead = true;
1266 if (TrafficLogFile != NULL)
1267 (void) sm_io_putc(TrafficLogFile,
1268 SM_TIME_DEFAULT,
1269 '>');
1270 }
1271 if (dead)
1272 break;
1273
1274 PUTX(q);
1275 if (dead)
1276 break;
1277
1278 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1279 '!') == SM_IO_EOF ||
1280 sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
1281 mci->mci_mailer->m_eol) == SM_IO_EOF ||
1282 sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1283 ' ') == SM_IO_EOF)
1284 {
1285 dead = true;
1286 break;
1287 }
1288 if (TrafficLogFile != NULL)
1289 {
1290 (void) sm_io_fprintf(TrafficLogFile,
1291 SM_TIME_DEFAULT,
1292 "!\n%05d >>> ",
1293 (int) CurrentPid);
1294 }
1295 slop = 1;
1296 }
1297
1298 if (dead)
1299 break;
1300
1301 /* output last part */
1302 if (l[0] == '.' && slop == 0 &&
1303 bitnset(M_XDOT, mci->mci_mailer->m_flags) &&
1304 !bitset(MCIF_INLONGLINE, mci->mci_flags))
1305 {
1306 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') ==
1307 SM_IO_EOF)
1308 {
1309 dead = true;
1310 break;
1311 }
1312 if (TrafficLogFile != NULL)
1313 (void) sm_io_putc(TrafficLogFile,
1314 SM_TIME_DEFAULT, '.');
1315 }
1316 else if (l[0] == 'F' && slop == 0 &&
1317 bitset(PXLF_MAPFROM, pxflags) &&
1318 strncmp(l, "From ", 5) == 0 &&
1319 bitnset(M_ESCFROM, mci->mci_mailer->m_flags) &&
1320 !bitset(MCIF_INLONGLINE, mci->mci_flags))
1321 {
1322 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') ==
1323 SM_IO_EOF)
1324 {
1325 dead = true;
1326 break;
1327 }
1328 if (TrafficLogFile != NULL)
1329 (void) sm_io_putc(TrafficLogFile,
1330 SM_TIME_DEFAULT, '>');
1331 }
1332 PUTX(p);
1333 if (dead)
1334 break;
1335
1336 if (TrafficLogFile != NULL)
1337 (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT,
1338 '\n');
1339 if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol))
1340 {
1341 mci->mci_flags &= ~MCIF_INLONGLINE;
1342 if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
1343 mci->mci_mailer->m_eol) == SM_IO_EOF)
1344 {
1345 dead = true;
1346 break;
1347 }
1348 }
1349 else
1350 mci->mci_flags |= MCIF_INLONGLINE;
1351
1352 if (l < end && *l == '\n')
1353 {
1354 if (*++l != ' ' && *l != '\t' && *l != '\0' &&
1355 bitset(PXLF_HEADER, pxflags))
1356 {
1357 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1358 ' ') == SM_IO_EOF)
1359 {
1360 dead = true;
1361 break;
1362 }
1363
1364 if (TrafficLogFile != NULL)
1365 (void) sm_io_putc(TrafficLogFile,
1366 SM_TIME_DEFAULT, ' ');
1367 }
1368 }
1369
1370 } while (l < end);
1371 return !dead;
1372 }
1373
1374 /*
1375 ** XUNLINK -- unlink a file, doing logging as appropriate.
1376 **
1377 ** Parameters:
1378 ** f -- name of file to unlink.
1379 **
1380 ** Returns:
1381 ** return value of unlink()
1382 **
1383 ** Side Effects:
1384 ** f is unlinked.
1385 */
1386
1387 int
xunlink(f)1388 xunlink(f)
1389 char *f;
1390 {
1391 register int i;
1392 int save_errno;
1393
1394 if (LogLevel > 98)
1395 sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f);
1396
1397 i = unlink(f);
1398 save_errno = errno;
1399 if (i < 0 && LogLevel > 97)
1400 sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d",
1401 f, errno);
1402 if (i >= 0)
1403 SYNC_DIR(f, false);
1404 errno = save_errno;
1405 return i;
1406 }
1407
1408 /*
1409 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts.
1410 **
1411 ** Parameters:
1412 ** buf -- place to put the input line.
1413 ** (can be [A], but should be [x])
1414 ** siz -- size of buf.
1415 ** fp -- file to read from.
1416 ** timeout -- the timeout before error occurs.
1417 ** during -- what we are trying to read (for error messages).
1418 **
1419 ** Returns:
1420 ** NULL on error (including timeout). This may also leave
1421 ** buf containing a null string.
1422 ** buf otherwise.
1423 */
1424
1425 char *
sfgets(buf,siz,fp,timeout,during)1426 sfgets(buf, siz, fp, timeout, during)
1427 char *buf;
1428 int siz;
1429 SM_FILE_T *fp;
1430 time_t timeout;
1431 char *during;
1432 {
1433 register char *p;
1434 int save_errno, io_timeout, l;
1435
1436 SM_REQUIRE(siz > 0);
1437 SM_REQUIRE(buf != NULL);
1438
1439 if (fp == NULL)
1440 {
1441 buf[0] = '\0';
1442 errno = EBADF;
1443 return NULL;
1444 }
1445
1446 /* try to read */
1447 l = -1;
1448 errno = 0;
1449
1450 /* convert the timeout to sm_io notation */
1451 io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000;
1452 while (!sm_io_eof(fp) && !sm_io_error(fp))
1453 {
1454 errno = 0;
1455 l = sm_io_fgets(fp, io_timeout, buf, siz);
1456 if (l < 0 && errno == EAGAIN)
1457 {
1458 /* The sm_io_fgets() call timedout */
1459 if (LogLevel > 1)
1460 sm_syslog(LOG_NOTICE, CurEnv->e_id,
1461 "timeout waiting for input from %.100s during %s",
1462 CURHOSTNAME,
1463 during);
1464 buf[0] = '\0';
1465 checkfd012(during);
1466 if (TrafficLogFile != NULL)
1467 (void) sm_io_fprintf(TrafficLogFile,
1468 SM_TIME_DEFAULT,
1469 "%05d <<< [TIMEOUT]\n",
1470 (int) CurrentPid);
1471 errno = ETIMEDOUT;
1472 return NULL;
1473 }
1474 if (l >= 0 || errno != EINTR)
1475 break;
1476 (void) sm_io_clearerr(fp);
1477 }
1478 save_errno = errno;
1479
1480 /* clean up the books and exit */
1481 LineNumber++;
1482 if (l < 0)
1483 {
1484 buf[0] = '\0';
1485 if (TrafficLogFile != NULL)
1486 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1487 "%05d <<< [EOF]\n",
1488 (int) CurrentPid);
1489 errno = save_errno;
1490 return NULL;
1491 }
1492 if (TrafficLogFile != NULL)
1493 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1494 "%05d <<< %s", (int) CurrentPid, buf);
1495 if (SevenBitInput)
1496 {
1497 for (p = buf; *p != '\0'; p++)
1498 *p &= ~0200;
1499 }
1500 else if (!HasEightBits)
1501 {
1502 for (p = buf; *p != '\0'; p++)
1503 {
1504 if (bitset(0200, *p))
1505 {
1506 HasEightBits = true;
1507 break;
1508 }
1509 }
1510 }
1511 return buf;
1512 }
1513
1514 /*
1515 ** FGETFOLDED -- like fgets, but knows about folded lines.
1516 **
1517 ** Parameters:
1518 ** buf -- place to put result.
1519 ** (can be [A], but should be [x])
1520 ** np -- pointer to bytes available; will be updated with
1521 ** the actual buffer size (not number of bytes filled)
1522 ** on return.
1523 ** f -- file to read from.
1524 **
1525 ** Returns:
1526 ** input line(s) on success, NULL on error or SM_IO_EOF.
1527 ** This will normally be buf -- unless the line is too
1528 ** long, when it will be sm_malloc_x()ed.
1529 **
1530 ** Side Effects:
1531 ** buf gets lines from f, with continuation lines (lines
1532 ** with leading white space) appended. CRLF's are mapped
1533 ** into single newlines. Any trailing LF is stripped.
1534 ** Increases LineNumber for each line.
1535 */
1536
1537 char *
fgetfolded(buf,np,f)1538 fgetfolded(buf, np, f)
1539 char *buf;
1540 int *np;
1541 SM_FILE_T *f;
1542 {
1543 register char *p = buf;
1544 char *bp = buf;
1545 register int i;
1546 int n;
1547
1548 SM_REQUIRE(np != NULL);
1549 n = *np;
1550 SM_REQUIRE(n > 0);
1551 SM_REQUIRE(buf != NULL);
1552 if (f == NULL)
1553 {
1554 buf[0] = '\0';
1555 errno = EBADF;
1556 return NULL;
1557 }
1558
1559 n--;
1560 while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF)
1561 {
1562 if (i == '\r')
1563 {
1564 i = sm_io_getc(f, SM_TIME_DEFAULT);
1565 if (i != '\n')
1566 {
1567 if (i != SM_IO_EOF)
1568 (void) sm_io_ungetc(f, SM_TIME_DEFAULT,
1569 i);
1570 i = '\r';
1571 }
1572 }
1573 if (--n <= 0)
1574 {
1575 /* allocate new space */
1576 char *nbp;
1577 int nn;
1578
1579 nn = (p - bp);
1580 if (nn < MEMCHUNKSIZE)
1581 nn *= 2;
1582 else
1583 nn += MEMCHUNKSIZE;
1584 nbp = sm_malloc_x(nn);
1585 memmove(nbp, bp, p - bp);
1586 p = &nbp[p - bp];
1587 if (bp != buf)
1588 sm_free(bp);
1589 bp = nbp;
1590 n = nn - (p - bp);
1591 *np = nn;
1592 }
1593 *p++ = i;
1594 if (i == '\n')
1595 {
1596 LineNumber++;
1597 i = sm_io_getc(f, SM_TIME_DEFAULT);
1598 if (i != SM_IO_EOF)
1599 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i);
1600 if (i != ' ' && i != '\t')
1601 break;
1602 }
1603 }
1604 if (p == bp)
1605 return NULL;
1606 if (p[-1] == '\n')
1607 p--;
1608 *p = '\0';
1609 return bp;
1610 }
1611
1612 /*
1613 ** CURTIME -- return current time.
1614 **
1615 ** Parameters:
1616 ** none.
1617 **
1618 ** Returns:
1619 ** the current time.
1620 */
1621
1622 time_t
curtime()1623 curtime()
1624 {
1625 auto time_t t;
1626
1627 (void) time(&t);
1628 return t;
1629 }
1630
1631 /*
1632 ** ATOBOOL -- convert a string representation to boolean.
1633 **
1634 ** Defaults to false
1635 **
1636 ** Parameters:
1637 ** s -- string to convert. Takes "tTyY", empty, and NULL as true,
1638 ** others as false.
1639 **
1640 ** Returns:
1641 ** A boolean representation of the string.
1642 */
1643
1644 bool
atobool(s)1645 atobool(s)
1646 register char *s;
1647 {
1648 if (SM_IS_EMPTY(s) || strchr("tTyY", *s) != NULL)
1649 return true;
1650 return false;
1651 }
1652
1653 /*
1654 ** ATOOCT -- convert a string representation to octal.
1655 **
1656 ** Parameters:
1657 ** s -- string to convert.
1658 **
1659 ** Returns:
1660 ** An integer representing the string interpreted as an
1661 ** octal number.
1662 */
1663
1664 int
atooct(s)1665 atooct(s)
1666 register char *s;
1667 {
1668 register int i = 0;
1669
1670 while (*s >= '0' && *s <= '7')
1671 i = (i << 3) | (*s++ - '0');
1672 return i;
1673 }
1674
1675 /*
1676 ** BITINTERSECT -- tell if two bitmaps intersect
1677 **
1678 ** Parameters:
1679 ** a, b -- the bitmaps in question
1680 **
1681 ** Returns:
1682 ** true if they have a non-null intersection
1683 ** false otherwise
1684 */
1685
1686 bool
bitintersect(a,b)1687 bitintersect(a, b)
1688 BITMAP256 a;
1689 BITMAP256 b;
1690 {
1691 int i;
1692
1693 for (i = BITMAPBYTES / sizeof(int); --i >= 0; )
1694 {
1695 if ((a[i] & b[i]) != 0)
1696 return true;
1697 }
1698 return false;
1699 }
1700
1701 /*
1702 ** BITZEROP -- tell if a bitmap is all zero
1703 **
1704 ** Parameters:
1705 ** map -- the bit map to check
1706 **
1707 ** Returns:
1708 ** true if map is all zero.
1709 ** false if there are any bits set in map.
1710 */
1711
1712 bool
bitzerop(map)1713 bitzerop(map)
1714 BITMAP256 map;
1715 {
1716 int i;
1717
1718 for (i = BITMAPBYTES / sizeof(int); --i >= 0; )
1719 {
1720 if (map[i] != 0)
1721 return false;
1722 }
1723 return true;
1724 }
1725
1726 /*
1727 ** STRCONTAINEDIN -- tell if one string is contained in another
1728 **
1729 ** Parameters:
1730 ** icase -- ignore case?
1731 ** a -- possible substring. [A]
1732 ** b -- possible superstring. [A]
1733 ** (both must be the same format: X or Q)
1734 **
1735 ** Returns:
1736 ** true if a is contained in b (case insensitive).
1737 ** false otherwise.
1738 */
1739
1740 bool
strcontainedin(icase,a,b)1741 strcontainedin(icase, a, b)
1742 bool icase;
1743 register char *a;
1744 register char *b;
1745 {
1746 int la;
1747 int lb;
1748 int c;
1749
1750 la = strlen(a);
1751 lb = strlen(b);
1752 c = *a;
1753 if (icase && isascii(c) && isupper(c))
1754 c = tolower(c);
1755 for (; lb-- >= la; b++)
1756 {
1757 if (icase)
1758 {
1759 if (*b != c &&
1760 isascii(*b) && isupper(*b) && tolower(*b) != c)
1761 continue;
1762 if (sm_strncasecmp(a, b, la) == 0)
1763 return true;
1764 }
1765 else
1766 {
1767 if (*b != c)
1768 continue;
1769 if (strncmp(a, b, la) == 0)
1770 return true;
1771 }
1772 }
1773 return false;
1774 }
1775
1776 #if XDEBUG
1777 /*
1778 ** CHECKFD012 -- check low numbered file descriptors
1779 **
1780 ** File descriptors 0, 1, and 2 should be open at all times.
1781 ** This routine verifies that, and fixes it if not true.
1782 **
1783 ** Parameters:
1784 ** where -- a tag printed if the assertion failed
1785 **
1786 ** Returns:
1787 ** none
1788 */
1789
1790 void
checkfd012(where)1791 checkfd012(where)
1792 char *where;
1793 {
1794 register int i;
1795
1796 for (i = 0; i < 3; i++)
1797 fill_fd(i, where);
1798 }
1799
1800 /*
1801 ** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
1802 **
1803 ** Parameters:
1804 ** fd -- file descriptor to check.
1805 ** where -- tag to print on failure.
1806 **
1807 ** Returns:
1808 ** none.
1809 */
1810
1811 void
checkfdopen(fd,where)1812 checkfdopen(fd, where)
1813 int fd;
1814 char *where;
1815 {
1816 struct stat st;
1817
1818 if (fstat(fd, &st) < 0 && errno == EBADF)
1819 {
1820 syserr("checkfdopen(%d): %s not open as expected!", fd, where);
1821 printopenfds(true);
1822 }
1823 }
1824 #endif /* XDEBUG */
1825
1826 /*
1827 ** CHECKFDS -- check for new or missing file descriptors
1828 **
1829 ** Parameters:
1830 ** where -- tag for printing. If null, take a base line.
1831 **
1832 ** Returns:
1833 ** none
1834 **
1835 ** Side Effects:
1836 ** If where is set, shows changes since the last call.
1837 */
1838
1839 void
checkfds(where)1840 checkfds(where)
1841 char *where;
1842 {
1843 int maxfd;
1844 register int fd;
1845 bool printhdr = true;
1846 int save_errno = errno;
1847 static BITMAP256 baseline;
1848 extern int DtableSize;
1849
1850 if (DtableSize > BITMAPBITS)
1851 maxfd = BITMAPBITS;
1852 else
1853 maxfd = DtableSize;
1854 if (where == NULL)
1855 clrbitmap(baseline);
1856
1857 for (fd = 0; fd < maxfd; fd++)
1858 {
1859 struct stat stbuf;
1860
1861 if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
1862 {
1863 if (!bitnset(fd, baseline))
1864 continue;
1865 clrbitn(fd, baseline);
1866 }
1867 else if (!bitnset(fd, baseline))
1868 setbitn(fd, baseline);
1869 else
1870 continue;
1871
1872 /* file state has changed */
1873 if (where == NULL)
1874 continue;
1875 if (printhdr)
1876 {
1877 sm_syslog(LOG_DEBUG, CurEnv->e_id,
1878 "%s: changed fds:",
1879 where);
1880 printhdr = false;
1881 }
1882 dumpfd(fd, true, true);
1883 }
1884 errno = save_errno;
1885 }
1886
1887 /*
1888 ** PRINTOPENFDS -- print the open file descriptors (for debugging)
1889 **
1890 ** Parameters:
1891 ** logit -- if set, send output to syslog; otherwise
1892 ** print for debugging.
1893 **
1894 ** Returns:
1895 ** none.
1896 */
1897
1898 #if NETINET || NETINET6
1899 # include <arpa/inet.h>
1900 #endif
1901
1902 void
printopenfds(logit)1903 printopenfds(logit)
1904 bool logit;
1905 {
1906 register int fd;
1907 extern int DtableSize;
1908
1909 for (fd = 0; fd < DtableSize; fd++)
1910 dumpfd(fd, false, logit);
1911 }
1912
1913 /*
1914 ** DUMPFD -- dump a file descriptor
1915 **
1916 ** Parameters:
1917 ** fd -- the file descriptor to dump.
1918 ** printclosed -- if set, print a notification even if
1919 ** it is closed; otherwise print nothing.
1920 ** logit -- if set, use sm_syslog instead of sm_dprintf()
1921 **
1922 ** Returns:
1923 ** none.
1924 */
1925
1926 void
dumpfd(fd,printclosed,logit)1927 dumpfd(fd, printclosed, logit)
1928 int fd;
1929 bool printclosed;
1930 bool logit;
1931 {
1932 register char *p;
1933 char *hp;
1934 #ifdef S_IFSOCK
1935 SOCKADDR sa;
1936 #endif
1937 auto SOCKADDR_LEN_T slen;
1938 int i;
1939 #if STAT64 > 0
1940 struct stat64 st;
1941 #else
1942 struct stat st;
1943 #endif
1944 char buf[200];
1945
1946 p = buf;
1947 (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
1948 p += strlen(p);
1949
1950 if (
1951 #if STAT64 > 0
1952 fstat64(fd, &st)
1953 #else
1954 fstat(fd, &st)
1955 #endif
1956 < 0)
1957 {
1958 if (errno != EBADF)
1959 {
1960 (void) sm_snprintf(p, SPACELEFT(buf, p),
1961 "CANNOT STAT (%s)",
1962 sm_errstring(errno));
1963 goto printit;
1964 }
1965 else if (printclosed)
1966 {
1967 (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED");
1968 goto printit;
1969 }
1970 return;
1971 }
1972
1973 i = fcntl(fd, F_GETFL, 0);
1974 if (i != -1)
1975 {
1976 (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
1977 p += strlen(p);
1978 }
1979
1980 (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ",
1981 (unsigned int) st.st_mode);
1982 p += strlen(p);
1983 switch (st.st_mode & S_IFMT)
1984 {
1985 #ifdef S_IFSOCK
1986 case S_IFSOCK:
1987 (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK ");
1988 p += strlen(p);
1989 memset(&sa, '\0', sizeof(sa));
1990 slen = sizeof(sa);
1991 if (getsockname(fd, &sa.sa, &slen) < 0)
1992 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
1993 sm_errstring(errno));
1994 else
1995 {
1996 hp = hostnamebyanyaddr(&sa);
1997 if (hp == NULL)
1998 {
1999 /* EMPTY */
2000 /* do nothing */
2001 }
2002 # if NETINET
2003 else if (sa.sa.sa_family == AF_INET)
2004 (void) sm_snprintf(p, SPACELEFT(buf, p),
2005 "%s/%d", hp, ntohs(sa.sin.sin_port));
2006 # endif
2007 # if NETINET6
2008 else if (sa.sa.sa_family == AF_INET6)
2009 (void) sm_snprintf(p, SPACELEFT(buf, p),
2010 "%s/%d", hp, ntohs(sa.sin6.sin6_port));
2011 # endif
2012 else
2013 (void) sm_snprintf(p, SPACELEFT(buf, p),
2014 "%s", hp);
2015 }
2016 p += strlen(p);
2017 (void) sm_snprintf(p, SPACELEFT(buf, p), "->");
2018 p += strlen(p);
2019 slen = sizeof(sa);
2020 if (getpeername(fd, &sa.sa, &slen) < 0)
2021 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
2022 sm_errstring(errno));
2023 else
2024 {
2025 hp = hostnamebyanyaddr(&sa);
2026 if (hp == NULL)
2027 {
2028 /* EMPTY */
2029 /* do nothing */
2030 }
2031 # if NETINET
2032 else if (sa.sa.sa_family == AF_INET)
2033 (void) sm_snprintf(p, SPACELEFT(buf, p),
2034 "%s/%d", hp, ntohs(sa.sin.sin_port));
2035 # endif
2036 # if NETINET6
2037 else if (sa.sa.sa_family == AF_INET6)
2038 (void) sm_snprintf(p, SPACELEFT(buf, p),
2039 "%s/%d", hp, ntohs(sa.sin6.sin6_port));
2040 # endif
2041 else
2042 (void) sm_snprintf(p, SPACELEFT(buf, p),
2043 "%s", hp);
2044 }
2045 break;
2046 #endif /* S_IFSOCK */
2047
2048 case S_IFCHR:
2049 (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: ");
2050 p += strlen(p);
2051 goto defprint;
2052
2053 #ifdef S_IFBLK
2054 case S_IFBLK:
2055 (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: ");
2056 p += strlen(p);
2057 goto defprint;
2058 #endif
2059
2060 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
2061 case S_IFIFO:
2062 (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: ");
2063 p += strlen(p);
2064 goto defprint;
2065 #endif
2066
2067 #ifdef S_IFDIR
2068 case S_IFDIR:
2069 (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: ");
2070 p += strlen(p);
2071 goto defprint;
2072 #endif
2073
2074 #ifdef S_IFLNK
2075 case S_IFLNK:
2076 (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: ");
2077 p += strlen(p);
2078 goto defprint;
2079 #endif
2080
2081 default:
2082 defprint:
2083 (void) sm_snprintf(p, SPACELEFT(buf, p),
2084 "dev=%ld/%ld, ino=%llu, nlink=%d, u/gid=%ld/%ld, ",
2085 (long) major(st.st_dev), (long) minor(st.st_dev),
2086 (ULONGLONG_T) st.st_ino,
2087 (int) st.st_nlink, (long) st.st_uid,
2088 (long) st.st_gid);
2089 p += strlen(p);
2090 (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu",
2091 (ULONGLONG_T) st.st_size);
2092 break;
2093 }
2094
2095 printit:
2096 if (logit)
2097 sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
2098 "%.800s", buf);
2099 else
2100 sm_dprintf("%s\n", buf);
2101 }
2102
2103 /*
2104 ** SHORTEN_HOSTNAME -- strip local domain information off of hostname.
2105 **
2106 ** Parameters:
2107 ** host -- the host to shorten (stripped in place).
2108 ** [EAI: matched against $m: must be same format;
2109 ** conversion needed?]
2110 **
2111 ** Returns:
2112 ** place where string was truncated, NULL if not truncated.
2113 */
2114
2115 char *
shorten_hostname(host)2116 shorten_hostname(host)
2117 char host[];
2118 {
2119 register char *p;
2120 char *mydom;
2121 int i;
2122 bool canon = false;
2123
2124 /* strip off final dot */
2125 i = strlen(host);
2126 p = &host[(i == 0) ? 0 : i - 1];
2127 if (*p == '.')
2128 {
2129 *p = '\0';
2130 canon = true;
2131 }
2132
2133 /* see if there is any domain at all -- if not, we are done */
2134 p = strchr(host, '.');
2135 if (p == NULL)
2136 return NULL;
2137
2138 /* yes, we have a domain -- see if it looks like us */
2139 mydom = macvalue('m', CurEnv);
2140 if (mydom == NULL)
2141 mydom = "";
2142 i = strlen(++p);
2143 if ((canon ? sm_strcasecmp(p, mydom)
2144 : sm_strncasecmp(p, mydom, i)) == 0 &&
2145 (mydom[i] == '.' || mydom[i] == '\0'))
2146 {
2147 *--p = '\0';
2148 return p;
2149 }
2150 return NULL;
2151 }
2152
2153 /*
2154 ** PROG_OPEN -- open a program for reading
2155 **
2156 ** Parameters:
2157 ** argv -- the argument list.
2158 ** pfd -- pointer to a place to store the file descriptor.
2159 ** e -- the current envelope.
2160 **
2161 ** Returns:
2162 ** pid of the process -- -1 if it failed.
2163 */
2164
2165 pid_t
prog_open(argv,pfd,e)2166 prog_open(argv, pfd, e)
2167 char **argv;
2168 int *pfd;
2169 ENVELOPE *e;
2170 {
2171 pid_t pid;
2172 int save_errno;
2173 int sff;
2174 int ret;
2175 int fdv[2];
2176 char *p, *q;
2177 char buf[MAXPATHLEN];
2178 extern int DtableSize;
2179
2180 if (pipe(fdv) < 0)
2181 {
2182 syserr("%s: cannot create pipe for stdout", argv[0]);
2183 return -1;
2184 }
2185 pid = fork();
2186 if (pid < 0)
2187 {
2188 syserr("%s: cannot fork", argv[0]);
2189 (void) close(fdv[0]);
2190 (void) close(fdv[1]);
2191 return -1;
2192 }
2193 if (pid > 0)
2194 {
2195 /* parent */
2196 (void) close(fdv[1]);
2197 *pfd = fdv[0];
2198 return pid;
2199 }
2200
2201 /* Reset global flags */
2202 RestartRequest = NULL;
2203 RestartWorkGroup = false;
2204 ShutdownRequest = NULL;
2205 PendingSignal = 0;
2206 CurrentPid = getpid();
2207
2208 /*
2209 ** Initialize exception stack and default exception
2210 ** handler for child process.
2211 */
2212
2213 sm_exc_newthread(fatal_error);
2214
2215 /* child -- close stdin */
2216 (void) close(0);
2217
2218 /* stdout goes back to parent */
2219 (void) close(fdv[0]);
2220 if (dup2(fdv[1], 1) < 0)
2221 {
2222 syserr("%s: cannot dup2 for stdout", argv[0]);
2223 _exit(EX_OSERR);
2224 }
2225 (void) close(fdv[1]);
2226
2227 /* stderr goes to transcript if available */
2228 if (e->e_xfp != NULL)
2229 {
2230 int xfd;
2231
2232 xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL);
2233 if (xfd >= 0 && dup2(xfd, 2) < 0)
2234 {
2235 syserr("%s: cannot dup2 for stderr", argv[0]);
2236 _exit(EX_OSERR);
2237 }
2238 }
2239
2240 /* this process has no right to the queue file */
2241 if (e->e_lockfp != NULL)
2242 {
2243 int fd;
2244
2245 fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL);
2246 if (fd >= 0)
2247 (void) close(fd);
2248 else
2249 syserr("%s: lockfp does not have a fd", argv[0]);
2250 }
2251
2252 /* chroot to the program mailer directory, if defined */
2253 if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL)
2254 {
2255 expand(ProgMailer->m_rootdir, buf, sizeof(buf), e);
2256 if (chroot(buf) < 0)
2257 {
2258 syserr("prog_open: cannot chroot(%s)", buf);
2259 exit(EX_TEMPFAIL);
2260 }
2261 if (chdir("/") < 0)
2262 {
2263 syserr("prog_open: cannot chdir(/)");
2264 exit(EX_TEMPFAIL);
2265 }
2266 }
2267
2268 /* run as default user */
2269 endpwent();
2270 sm_mbdb_terminate();
2271 #if _FFR_MEMSTAT
2272 (void) sm_memstat_close();
2273 #endif
2274 if (setgid(DefGid) < 0 && geteuid() == 0)
2275 {
2276 syserr("prog_open: setgid(%ld) failed", (long) DefGid);
2277 exit(EX_TEMPFAIL);
2278 }
2279 if (setuid(DefUid) < 0 && geteuid() == 0)
2280 {
2281 syserr("prog_open: setuid(%ld) failed", (long) DefUid);
2282 exit(EX_TEMPFAIL);
2283 }
2284
2285 /* run in some directory */
2286 if (ProgMailer != NULL)
2287 p = ProgMailer->m_execdir;
2288 else
2289 p = NULL;
2290 for (; p != NULL; p = q)
2291 {
2292 q = strchr(p, ':');
2293 if (q != NULL)
2294 *q = '\0';
2295 expand(p, buf, sizeof(buf), e);
2296 if (q != NULL)
2297 *q++ = ':';
2298 if (buf[0] != '\0' && chdir(buf) >= 0)
2299 break;
2300 }
2301 if (p == NULL)
2302 {
2303 /* backup directories */
2304 if (chdir("/tmp") < 0)
2305 (void) chdir("/");
2306 }
2307
2308 /* Check safety of program to be run */
2309 sff = SFF_ROOTOK|SFF_EXECOK;
2310 if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail))
2311 sff |= SFF_NOGWFILES|SFF_NOWWFILES;
2312 if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail))
2313 sff |= SFF_NOPATHCHECK;
2314 else
2315 sff |= SFF_SAFEDIRPATH;
2316 ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL);
2317 if (ret != 0)
2318 sm_syslog(LOG_INFO, e->e_id,
2319 "Warning: prog_open: program %s unsafe: %s",
2320 argv[0], sm_errstring(ret));
2321
2322 /* arrange for all the files to be closed */
2323 sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
2324
2325 /* now exec the process */
2326 (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
2327
2328 /* woops! failed */
2329 save_errno = errno;
2330 syserr("%s: cannot exec", argv[0]);
2331 if (transienterror(save_errno))
2332 _exit(EX_OSERR);
2333 _exit(EX_CONFIG);
2334 return -1; /* avoid compiler warning on IRIX */
2335 }
2336
2337 /*
2338 ** GET_COLUMN -- look up a Column in a line buffer
2339 **
2340 ** Parameters:
2341 ** line -- the raw text line to search. [A]
2342 ** col -- the column number to fetch.
2343 ** delim -- the delimiter between columns. If null,
2344 ** use white space.
2345 ** buf -- the output buffer.
2346 ** buflen -- the length of buf.
2347 **
2348 ** Returns:
2349 ** buf if successful.
2350 ** NULL otherwise.
2351 */
2352
2353 char *
get_column(line,col,delim,buf,buflen)2354 get_column(line, col, delim, buf, buflen)
2355 char line[];
2356 int col;
2357 int delim;
2358 char buf[];
2359 int buflen;
2360 {
2361 char *p;
2362 char *begin, *end;
2363 int i;
2364 char delimbuf[4];
2365
2366 if ((char) delim == '\0')
2367 (void) sm_strlcpy(delimbuf, "\n\t ", sizeof(delimbuf));
2368 else
2369 {
2370 delimbuf[0] = (char) delim;
2371 delimbuf[1] = '\0';
2372 }
2373
2374 p = line;
2375 if (*p == '\0')
2376 return NULL; /* line empty */
2377 if (*p == (char) delim && col == 0)
2378 return NULL; /* first column empty */
2379
2380 begin = line;
2381
2382 if (col == 0 && (char) delim == '\0')
2383 {
2384 while (*begin != '\0' && SM_ISSPACE(*begin))
2385 begin++;
2386 }
2387
2388 for (i = 0; i < col; i++)
2389 {
2390 if ((begin = strpbrk(begin, delimbuf)) == NULL)
2391 return NULL; /* no such column */
2392 begin++;
2393 if ((char) delim == '\0')
2394 {
2395 while (*begin != '\0' && SM_ISSPACE(*begin))
2396 begin++;
2397 }
2398 }
2399
2400 end = strpbrk(begin, delimbuf);
2401 if (end == NULL)
2402 i = strlen(begin);
2403 else
2404 i = end - begin;
2405 if (i >= buflen)
2406 i = buflen - 1;
2407 (void) sm_strlcpy(buf, begin, i + 1);
2408 return buf;
2409 }
2410
2411 /*
2412 ** CLEANSTRCPY -- copy string keeping out bogus characters
2413 ** XXX: This may be a problem for EAI?
2414 **
2415 ** Parameters:
2416 ** t -- "to" string.
2417 ** f -- "from" string. [A]
2418 ** l -- length of space available in "to" string.
2419 **
2420 ** Returns:
2421 ** none.
2422 */
2423
2424 void
cleanstrcpy(t,f,l)2425 cleanstrcpy(t, f, l)
2426 register char *t;
2427 register char *f;
2428 int l;
2429 {
2430 /* check for newlines and log if necessary */
2431 (void) denlstring(f, true, true);
2432
2433 if (l <= 0)
2434 syserr("!cleanstrcpy: length == 0");
2435
2436 l--;
2437 while (l > 0 && *f != '\0')
2438 {
2439 if (isascii(*f) &&
2440 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
2441 {
2442 l--;
2443 *t++ = *f;
2444 }
2445 f++;
2446 }
2447 *t = '\0';
2448 }
2449
2450 /*
2451 ** DENLSTRING -- convert newlines in a string to spaces
2452 **
2453 ** Parameters:
2454 ** s -- the input string [A]
2455 ** strict -- if set, don't permit continuation lines.
2456 ** logattacks -- if set, log attempted attacks.
2457 **
2458 ** Returns:
2459 ** A pointer to a version of the string with newlines
2460 ** mapped to spaces. This should be copied.
2461 */
2462
2463 char *
denlstring(s,strict,logattacks)2464 denlstring(s, strict, logattacks)
2465 char *s;
2466 bool strict;
2467 bool logattacks;
2468 {
2469 register char *p;
2470 int l;
2471 static char *bp = NULL;
2472 static int bl = 0;
2473
2474 p = s;
2475 while ((p = strchr(p, '\n')) != NULL)
2476 if (strict || (*++p != ' ' && *p != '\t'))
2477 break;
2478 if (p == NULL)
2479 return s;
2480
2481 l = strlen(s) + 1;
2482 if (bl < l)
2483 {
2484 /* allocate more space */
2485 char *nbp = sm_pmalloc_x(l);
2486
2487 if (bp != NULL)
2488 sm_free(bp);
2489 bp = nbp;
2490 bl = l;
2491 }
2492 (void) sm_strlcpy(bp, s, l);
2493 for (p = bp; (p = strchr(p, '\n')) != NULL; )
2494 *p++ = ' ';
2495
2496 if (logattacks)
2497 {
2498 sm_syslog(LOG_NOTICE, CurEnv ? CurEnv->e_id : NULL,
2499 "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
2500 RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
2501 shortenstring(bp, MAXSHORTSTR));
2502 }
2503
2504 return bp;
2505 }
2506
2507 /*
2508 ** STRREPLNONPRT -- replace "unprintable" characters in a string with subst
2509 **
2510 ** Parameters:
2511 ** s -- string to manipulate (in place) [A]
2512 ** c -- character to use as replacement
2513 **
2514 ** Returns:
2515 ** true iff string did not contain "unprintable" characters
2516 */
2517
2518 bool
strreplnonprt(s,c)2519 strreplnonprt(s, c)
2520 char *s;
2521 int c;
2522 {
2523 bool ok;
2524
2525 ok = true;
2526 if (s == NULL)
2527 return ok;
2528 while (*s != '\0')
2529 {
2530 if (!(isascii(*s) && isprint(*s)))
2531 {
2532 *s = c;
2533 ok = false;
2534 }
2535 ++s;
2536 }
2537 return ok;
2538 }
2539
2540 /*
2541 ** PATH_IS_DIR -- check to see if file exists and is a directory.
2542 **
2543 ** There are some additional checks for security violations in
2544 ** here. This routine is intended to be used for the host status
2545 ** support.
2546 **
2547 ** Parameters:
2548 ** pathname -- pathname to check for directory-ness. [x]
2549 ** createflag -- if set, create directory if needed.
2550 **
2551 ** Returns:
2552 ** true -- if the indicated pathname is a directory
2553 ** false -- otherwise
2554 */
2555
2556 bool
path_is_dir(pathname,createflag)2557 path_is_dir(pathname, createflag)
2558 char *pathname;
2559 bool createflag;
2560 {
2561 struct stat statbuf;
2562
2563 #if HASLSTAT
2564 if (lstat(pathname, &statbuf) < 0)
2565 #else
2566 if (stat(pathname, &statbuf) < 0)
2567 #endif
2568 {
2569 if (errno != ENOENT || !createflag)
2570 return false;
2571 if (mkdir(pathname, 0755) < 0)
2572 return false;
2573 return true;
2574 }
2575 if (!S_ISDIR(statbuf.st_mode))
2576 {
2577 errno = ENOTDIR;
2578 return false;
2579 }
2580
2581 /* security: don't allow writable directories */
2582 if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
2583 {
2584 errno = EACCES;
2585 return false;
2586 }
2587 return true;
2588 }
2589
2590 /*
2591 ** PROC_LIST_ADD -- add process id to list of our children
2592 **
2593 ** Parameters:
2594 ** pid -- pid to add to list.
2595 ** task -- task of pid.
2596 ** type -- type of process.
2597 ** count -- number of processes.
2598 ** other -- other information for this type.
2599 **
2600 ** Returns:
2601 ** none
2602 **
2603 ** Side Effects:
2604 ** May increase CurChildren. May grow ProcList.
2605 */
2606
2607 typedef struct procs PROCS_T;
2608
2609 struct procs
2610 {
2611 pid_t proc_pid;
2612 char *proc_task;
2613 int proc_type;
2614 int proc_count;
2615 int proc_other;
2616 SOCKADDR proc_hostaddr;
2617 };
2618
2619 static PROCS_T *volatile ProcListVec = NULL;
2620 static int ProcListSize = 0;
2621
2622 void
proc_list_add(pid,task,type,count,other,hostaddr)2623 proc_list_add(pid, task, type, count, other, hostaddr)
2624 pid_t pid;
2625 char *task;
2626 int type;
2627 int count;
2628 int other;
2629 SOCKADDR *hostaddr;
2630 {
2631 int i;
2632
2633 for (i = 0; i < ProcListSize; i++)
2634 {
2635 if (ProcListVec[i].proc_pid == NO_PID)
2636 break;
2637 }
2638 if (i >= ProcListSize)
2639 {
2640 /* probe the existing vector to avoid growing infinitely */
2641 proc_list_probe();
2642
2643 /* now scan again */
2644 for (i = 0; i < ProcListSize; i++)
2645 {
2646 if (ProcListVec[i].proc_pid == NO_PID)
2647 break;
2648 }
2649 }
2650 if (i >= ProcListSize)
2651 {
2652 /* grow process list */
2653 int chldwasblocked;
2654 PROCS_T *npv;
2655
2656 SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG);
2657 npv = (PROCS_T *) sm_pmalloc_x((sizeof(*npv)) *
2658 (ProcListSize + PROC_LIST_SEG));
2659
2660 /* Block SIGCHLD so reapchild() doesn't mess with us */
2661 chldwasblocked = sm_blocksignal(SIGCHLD);
2662 if (ProcListSize > 0)
2663 {
2664 memmove(npv, ProcListVec,
2665 ProcListSize * sizeof(PROCS_T));
2666 sm_free(ProcListVec);
2667 }
2668
2669 /* XXX just use memset() to initialize this part? */
2670 for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
2671 {
2672 npv[i].proc_pid = NO_PID;
2673 npv[i].proc_task = NULL;
2674 npv[i].proc_type = PROC_NONE;
2675 }
2676 i = ProcListSize;
2677 ProcListSize += PROC_LIST_SEG;
2678 ProcListVec = npv;
2679 if (chldwasblocked == 0)
2680 (void) sm_releasesignal(SIGCHLD);
2681 }
2682 ProcListVec[i].proc_pid = pid;
2683 PSTRSET(ProcListVec[i].proc_task, task);
2684 ProcListVec[i].proc_type = type;
2685 ProcListVec[i].proc_count = count;
2686 ProcListVec[i].proc_other = other;
2687 if (hostaddr != NULL)
2688 ProcListVec[i].proc_hostaddr = *hostaddr;
2689 else
2690 memset(&ProcListVec[i].proc_hostaddr, 0,
2691 sizeof(ProcListVec[i].proc_hostaddr));
2692
2693 /* if process adding itself, it's not a child */
2694 if (pid != CurrentPid)
2695 {
2696 SM_ASSERT(CurChildren < INT_MAX);
2697 CurChildren++;
2698 }
2699 }
2700
2701 /*
2702 ** PROC_LIST_SET -- set pid task in process list
2703 **
2704 ** Parameters:
2705 ** pid -- pid to set
2706 ** task -- task of pid
2707 **
2708 ** Returns:
2709 ** none.
2710 */
2711
2712 void
proc_list_set(pid,task)2713 proc_list_set(pid, task)
2714 pid_t pid;
2715 char *task;
2716 {
2717 int i;
2718
2719 for (i = 0; i < ProcListSize; i++)
2720 {
2721 if (ProcListVec[i].proc_pid == pid)
2722 {
2723 PSTRSET(ProcListVec[i].proc_task, task);
2724 break;
2725 }
2726 }
2727 }
2728
2729 /*
2730 ** PROC_LIST_DROP -- drop pid from process list
2731 **
2732 ** Parameters:
2733 ** pid -- pid to drop
2734 ** st -- process status
2735 ** other -- storage for proc_other (return).
2736 **
2737 ** Returns:
2738 ** none.
2739 **
2740 ** Side Effects:
2741 ** May decrease CurChildren, CurRunners, or
2742 ** set RestartRequest or ShutdownRequest.
2743 **
2744 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
2745 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2746 ** DOING.
2747 */
2748
2749 void
proc_list_drop(pid,st,other)2750 proc_list_drop(pid, st, other)
2751 pid_t pid;
2752 int st;
2753 int *other;
2754 {
2755 int i;
2756 int type = PROC_NONE;
2757
2758 for (i = 0; i < ProcListSize; i++)
2759 {
2760 if (ProcListVec[i].proc_pid == pid)
2761 {
2762 ProcListVec[i].proc_pid = NO_PID;
2763 type = ProcListVec[i].proc_type;
2764 if (other != NULL)
2765 *other = ProcListVec[i].proc_other;
2766 if (CurChildren > 0)
2767 CurChildren--;
2768 break;
2769 }
2770 }
2771
2772 if (type == PROC_CONTROL && WIFEXITED(st))
2773 {
2774 /* if so, see if we need to restart or shutdown */
2775 if (WEXITSTATUS(st) == EX_RESTART)
2776 RestartRequest = "control socket";
2777 else if (WEXITSTATUS(st) == EX_SHUTDOWN)
2778 ShutdownRequest = "control socket";
2779 }
2780 else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) &&
2781 ProcListVec[i].proc_other > -1)
2782 {
2783 /* restart this persistent runner */
2784 mark_work_group_restart(ProcListVec[i].proc_other, st);
2785 }
2786 else if (type == PROC_QUEUE)
2787 {
2788 CurRunners -= ProcListVec[i].proc_count;
2789
2790 /* CHK_CUR_RUNNERS() can't be used here: uses syslog() */
2791 if (CurRunners < 0)
2792 CurRunners = 0;
2793 }
2794 }
2795
2796 /*
2797 ** PROC_LIST_CLEAR -- clear the process list
2798 **
2799 ** Parameters:
2800 ** none.
2801 **
2802 ** Returns:
2803 ** none.
2804 **
2805 ** Side Effects:
2806 ** Sets CurChildren to zero.
2807 */
2808
2809 void
proc_list_clear()2810 proc_list_clear()
2811 {
2812 int i;
2813
2814 /* start from 1 since 0 is the daemon itself */
2815 for (i = 1; i < ProcListSize; i++)
2816 ProcListVec[i].proc_pid = NO_PID;
2817 CurChildren = 0;
2818 }
2819
2820 /*
2821 ** PROC_LIST_PROBE -- probe processes in the list to see if they still exist
2822 **
2823 ** Parameters:
2824 ** none
2825 **
2826 ** Returns:
2827 ** none
2828 **
2829 ** Side Effects:
2830 ** May decrease CurChildren.
2831 */
2832
2833 void
proc_list_probe()2834 proc_list_probe()
2835 {
2836 int i, children;
2837 int chldwasblocked;
2838 pid_t pid;
2839
2840 children = 0;
2841 chldwasblocked = sm_blocksignal(SIGCHLD);
2842
2843 /* start from 1 since 0 is the daemon itself */
2844 for (i = 1; i < ProcListSize; i++)
2845 {
2846 pid = ProcListVec[i].proc_pid;
2847 if (pid == NO_PID || pid == CurrentPid)
2848 continue;
2849 if (kill(pid, 0) < 0)
2850 {
2851 if (LogLevel > 3)
2852 sm_syslog(LOG_DEBUG, CurEnv->e_id,
2853 "proc_list_probe: lost pid %d",
2854 (int) ProcListVec[i].proc_pid);
2855 ProcListVec[i].proc_pid = NO_PID;
2856 SM_FREE(ProcListVec[i].proc_task);
2857
2858 if (ProcListVec[i].proc_type == PROC_QUEUE)
2859 {
2860 CurRunners -= ProcListVec[i].proc_count;
2861 CHK_CUR_RUNNERS("proc_list_probe", i,
2862 ProcListVec[i].proc_count);
2863 }
2864
2865 CurChildren--;
2866 }
2867 else
2868 {
2869 ++children;
2870 }
2871 }
2872 if (CurChildren < 0)
2873 CurChildren = 0;
2874 if (chldwasblocked == 0)
2875 (void) sm_releasesignal(SIGCHLD);
2876 if (LogLevel > 10 && children != CurChildren && CurrentPid == DaemonPid)
2877 {
2878 sm_syslog(LOG_ERR, NOQID,
2879 "proc_list_probe: found %d children, expected %d",
2880 children, CurChildren);
2881 }
2882 }
2883
2884 /*
2885 ** PROC_LIST_DISPLAY -- display the process list
2886 **
2887 ** Parameters:
2888 ** out -- output file pointer
2889 ** prefix -- string to output in front of each line.
2890 **
2891 ** Returns:
2892 ** none.
2893 */
2894
2895 void
proc_list_display(out,prefix)2896 proc_list_display(out, prefix)
2897 SM_FILE_T *out;
2898 char *prefix;
2899 {
2900 int i;
2901
2902 for (i = 0; i < ProcListSize; i++)
2903 {
2904 if (ProcListVec[i].proc_pid == NO_PID)
2905 continue;
2906
2907 (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n",
2908 prefix,
2909 (int) ProcListVec[i].proc_pid,
2910 ProcListVec[i].proc_task != NULL ?
2911 ProcListVec[i].proc_task : "(unknown)",
2912 (OpMode == MD_SMTP ||
2913 OpMode == MD_DAEMON ||
2914 OpMode == MD_ARPAFTP) ? "\r" : "");
2915 }
2916 }
2917
2918 /*
2919 ** PROC_LIST_SIGNAL -- send a signal to a type of process in the list
2920 **
2921 ** Parameters:
2922 ** type -- type of process to signal
2923 ** signal -- the type of signal to send
2924 **
2925 ** Returns:
2926 ** none.
2927 **
2928 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
2929 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2930 ** DOING.
2931 */
2932
2933 void
proc_list_signal(type,signal)2934 proc_list_signal(type, signal)
2935 int type;
2936 int signal;
2937 {
2938 int chldwasblocked;
2939 int alrmwasblocked;
2940 int i;
2941 pid_t mypid = getpid();
2942
2943 /* block these signals so that we may signal cleanly */
2944 chldwasblocked = sm_blocksignal(SIGCHLD);
2945 alrmwasblocked = sm_blocksignal(SIGALRM);
2946
2947 /* Find all processes of type and send signal */
2948 for (i = 0; i < ProcListSize; i++)
2949 {
2950 if (ProcListVec[i].proc_pid == NO_PID ||
2951 ProcListVec[i].proc_pid == mypid)
2952 continue;
2953 if (ProcListVec[i].proc_type != type)
2954 continue;
2955 (void) kill(ProcListVec[i].proc_pid, signal);
2956 }
2957
2958 /* restore the signals */
2959 if (alrmwasblocked == 0)
2960 (void) sm_releasesignal(SIGALRM);
2961 if (chldwasblocked == 0)
2962 (void) sm_releasesignal(SIGCHLD);
2963 }
2964
2965 /*
2966 ** COUNT_OPEN_CONNECTIONS
2967 **
2968 ** Parameters:
2969 ** hostaddr - ClientAddress
2970 **
2971 ** Returns:
2972 ** the number of open connections for this client
2973 **
2974 */
2975
2976 int
count_open_connections(hostaddr)2977 count_open_connections(hostaddr)
2978 SOCKADDR *hostaddr;
2979 {
2980 int i, n;
2981
2982 if (hostaddr == NULL)
2983 return 0;
2984
2985 /*
2986 ** This code gets called before proc_list_add() gets called,
2987 ** so we (the daemon child for this connection) have not yet
2988 ** counted ourselves. Hence initialize the counter to 1
2989 ** instead of 0 to compensate.
2990 */
2991
2992 n = 1;
2993 for (i = 0; i < ProcListSize; i++)
2994 {
2995 if (ProcListVec[i].proc_pid == NO_PID)
2996 continue;
2997 if (hostaddr->sa.sa_family !=
2998 ProcListVec[i].proc_hostaddr.sa.sa_family)
2999 continue;
3000 #if NETINET
3001 if (hostaddr->sa.sa_family == AF_INET &&
3002 (hostaddr->sin.sin_addr.s_addr ==
3003 ProcListVec[i].proc_hostaddr.sin.sin_addr.s_addr))
3004 n++;
3005 #endif
3006 #if NETINET6
3007 if (hostaddr->sa.sa_family == AF_INET6 &&
3008 IN6_ARE_ADDR_EQUAL(&(hostaddr->sin6.sin6_addr),
3009 &(ProcListVec[i].proc_hostaddr.sin6.sin6_addr)))
3010 n++;
3011 #endif
3012 }
3013 return n;
3014 }
3015
3016 #if _FFR_XCNCT
3017 /*
3018 ** XCONNECT -- get X-CONNECT info
3019 **
3020 ** Parameters:
3021 ** inchannel -- FILE to check
3022 **
3023 ** Returns:
3024 ** >0 if X-CONNECT/PROXY was used successfully (D_XCNCT*)
3025 ** 0 if X-CONNECT/PROXY was not given
3026 ** -1 on error
3027 ** -2 PROXY UNKNOWN
3028 */
3029
3030 /*
3031 ** HA proxy version 1:
3032 **
3033 ** PROXY TCP[4|6] IPv[4|6]-src-addr IPv[4|6]-dst-addr src-port dst-port\r\n
3034 ** PROXY UNKNOWN ...
3035 ** examples:
3036 ** "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n"
3037 ** "PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n"
3038 ** "PROXY UNKNOWN\r\n"
3039 */
3040
3041 int
xconnect(inchannel)3042 xconnect(inchannel)
3043 SM_FILE_T *inchannel;
3044 {
3045 int r, i;
3046 char *p, *b, delim, inp[MAXINPLINE];
3047 SOCKADDR addr;
3048 char **pvp;
3049 char pvpbuf[PSBUFSIZE];
3050 char *peerhostname; /* name of SMTP peer or "localhost" */
3051 extern ENVELOPE BlankEnvelope;
3052 #if _FFR_HAPROXY
3053 int haproxy = AF_UNSPEC;
3054 # define HAPROXY "PROXY "
3055 # define HAPROXYLEN (sizeof(HAPROXY) - 1)
3056 #endif
3057
3058 #define XCONNECT "X-CONNECT "
3059 #define XCNNCTLEN (sizeof(XCONNECT) - 1)
3060
3061 /* Ask the ruleset whether to use x-connect */
3062 pvp = NULL;
3063 peerhostname = RealHostName;
3064 if (peerhostname == NULL)
3065 peerhostname = "localhost";
3066 r = rscap("x_connect", peerhostname,
3067 anynet_ntoa(&RealHostAddr), &BlankEnvelope,
3068 &pvp, pvpbuf, sizeof(pvpbuf));
3069 if (tTd(75, 8))
3070 sm_syslog(LOG_INFO, NOQID, "x-connect: rscap=%d", r);
3071 if (r == EX_UNAVAILABLE)
3072 return 0;
3073 if (r != EX_OK)
3074 {
3075 /* ruleset error */
3076 sm_syslog(LOG_INFO, NOQID, "x-connect: rscap=%d", r);
3077 return 0;
3078 }
3079 if (pvp != NULL && pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET)
3080 {
3081 /* $#: no x-connect */
3082 if (tTd(75, 7))
3083 sm_syslog(LOG_INFO, NOQID, "x-connect: nope");
3084 return 0;
3085 }
3086
3087 #if _FFR_HAPROXY
3088 if (pvp != NULL && pvp[0] != NULL && strcasecmp(pvp[0], "haproxy1") == 0)
3089 {
3090 haproxy = AF_LOCAL;
3091 }
3092 #endif
3093
3094 # if _FFR_XCNCT > 1
3095 if (pvp != NULL && pvp[0] != NULL &&
3096 pvp[0][0] == '2' && pvp[0][1] == '2' && pvp[0][2] == '0')
3097 {
3098 char *hostname; /* my hostname ($j) */
3099
3100 hostname = macvalue('j', &BlankEnvelope);
3101 if (tTd(75, 7))
3102 sm_syslog(LOG_INFO, NOQID, "x-connect=%s", pvp[0]);
3103 message("220-%s %s", hostname != NULL ? hostname : "xconnect",
3104 pvp[1] != NULL ? pvp[1] : "waiting for xconnect");
3105 sm_io_flush(OutChannel, SM_TIME_DEFAULT);
3106 }
3107 # endif
3108
3109 p = sfgets(inp, sizeof(inp), InChannel, TimeOuts.to_nextcommand, "pre");
3110 if (tTd(75, 6))
3111 sm_syslog(LOG_INFO, NOQID, "x-connect: input=%s", p);
3112 #if _FFR_HAPROXY
3113 if (AF_UNSPEC != haproxy)
3114 {
3115 if (p == NULL || strncasecmp(p, HAPROXY, HAPROXYLEN) != 0)
3116 return -1;
3117 p += HAPROXYLEN;
3118 # define HAPUNKNOWN "UNKNOWN"
3119 # define HAPUNKNOWNLEN (sizeof(HAPUNKNOWN) - 1)
3120 if (strncasecmp(p, HAPUNKNOWN, HAPUNKNOWNLEN) == 0)
3121 {
3122 /* how to handle this? */
3123 sm_syslog(LOG_INFO, NOQID, "haproxy: input=%s, status=ignored", p);
3124 return -2;
3125 }
3126 # define HAPTCP4 "TCP4 "
3127 # define HAPTCP6 "TCP6 "
3128 # define HAPTCPNLEN (sizeof(HAPTCP4) - 1)
3129 if (strncasecmp(p, HAPTCP4, HAPTCPNLEN) == 0)
3130 haproxy = AF_INET;
3131 # if NETINET6
3132 if (strncasecmp(p, HAPTCP6, HAPTCPNLEN) == 0)
3133 haproxy = AF_INET6;
3134 # endif
3135 if (AF_LOCAL != haproxy)
3136 {
3137 p += HAPTCPNLEN;
3138 goto getip;
3139 }
3140 return -1;
3141 }
3142 #endif
3143 if (p == NULL || strncasecmp(p, XCONNECT, XCNNCTLEN) != 0)
3144 return -1;
3145 p += XCNNCTLEN;
3146 while (SM_ISSPACE(*p))
3147 p++;
3148
3149 #if _FFR_HAPROXY
3150 getip:
3151 #endif
3152 /* parameters: IPAddress [Hostname[ M]] */
3153 b = p;
3154 while (*p != '\0' && isascii(*p) &&
3155 (isalnum(*p) || *p == '.' || *p== ':'))
3156 p++;
3157 delim = *p;
3158 *p = '\0';
3159
3160 memset(&addr, '\0', sizeof(addr));
3161 addr.sin.sin_addr.s_addr = inet_addr(b);
3162 if (addr.sin.sin_addr.s_addr != INADDR_NONE)
3163 {
3164 addr.sa.sa_family = AF_INET;
3165 memcpy(&RealHostAddr, &addr, sizeof(addr));
3166 if (tTd(75, 2))
3167 sm_syslog(LOG_INFO, NOQID, "x-connect: addr=%s",
3168 anynet_ntoa(&RealHostAddr));
3169 }
3170 # if NETINET6
3171 else if ((r = inet_pton(AF_INET6, b, &addr.sin6.sin6_addr)) == 1)
3172 {
3173 addr.sa.sa_family = AF_INET6;
3174 memcpy(&RealHostAddr, &addr, sizeof(addr));
3175 }
3176 # endif
3177 else
3178 return -1;
3179 #if _FFR_HAPROXY
3180 if (AF_UNSPEC != haproxy)
3181 {
3182 /*
3183 ** dst-addr and dst-port are ignored because
3184 ** they are not really worth to check:
3185 ** IPv[4|6]-dst-addr: must be one of "our" addresses,
3186 ** dst-port: must be the DaemonPort.
3187 ** We also do not check whether
3188 ** haproxy == addr.sa.sa_family
3189 */
3190
3191 if (' ' == delim)
3192 {
3193 b = ++p;
3194 while (*p != '\0' && !SM_ISSPACE(*p))
3195 ++p;
3196 if (*p != '\0' && SM_ISSPACE(*p))
3197 ++p;
3198 if (*p != '\0')
3199 {
3200 unsigned short port;
3201
3202 port = htons(atoi(p));
3203 if (AF_INET == haproxy)
3204 RealHostAddr.sin.sin_port = port;
3205 # if NETINET6
3206 if (AF_INET6 == haproxy)
3207 RealHostAddr.sin6.sin6_port = port;
3208 # endif
3209 }
3210 }
3211 SM_FREE(RealHostName);
3212 return D_XCNCT;
3213 }
3214 #endif
3215
3216 /* more parameters? */
3217 if (delim != ' ')
3218 return D_XCNCT;
3219
3220 for (b = ++p, i = 0;
3221 *p != '\0' && isascii(*p) && (isalnum(*p) || *p == '.' || *p == '-');
3222 p++, i++)
3223 ;
3224 if (i == 0)
3225 return D_XCNCT;
3226 delim = *p;
3227 if (i > MAXNAME) /* EAI:ok */
3228 b[MAXNAME] = '\0'; /* EAI:ok */
3229 else
3230 b[i] = '\0';
3231 SM_FREE(RealHostName);
3232 RealHostName = newstr(b);
3233 if (tTd(75, 2))
3234 sm_syslog(LOG_INFO, NOQID, "x-connect: host=%s", b);
3235 *p = delim;
3236
3237 b = p;
3238 if (*p != ' ')
3239 return D_XCNCT;
3240
3241 while (*p != '\0' && SM_ISSPACE(*p))
3242 p++;
3243
3244 if (tTd(75, 4))
3245 {
3246 char *e;
3247
3248 e = strpbrk(p, "\r\n");
3249 if (e != NULL)
3250 *e = '\0';
3251 sm_syslog(LOG_INFO, NOQID, "x-connect: rest=%s", p);
3252 }
3253 if (*p == 'M')
3254 return D_XCNCT_M;
3255
3256 return D_XCNCT;
3257 }
3258 #endif /* _FFR_XCNCT */
3259