]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/src/util.c
Merge commit '850ef5ae11d69ea3381bd310f564f025fc8caea3'
[FreeBSD/FreeBSD.git] / contrib / sendmail / src / util.c
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 *
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
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
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
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 *
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
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
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
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 **
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 *
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
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
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
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
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
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
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 *
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
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
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
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
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
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 *
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 *
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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 *
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
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 *
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
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
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
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
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
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
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
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
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
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
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
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 */