Deleted Added
full compact
1/*
2 * Copyright (c) 1998-2001 Sendmail, 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#ifndef lint
15static char id[] = "@(#)$Id: util.c,v 8.225.2.1.2.19 2001/02/22 18:56:24 gshapiro Exp $";
15static char id[] = "@(#)$Id: util.c,v 8.225.2.1.2.23 2001/05/17 18:10:18 gshapiro Exp $";
16#endif /* ! lint */
17
18#include <sendmail.h>
19#include <sysexits.h>
20
21
22static void readtimeout __P((time_t));
23
24 /*
25** STRIPQUOTES -- Strip quotes & quote bits from a string.
26**
27** Runs through a string and strips off unquoted quote
28** characters and quote bits. This is done in place.
29**
30** Parameters:
31** s -- the string to strip.
32**
33** Returns:
34** none.
35**
36** Side Effects:
37** none.
38*/
39
40void
41stripquotes(s)
42 char *s;
43{
44 register char *p;
45 register char *q;
46 register char c;
47
48 if (s == NULL)
49 return;
50
51 p = q = s;
52 do
53 {
54 c = *p++;
55 if (c == '\\')
56 c = *p++;
57 else if (c == '"')
58 continue;
59 *q++ = c;
60 } while (c != '\0');
61}
62 /*
63** ADDQUOTES -- Adds quotes & quote bits to a string.
64**
65** Runs through a string and adds characters and quote bits.
66**
67** Parameters:
68** s -- the string to modify.
69**
70** Returns:
71** pointer to quoted string.
72**
73** Side Effects:
74** none.
75**
76*/
77
78char *
79addquotes(s)
80 char *s;
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 = xalloc(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** RFC822_STRING -- Checks string for proper RFC822 string quoting.
115**
116** Runs through a string and verifies RFC822 special characters
117** are only found inside comments, quoted strings, or backslash
118** escaped. Also verified balanced quotes and parenthesis.
119**
120** Parameters:
121** s -- the string to modify.
122**
123** Returns:
124** TRUE -- if the string is RFC822 compliant.
125** FALSE -- if the string is not RFC822 compliant.
126**
127** Side Effects:
128** none.
129**
130*/
131
132bool
133rfc822_string(s)
134 char *s;
135{
136 bool quoted = FALSE;
137 int commentlev = 0;
138 char *c = s;
139
140 if (s == NULL)
141 return FALSE;
142
143 while (*c != '\0')
144 {
145 /* escaped character */
146 if (*c == '\\')
147 {
148 c++;
149 if (*c == '\0')
150 return FALSE;
151 }
152 else if (commentlev == 0 && *c == '"')
153 quoted = !quoted;
154 else if (!quoted)
155 {
156 if (*c == ')')
157 {
158 /* unbalanced ')' */
159 if (commentlev == 0)
160 return FALSE;
161 else
162 commentlev--;
163 }
164 else if (*c == '(')
165 commentlev++;
166 else if (commentlev == 0 &&
167 strchr(MustQuoteChars, *c) != NULL)
168 return FALSE;
169 }
170 c++;
171 }
172 /* unbalanced '"' or '(' */
173 if (quoted || commentlev != 0)
174 return FALSE;
175 else
176 return TRUE;
177}
178 /*
179** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
180**
181** Arbitrarily shorten (in place) an RFC822 string and rebalance
182** comments and quotes.
183**
184** Parameters:
185** string -- the string to shorten
186** length -- the maximum size, 0 if no maximum
187**
188** Returns:
189** TRUE if string is changed, FALSE otherwise
190**
191** Side Effects:
192** Changes string in place, possibly resulting
193** in a shorter string.
194*/
195
196bool
197shorten_rfc822_string(string, length)
198 char *string;
199 size_t length;
200{
201 bool backslash = FALSE;
202 bool modified = FALSE;
203 bool quoted = FALSE;
204 size_t slen;
205 int parencount = 0;
206 char *ptr = string;
207
208 /*
209 ** If have to rebalance an already short enough string,
210 ** need to do it within allocated space.
211 */
212
213 slen = strlen(string);
214 if (length == 0 || slen < length)
215 length = slen;
216
217 while (*ptr != '\0')
218 {
219 if (backslash)
220 {
221 backslash = FALSE;
222 goto increment;
223 }
224
225 if (*ptr == '\\')
226 backslash = TRUE;
227 else if (*ptr == '(')
228 {
229 if (!quoted)
230 parencount++;
231 }
232 else if (*ptr == ')')
233 {
234 if (--parencount < 0)
235 parencount = 0;
236 }
237
238 /* Inside a comment, quotes don't matter */
239 if (parencount <= 0 && *ptr == '"')
240 quoted = !quoted;
241
242increment:
243 /* Check for sufficient space for next character */
244 if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) +
245 parencount +
246 (quoted ? 1 : 0)))
247 {
248 /* Not enough, backtrack */
249 if (*ptr == '\\')
250 backslash = FALSE;
251 else if (*ptr == '(' && !quoted)
252 parencount--;
253 else if (*ptr == '"' && parencount == 0)
254 quoted = FALSE;
255 break;
256 }
257 ptr++;
258 }
259
260 /* Rebalance */
261 while (parencount-- > 0)
262 {
263 if (*ptr != ')')
264 {
265 modified = TRUE;
266 *ptr = ')';
267 }
268 ptr++;
269 }
270 if (quoted)
271 {
272 if (*ptr != '"')
273 {
274 modified = TRUE;
275 *ptr = '"';
276 }
277 ptr++;
278 }
279 if (*ptr != '\0')
280 {
281 modified = TRUE;
282 *ptr = '\0';
283 }
284 return modified;
285}
286 /*
287** FIND_CHARACTER -- find an unquoted character in an RFC822 string
288**
289** Find an unquoted, non-commented character in an RFC822
290** string and return a pointer to its location in the
291** string.
292**
293** Parameters:
294** string -- the string to search
295** character -- the character to find
296**
297** Returns:
298** pointer to the character, or
299** a pointer to the end of the line if character is not found
300*/
301
302char *
303find_character(string, character)
304 char *string;
305 int character;
306{
307 bool backslash = FALSE;
308 bool quoted = FALSE;
309 int parencount = 0;
310
311 while (string != NULL && *string != '\0')
312 {
313 if (backslash)
314 {
315 backslash = FALSE;
316 if (!quoted && character == '\\' && *string == '\\')
317 break;
318 string++;
319 continue;
320 }
321 switch (*string)
322 {
323 case '\\':
324 backslash = TRUE;
325 break;
326
327 case '(':
328 if (!quoted)
329 parencount++;
330 break;
331
332 case ')':
333 if (--parencount < 0)
334 parencount = 0;
335 break;
336 }
337
338 /* Inside a comment, nothing matters */
339 if (parencount > 0)
340 {
341 string++;
342 continue;
343 }
344
345 if (*string == '"')
346 quoted = !quoted;
347 else if (*string == character && !quoted)
348 break;
349 string++;
350 }
351
352 /* Return pointer to the character */
353 return string;
354}
355 /*
356** XALLOC -- Allocate memory and bitch wildly on failure.
357**
358** THIS IS A CLUDGE. This should be made to give a proper
359** error -- but after all, what can we do?
360**
361** Parameters:
362** sz -- size of area to allocate.
363**
364** Returns:
365** pointer to data region.
366**
367** Side Effects:
368** Memory is allocated.
369*/
370
371char *
372xalloc(sz)
373 register int sz;
374{
375 register char *p;
376
377 /* some systems can't handle size zero mallocs */
378 if (sz <= 0)
379 sz = 1;
380
381 ENTER_CRITICAL();
382 p = malloc((unsigned) sz);
383 LEAVE_CRITICAL();
384 if (p == NULL)
385 {
386 syserr("!Out of memory!!");
385 /* exit(EX_UNAVAILABLE); */
387
388 /* NOTREACHED */
389 exit(EX_UNAVAILABLE);
390 }
391 return p;
392}
393 /*
394** XREALLOC -- Reallocate memory and bitch wildly on failure.
395**
396** THIS IS A CLUDGE. This should be made to give a proper
397** error -- but after all, what can we do?
398**
399** Parameters:
400** ptr -- original area.
401** sz -- size of new area to allocate.
402**
403** Returns:
404** pointer to data region.
405**
406** Side Effects:
407** Memory is allocated.
408*/
409
410char *
411xrealloc(ptr, sz)
412 void *ptr;
413 size_t sz;
414{
415 register char *p;
416
417 /* some systems can't handle size zero mallocs */
418 if (sz <= 0)
419 sz = 1;
420
421 ENTER_CRITICAL();
422 p = realloc(ptr, (unsigned) sz);
423 LEAVE_CRITICAL();
424 if (p == NULL)
425 {
426 syserr("!Out of memory!!");
427
428 /* NOTREACHED */
429 exit(EX_UNAVAILABLE);
430 }
431 return p;
432}
433 /*
434** XCALLOC -- Allocate memory and bitch wildly on failure.
435**
436** THIS IS A CLUDGE. This should be made to give a proper
437** error -- but after all, what can we do?
438**
439** Parameters:
440** num -- number of items to allocate
441** sz -- size of new area to allocate.
442**
443** Returns:
444** pointer to data region.
445**
446** Side Effects:
447** Memory is allocated.
448*/
449
450char *
451xcalloc(num, sz)
452 size_t num;
453 size_t sz;
454{
455 register char *p;
456
457 /* some systems can't handle size zero mallocs */
458 if (num <= 0)
459 num = 1;
460 if (sz <= 0)
461 sz = 1;
462
463 ENTER_CRITICAL();
464 p = calloc((unsigned) num, (unsigned) sz);
465 LEAVE_CRITICAL();
466 if (p == NULL)
467 {
468 syserr("!Out of memory!!");
469
470 /* NOTREACHED */
471 exit(EX_UNAVAILABLE);
472 }
473 return p;
474}
475 /*
476** SM_FREE -- Free memory safely.
477**
478** Parameters:
479** ptr -- area to free
480**
481** Returns:
482** none.
483**
484** Side Effects:
485** Memory is freed.
486*/
487
488void
489sm_free(ptr)
490 void *ptr;
491{
492 ENTER_CRITICAL();
493 free(ptr);
494 LEAVE_CRITICAL();
495}
496 /*
497** COPYPLIST -- copy list of pointers.
498**
499** This routine is the equivalent of newstr for lists of
500** pointers.
501**
502** Parameters:
503** list -- list of pointers to copy.
504** Must be NULL terminated.
505** copycont -- if TRUE, copy the contents of the vector
506** (which must be a string) also.
507**
508** Returns:
509** a copy of 'list'.
510**
511** Side Effects:
512** none.
513*/
514
515char **
516copyplist(list, copycont)
517 char **list;
518 bool copycont;
519{
520 register char **vp;
521 register char **newvp;
522
523 for (vp = list; *vp != NULL; vp++)
524 continue;
525
526 vp++;
527
528 newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
529 memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof *vp);
530
531 if (copycont)
532 {
533 for (vp = newvp; *vp != NULL; vp++)
534 *vp = newstr(*vp);
535 }
536
537 return newvp;
538}
539 /*
540** COPYQUEUE -- copy address queue.
541**
542** This routine is the equivalent of newstr for address queues
543** addresses marked as QS_IS_DEAD() aren't copied
544**
545** Parameters:
546** addr -- list of address structures to copy.
547**
548** Returns:
549** a copy of 'addr'.
550**
551** Side Effects:
552** none.
553*/
554
555ADDRESS *
556copyqueue(addr)
557 ADDRESS *addr;
558{
559 register ADDRESS *newaddr;
560 ADDRESS *ret;
561 register ADDRESS **tail = &ret;
562
563 while (addr != NULL)
564 {
565 if (!QS_IS_DEAD(addr->q_state))
566 {
567 newaddr = (ADDRESS *) xalloc(sizeof *newaddr);
568 STRUCTCOPY(*addr, *newaddr);
569 *tail = newaddr;
570 tail = &newaddr->q_next;
571 }
572 addr = addr->q_next;
573 }
574 *tail = NULL;
575
576 return ret;
577}
578 /*
579** LOG_SENDMAIL_PID -- record sendmail pid and command line.
580**
581** Parameters:
582** e -- the current envelope.
583**
584** Returns:
585** none.
586**
587** Side Effects:
588** writes pidfile.
589*/
590
591void
592log_sendmail_pid(e)
593 ENVELOPE *e;
594{
595 long sff;
596 FILE *pidf;
597 char pidpath[MAXPATHLEN + 1];
598
599 /* write the pid to the log file for posterity */
600 sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
601 if (TrustedUid != 0 && RealUid == TrustedUid)
602 sff |= SFF_OPENASROOT;
603 expand(PidFile, pidpath, sizeof pidpath, e);
604 pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, 0644, sff);
605 if (pidf == NULL)
606 {
607 sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s",
608 pidpath, errstring(errno));
609 }
610 else
611 {
505 long pid;
612 pid_t pid;
613 extern char *CommandLineArgs;
614
508 pid = (long) getpid();
615 pid = getpid();
616
617 /* write the process id on line 1 */
511 fprintf(pidf, "%ld\n", pid);
618 fprintf(pidf, "%ld\n", (long) pid);
619
620 /* line 2 contains all command line flags */
621 fprintf(pidf, "%s\n", CommandLineArgs);
622
623 /* flush and close */
624 (void) fclose(pidf);
625 }
626}
627 /*
628** SET_DELIVERY_MODE -- set and record the delivery mode
629**
630** Parameters:
631** mode -- delivery mode
632** e -- the current envelope.
633**
634** Returns:
635** none.
636**
637** Side Effects:
638** sets $&{deliveryMode} macro
639*/
640
641void
642set_delivery_mode(mode, e)
643 int mode;
644 ENVELOPE *e;
645{
646 char buf[2];
647
648 e->e_sendmode = (char)mode;
649 buf[0] = (char)mode;
650 buf[1] = '\0';
651 define(macid("{deliveryMode}", NULL), newstr(buf), e);
652}
653 /*
654** PRINTAV -- print argument vector.
655**
656** Parameters:
657** av -- argument vector.
658**
659** Returns:
660** none.
661**
662** Side Effects:
663** prints av.
664*/
665
666void
667printav(av)
668 register char **av;
669{
670 while (*av != NULL)
671 {
672 if (tTd(0, 44))
673 dprintf("\n\t%08lx=", (u_long) *av);
674 else
675 (void) putchar(' ');
676 xputs(*av++);
677 }
678 (void) putchar('\n');
679}
680 /*
681** LOWER -- turn letter into lower case.
682**
683** Parameters:
684** c -- character to turn into lower case.
685**
686** Returns:
687** c, in lower case.
688**
689** Side Effects:
690** none.
691*/
692
693char
694lower(c)
695 register int c;
696{
697 return ((isascii(c) && isupper(c)) ? tolower(c) : c);
698}
699 /*
700** XPUTS -- put string doing control escapes.
701**
702** Parameters:
703** s -- string to put.
704**
705** Returns:
706** none.
707**
708** Side Effects:
709** output to stdout
710*/
711
712void
713xputs(s)
714 register const char *s;
715{
716 register int c;
717 register struct metamac *mp;
718 bool shiftout = FALSE;
719 extern struct metamac MetaMacros[];
720
721 if (s == NULL)
722 {
723 printf("%s<null>%s", TermEscape.te_rv_on, TermEscape.te_rv_off);
724 return;
725 }
726 while ((c = (*s++ & 0377)) != '\0')
727 {
728 if (shiftout)
729 {
730 printf("%s", TermEscape.te_rv_off);
731 shiftout = FALSE;
732 }
733 if (!isascii(c))
734 {
735 if (c == MATCHREPL)
736 {
737 printf("%s$", TermEscape.te_rv_on);
738 shiftout = TRUE;
739 if (*s == '\0')
740 continue;
741 c = *s++ & 0377;
742 goto printchar;
743 }
744 if (c == MACROEXPAND || c == MACRODEXPAND)
745 {
746 printf("%s$", TermEscape.te_rv_on);
747 if (c == MACRODEXPAND)
748 (void) putchar('&');
749 shiftout = TRUE;
750 if (*s == '\0')
751 continue;
752 if (strchr("=~&?", *s) != NULL)
753 (void) putchar(*s++);
754 if (bitset(0200, *s))
755 printf("{%s}", macname(bitidx(*s++)));
756 else
757 printf("%c", *s++);
758 continue;
759 }
760 for (mp = MetaMacros; mp->metaname != '\0'; mp++)
761 {
762 if ((mp->metaval & 0377) == c)
763 {
764 printf("%s$%c",
765 TermEscape.te_rv_on,
766 mp->metaname);
767 shiftout = TRUE;
768 break;
769 }
770 }
771 if (c == MATCHCLASS || c == MATCHNCLASS)
772 {
773 if (bitset(0200, *s))
774 printf("{%s}", macname(*s++ & 0377));
775 else if (*s != '\0')
776 printf("%c", *s++);
777 }
778 if (mp->metaname != '\0')
779 continue;
780
781 /* unrecognized meta character */
782 printf("%sM-", TermEscape.te_rv_on);
783 shiftout = TRUE;
784 c &= 0177;
785 }
786 printchar:
787 if (isprint(c))
788 {
789 (void) putchar(c);
790 continue;
791 }
792
793 /* wasn't a meta-macro -- find another way to print it */
794 switch (c)
795 {
796 case '\n':
797 c = 'n';
798 break;
799
800 case '\r':
801 c = 'r';
802 break;
803
804 case '\t':
805 c = 't';
806 break;
807 }
808 if (!shiftout)
809 {
810 printf("%s", TermEscape.te_rv_on);
811 shiftout = TRUE;
812 }
813 if (isprint(c))
814 {
815 (void) putchar('\\');
816 (void) putchar(c);
817 }
818 else
819 {
820 (void) putchar('^');
821 (void) putchar(c ^ 0100);
822 }
823 }
824 if (shiftout)
825 printf("%s", TermEscape.te_rv_off);
826 (void) fflush(stdout);
827}
828 /*
829** MAKELOWER -- Translate a line into lower case
830**
831** Parameters:
832** p -- the string to translate. If NULL, return is
833** immediate.
834**
835** Returns:
836** none.
837**
838** Side Effects:
839** String pointed to by p is translated to lower case.
840*/
841
842void
843makelower(p)
844 register char *p;
845{
846 register char c;
847
848 if (p == NULL)
849 return;
850 for (; (c = *p) != '\0'; p++)
851 if (isascii(c) && isupper(c))
852 *p = tolower(c);
853}
854 /*
855** BUILDFNAME -- build full name from gecos style entry.
856**
857** This routine interprets the strange entry that would appear
858** in the GECOS field of the password file.
859**
860** Parameters:
861** p -- name to build.
862** user -- the login name of this user (for &).
863** buf -- place to put the result.
864** buflen -- length of buf.
865**
866** Returns:
867** none.
868**
869** Side Effects:
870** none.
871*/
872
873void
874buildfname(gecos, user, buf, buflen)
875 register char *gecos;
876 char *user;
877 char *buf;
878 int buflen;
879{
880 register char *p;
881 register char *bp = buf;
882
883 if (*gecos == '*')
884 gecos++;
885
886 /* copy gecos, interpolating & to be full name */
887 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
888 {
889 if (bp >= &buf[buflen - 1])
890 {
891 /* buffer overflow -- just use login name */
892 snprintf(buf, buflen, "%s", user);
893 return;
894 }
895 if (*p == '&')
896 {
897 /* interpolate full name */
898 snprintf(bp, buflen - (bp - buf), "%s", user);
899 *bp = toupper(*bp);
900 bp += strlen(bp);
901 }
902 else
903 *bp++ = *p;
904 }
905 *bp = '\0';
906}
907 /*
908** FIXCRLF -- fix <CR><LF> in line.
909**
910** Looks for the <CR><LF> combination and turns it into the
911** UNIX canonical <NL> character. It only takes one line,
912** i.e., it is assumed that the first <NL> found is the end
913** of the line.
914**
915** Parameters:
916** line -- the line to fix.
917** stripnl -- if true, strip the newline also.
918**
919** Returns:
920** none.
921**
922** Side Effects:
923** line is changed in place.
924*/
925
926void
927fixcrlf(line, stripnl)
928 char *line;
929 bool stripnl;
930{
931 register char *p;
932
933 p = strchr(line, '\n');
934 if (p == NULL)
935 return;
936 if (p > line && p[-1] == '\r')
937 p--;
938 if (!stripnl)
939 *p++ = '\n';
940 *p = '\0';
941}
942 /*
943** PUTLINE -- put a line like fputs obeying SMTP conventions
944**
945** This routine always guarantees outputing a newline (or CRLF,
946** as appropriate) at the end of the string.
947**
948** Parameters:
949** l -- line to put.
950** mci -- the mailer connection information.
951**
952** Returns:
953** none
954**
955** Side Effects:
956** output of l to fp.
957*/
958
959void
960putline(l, mci)
961 register char *l;
962 register MCI *mci;
963{
964 putxline(l, strlen(l), mci, PXLF_MAPFROM);
965}
966 /*
967** PUTXLINE -- putline with flags bits.
968**
969** This routine always guarantees outputing a newline (or CRLF,
970** as appropriate) at the end of the string.
971**
972** Parameters:
973** l -- line to put.
974** len -- the length of the line.
975** mci -- the mailer connection information.
976** pxflags -- flag bits:
977** PXLF_MAPFROM -- map From_ to >From_.
978** PXLF_STRIP8BIT -- strip 8th bit.
979** PXLF_HEADER -- map bare newline in header to newline space.
980**
981** Returns:
982** none
983**
984** Side Effects:
985** output of l to fp.
986*/
987
988void
989putxline(l, len, mci, pxflags)
990 register char *l;
991 size_t len;
992 register MCI *mci;
993 int pxflags;
994{
995 bool dead = FALSE;
996 register char *p, *end;
997 int slop = 0;
998
999 /* strip out 0200 bits -- these can look like TELNET protocol */
1000 if (bitset(MCIF_7BIT, mci->mci_flags) ||
1001 bitset(PXLF_STRIP8BIT, pxflags))
1002 {
1003 register char svchar;
1004
1005 for (p = l; (svchar = *p) != '\0'; ++p)
1006 if (bitset(0200, svchar))
1007 *p = svchar &~ 0200;
1008 }
1009
1010 end = l + len;
1011 do
1012 {
1013 /* find the end of the line */
1014 p = memchr(l, '\n', end - l);
1015 if (p == NULL)
1016 p = end;
1017
1018 if (TrafficLogFile != NULL)
1019 fprintf(TrafficLogFile, "%05d >>> ", (int) getpid());
1020
1021 /* check for line overflow */
1022 while (mci->mci_mailer->m_linelimit > 0 &&
1023 (p - l + slop) > mci->mci_mailer->m_linelimit)
1024 {
1025 char *l_base = l;
1026 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
1027
1028 if (l[0] == '.' && slop == 0 &&
1029 bitnset(M_XDOT, mci->mci_mailer->m_flags))
1030 {
1031 if (putc('.', mci->mci_out) == EOF)
1032 dead = TRUE;
1033 else
1034 {
1035 /* record progress for DATA timeout */
1036 DataProgress = TRUE;
1037 }
1038 if (TrafficLogFile != NULL)
1039 (void) putc('.', TrafficLogFile);
1040 }
1041 else if (l[0] == 'F' && slop == 0 &&
1042 bitset(PXLF_MAPFROM, pxflags) &&
1043 strncmp(l, "From ", 5) == 0 &&
1044 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
1045 {
1046 if (putc('>', mci->mci_out) == EOF)
1047 dead = TRUE;
1048 else
1049 {
1050 /* record progress for DATA timeout */
1051 DataProgress = TRUE;
1052 }
1053 if (TrafficLogFile != NULL)
1054 (void) putc('>', TrafficLogFile);
1055 }
1056 if (dead)
1057 break;
1058
1059 while (l < q)
1060 {
1061 if (putc((unsigned char) *l++, mci->mci_out) ==
1062 EOF)
1063 {
1064 dead = TRUE;
1065 break;
1066 }
1067 else
1068 {
1069 /* record progress for DATA timeout */
1070 DataProgress = TRUE;
1071 }
1072 }
1073 if (dead)
1074 break;
1075
1076 if (putc('!', mci->mci_out) == EOF ||
1077 fputs(mci->mci_mailer->m_eol,
1078 mci->mci_out) == EOF ||
1079 putc(' ', mci->mci_out) == EOF)
1080 {
1081 dead = TRUE;
1082 break;
1083 }
1084 else
1085 {
1086 /* record progress for DATA timeout */
1087 DataProgress = TRUE;
1088 }
1089 if (TrafficLogFile != NULL)
1090 {
1091 for (l = l_base; l < q; l++)
1092 (void) putc((unsigned char)*l,
1093 TrafficLogFile);
1094 fprintf(TrafficLogFile, "!\n%05d >>> ",
1095 (int) getpid());
1096 }
1097 slop = 1;
1098 }
1099
1100 if (dead)
1101 break;
1102
1103 /* output last part */
1104 if (l[0] == '.' && slop == 0 &&
1105 bitnset(M_XDOT, mci->mci_mailer->m_flags))
1106 {
1107 if (putc('.', mci->mci_out) == EOF)
1108 break;
1109 else
1110 {
1111 /* record progress for DATA timeout */
1112 DataProgress = TRUE;
1113 }
1114 if (TrafficLogFile != NULL)
1115 (void) putc('.', TrafficLogFile);
1116 }
1117 else if (l[0] == 'F' && slop == 0 &&
1118 bitset(PXLF_MAPFROM, pxflags) &&
1119 strncmp(l, "From ", 5) == 0 &&
1120 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
1121 {
1122 if (putc('>', mci->mci_out) == EOF)
1123 break;
1124 else
1125 {
1126 /* record progress for DATA timeout */
1127 DataProgress = TRUE;
1128 }
1129 if (TrafficLogFile != NULL)
1130 (void) putc('>', TrafficLogFile);
1131 }
1132 for ( ; l < p; ++l)
1133 {
1134 if (TrafficLogFile != NULL)
1135 (void) putc((unsigned char)*l, TrafficLogFile);
1136 if (putc((unsigned char) *l, mci->mci_out) == EOF)
1137 {
1138 dead = TRUE;
1139 break;
1140 }
1141 else
1142 {
1143 /* record progress for DATA timeout */
1144 DataProgress = TRUE;
1145 }
1146 }
1147 if (dead)
1148 break;
1149
1150 if (TrafficLogFile != NULL)
1151 (void) putc('\n', TrafficLogFile);
1152 if (fputs(mci->mci_mailer->m_eol, mci->mci_out) == EOF)
1153 break;
1154 else
1155 {
1156 /* record progress for DATA timeout */
1157 DataProgress = TRUE;
1158 }
1159 if (l < end && *l == '\n')
1160 {
1161 if (*++l != ' ' && *l != '\t' && *l != '\0' &&
1162 bitset(PXLF_HEADER, pxflags))
1163 {
1164 if (putc(' ', mci->mci_out) == EOF)
1165 break;
1166 else
1167 {
1168 /* record progress for DATA timeout */
1169 DataProgress = TRUE;
1170 }
1171 if (TrafficLogFile != NULL)
1172 (void) putc(' ', TrafficLogFile);
1173 }
1174 }
1175 } while (l < end);
1176}
1177 /*
1178** XUNLINK -- unlink a file, doing logging as appropriate.
1179**
1180** Parameters:
1181** f -- name of file to unlink.
1182**
1183** Returns:
1184** none.
1185**
1186** Side Effects:
1187** f is unlinked.
1188*/
1189
1190void
1191xunlink(f)
1192 char *f;
1193{
1194 register int i;
1195
1196 if (LogLevel > 98)
1197 sm_syslog(LOG_DEBUG, CurEnv->e_id,
1198 "unlink %s",
1199 f);
1200
1201 i = unlink(f);
1202 if (i < 0 && LogLevel > 97)
1203 sm_syslog(LOG_DEBUG, CurEnv->e_id,
1204 "%s: unlink-fail %d",
1205 f, errno);
1206}
1207 /*
1208** SFGETS -- "safe" fgets -- times out and ignores random interrupts.
1209**
1210** Parameters:
1211** buf -- place to put the input line.
1212** siz -- size of buf.
1213** fp -- file to read from.
1214** timeout -- the timeout before error occurs.
1215** during -- what we are trying to read (for error messages).
1216**
1217** Returns:
1218** NULL on error (including timeout). This will also leave
1219** buf containing a null string.
1220** buf otherwise.
1221**
1222** Side Effects:
1223** none.
1224*/
1225
1226
1227static jmp_buf CtxReadTimeout;
1228
1229char *
1230sfgets(buf, siz, fp, timeout, during)
1231 char *buf;
1232 int siz;
1233 FILE *fp;
1234 time_t timeout;
1235 char *during;
1236{
1237 register EVENT *ev = NULL;
1238 register char *p;
1239 int save_errno;
1240
1241 if (fp == NULL)
1242 {
1243 buf[0] = '\0';
1244 return NULL;
1245 }
1246
1247 /* set the timeout */
1248 if (timeout != 0)
1249 {
1250 if (setjmp(CtxReadTimeout) != 0)
1251 {
1252 if (LogLevel > 1)
1253 sm_syslog(LOG_NOTICE, CurEnv->e_id,
1254 "timeout waiting for input from %.100s during %s",
1255 CurHostName ? CurHostName : "local",
1256 during);
1257 buf[0] = '\0';
1258#if XDEBUG
1259 checkfd012(during);
1260#endif /* XDEBUG */
1261 if (TrafficLogFile != NULL)
1262 fprintf(TrafficLogFile, "%05d <<< [TIMEOUT]\n",
1263 (int) getpid());
1264 errno = 0;
1265 return NULL;
1266 }
1267 ev = setevent(timeout, readtimeout, 0);
1268 }
1269
1270 /* try to read */
1271 p = NULL;
1272 errno = 0;
1273 while (!feof(fp) && !ferror(fp))
1274 {
1275 errno = 0;
1276 p = fgets(buf, siz, fp);
1277 if (p != NULL || errno != EINTR)
1278 break;
1279 clearerr(fp);
1280 }
1281 save_errno = errno;
1282
1283 /* clear the event if it has not sprung */
1284 clrevent(ev);
1285
1286 /* clean up the books and exit */
1287 LineNumber++;
1288 if (p == NULL)
1289 {
1290 buf[0] = '\0';
1291 if (TrafficLogFile != NULL)
1292 fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid());
1293 errno = save_errno;
1294 return NULL;
1295 }
1296 if (TrafficLogFile != NULL)
1297 fprintf(TrafficLogFile, "%05d <<< %s", (int) getpid(), buf);
1298 if (SevenBitInput)
1299 {
1300 for (p = buf; *p != '\0'; p++)
1301 *p &= ~0200;
1302 }
1303 else if (!HasEightBits)
1304 {
1305 for (p = buf; *p != '\0'; p++)
1306 {
1307 if (bitset(0200, *p))
1308 {
1309 HasEightBits = TRUE;
1310 break;
1311 }
1312 }
1313 }
1314 return buf;
1315}
1316
1317/* ARGSUSED */
1318static void
1319readtimeout(timeout)
1320 time_t timeout;
1321{
1322 /*
1323 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
1324 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
1325 ** DOING.
1326 */
1327
1328 errno = ETIMEDOUT;
1329 longjmp(CtxReadTimeout, 1);
1330}
1331 /*
1332** FGETFOLDED -- like fgets, but know about folded lines.
1333**
1334** Parameters:
1335** buf -- place to put result.
1336** n -- bytes available.
1337** f -- file to read from.
1338**
1339** Returns:
1340** input line(s) on success, NULL on error or EOF.
1341** This will normally be buf -- unless the line is too
1342** long, when it will be xalloc()ed.
1343**
1344** Side Effects:
1345** buf gets lines from f, with continuation lines (lines
1346** with leading white space) appended. CRLF's are mapped
1347** into single newlines. Any trailing NL is stripped.
1348*/
1349
1350char *
1351fgetfolded(buf, n, f)
1352 char *buf;
1353 register int n;
1354 FILE *f;
1355{
1356 register char *p = buf;
1357 char *bp = buf;
1358 register int i;
1359
1360 n--;
1361 while ((i = getc(f)) != EOF)
1362 {
1363 if (i == '\r')
1364 {
1365 i = getc(f);
1366 if (i != '\n')
1367 {
1368 if (i != EOF)
1369 (void) ungetc(i, f);
1370 i = '\r';
1371 }
1372 }
1373 if (--n <= 0)
1374 {
1375 /* allocate new space */
1376 char *nbp;
1377 int nn;
1378
1379 nn = (p - bp);
1380 if (nn < MEMCHUNKSIZE)
1381 nn *= 2;
1382 else
1383 nn += MEMCHUNKSIZE;
1384 nbp = xalloc(nn);
1385 memmove(nbp, bp, p - bp);
1386 p = &nbp[p - bp];
1387 if (bp != buf)
1274 free(bp);
1388 sm_free(bp);
1389 bp = nbp;
1390 n = nn - (p - bp);
1391 }
1392 *p++ = i;
1393 if (i == '\n')
1394 {
1395 LineNumber++;
1396 i = getc(f);
1397 if (i != EOF)
1398 (void) ungetc(i, f);
1399 if (i != ' ' && i != '\t')
1400 break;
1401 }
1402 }
1403 if (p == bp)
1404 return NULL;
1405 if (p[-1] == '\n')
1406 p--;
1407 *p = '\0';
1408 return bp;
1409}
1410 /*
1411** CURTIME -- return current time.
1412**
1413** Parameters:
1414** none.
1415**
1416** Returns:
1417** the current time.
1418**
1419** Side Effects:
1420** none.
1421*/
1422
1423time_t
1424curtime()
1425{
1426 auto time_t t;
1427
1428 (void) time(&t);
1429 return t;
1430}
1431 /*
1432** ATOBOOL -- convert a string representation to boolean.
1433**
1434** Defaults to "TRUE"
1435**
1436** Parameters:
1437** s -- string to convert. Takes "tTyY" as true,
1438** others as false.
1439**
1440** Returns:
1441** A boolean representation of the string.
1442**
1443** Side Effects:
1444** none.
1445*/
1446
1447bool
1448atobool(s)
1449 register char *s;
1450{
1451 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
1452 return TRUE;
1453 return FALSE;
1454}
1455 /*
1456** ATOOCT -- convert a string representation to octal.
1457**
1458** Parameters:
1459** s -- string to convert.
1460**
1461** Returns:
1462** An integer representing the string interpreted as an
1463** octal number.
1464**
1465** Side Effects:
1466** none.
1467*/
1468
1469int
1470atooct(s)
1471 register char *s;
1472{
1473 register int i = 0;
1474
1475 while (*s >= '0' && *s <= '7')
1476 i = (i << 3) | (*s++ - '0');
1477 return i;
1478}
1479 /*
1480** BITINTERSECT -- tell if two bitmaps intersect
1481**
1482** Parameters:
1483** a, b -- the bitmaps in question
1484**
1485** Returns:
1486** TRUE if they have a non-null intersection
1487** FALSE otherwise
1488**
1489** Side Effects:
1490** none.
1491*/
1492
1493bool
1494bitintersect(a, b)
1495 BITMAP256 a;
1496 BITMAP256 b;
1497{
1498 int i;
1499
1500 for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1501 {
1502 if ((a[i] & b[i]) != 0)
1503 return TRUE;
1504 }
1505 return FALSE;
1506}
1507 /*
1508** BITZEROP -- tell if a bitmap is all zero
1509**
1510** Parameters:
1511** map -- the bit map to check
1512**
1513** Returns:
1514** TRUE if map is all zero.
1515** FALSE if there are any bits set in map.
1516**
1517** Side Effects:
1518** none.
1519*/
1520
1521bool
1522bitzerop(map)
1523 BITMAP256 map;
1524{
1525 int i;
1526
1527 for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1528 {
1529 if (map[i] != 0)
1530 return FALSE;
1531 }
1532 return TRUE;
1533}
1534 /*
1535** STRCONTAINEDIN -- tell if one string is contained in another
1536**
1537** Parameters:
1538** a -- possible substring.
1539** b -- possible superstring.
1540**
1541** Returns:
1542** TRUE if a is contained in b.
1543** FALSE otherwise.
1544*/
1545
1546bool
1547strcontainedin(a, b)
1548 register char *a;
1549 register char *b;
1550{
1551 int la;
1552 int lb;
1553 int c;
1554
1555 la = strlen(a);
1556 lb = strlen(b);
1557 c = *a;
1558 if (isascii(c) && isupper(c))
1559 c = tolower(c);
1560 for (; lb-- >= la; b++)
1561 {
1562 if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
1563 continue;
1564 if (strncasecmp(a, b, la) == 0)
1565 return TRUE;
1566 }
1567 return FALSE;
1568}
1569 /*
1570** CHECKFD012 -- check low numbered file descriptors
1571**
1572** File descriptors 0, 1, and 2 should be open at all times.
1573** This routine verifies that, and fixes it if not true.
1574**
1575** Parameters:
1576** where -- a tag printed if the assertion failed
1577**
1578** Returns:
1579** none
1580*/
1581
1582void
1583checkfd012(where)
1584 char *where;
1585{
1586#if XDEBUG
1587 register int i;
1588
1589 for (i = 0; i < 3; i++)
1590 fill_fd(i, where);
1591#endif /* XDEBUG */
1592}
1593 /*
1594** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
1595**
1596** Parameters:
1597** fd -- file descriptor to check.
1598** where -- tag to print on failure.
1599**
1600** Returns:
1601** none.
1602*/
1603
1604void
1605checkfdopen(fd, where)
1606 int fd;
1607 char *where;
1608{
1609#if XDEBUG
1610 struct stat st;
1611
1612 if (fstat(fd, &st) < 0 && errno == EBADF)
1613 {
1614 syserr("checkfdopen(%d): %s not open as expected!", fd, where);
1615 printopenfds(TRUE);
1616 }
1617#endif /* XDEBUG */
1618}
1619 /*
1620** CHECKFDS -- check for new or missing file descriptors
1621**
1622** Parameters:
1623** where -- tag for printing. If null, take a base line.
1624**
1625** Returns:
1626** none
1627**
1628** Side Effects:
1629** If where is set, shows changes since the last call.
1630*/
1631
1632void
1633checkfds(where)
1634 char *where;
1635{
1636 int maxfd;
1637 register int fd;
1638 bool printhdr = TRUE;
1639 int save_errno = errno;
1640 static BITMAP256 baseline;
1641 extern int DtableSize;
1642
1643 if (DtableSize > BITMAPBITS)
1644 maxfd = BITMAPBITS;
1645 else
1646 maxfd = DtableSize;
1647 if (where == NULL)
1648 clrbitmap(baseline);
1649
1650 for (fd = 0; fd < maxfd; fd++)
1651 {
1652 struct stat stbuf;
1653
1654 if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
1655 {
1656 if (!bitnset(fd, baseline))
1657 continue;
1658 clrbitn(fd, baseline);
1659 }
1660 else if (!bitnset(fd, baseline))
1661 setbitn(fd, baseline);
1662 else
1663 continue;
1664
1665 /* file state has changed */
1666 if (where == NULL)
1667 continue;
1668 if (printhdr)
1669 {
1670 sm_syslog(LOG_DEBUG, CurEnv->e_id,
1671 "%s: changed fds:",
1672 where);
1673 printhdr = FALSE;
1674 }
1675 dumpfd(fd, TRUE, TRUE);
1676 }
1677 errno = save_errno;
1678}
1679 /*
1680** PRINTOPENFDS -- print the open file descriptors (for debugging)
1681**
1682** Parameters:
1683** logit -- if set, send output to syslog; otherwise
1684** print for debugging.
1685**
1686** Returns:
1687** none.
1688*/
1689
1690#if NETINET || NETINET6
1691# include <arpa/inet.h>
1692#endif /* NETINET || NETINET6 */
1693
1694void
1695printopenfds(logit)
1696 bool logit;
1697{
1698 register int fd;
1699 extern int DtableSize;
1700
1701 for (fd = 0; fd < DtableSize; fd++)
1702 dumpfd(fd, FALSE, logit);
1703}
1704 /*
1705** DUMPFD -- dump a file descriptor
1706**
1707** Parameters:
1708** fd -- the file descriptor to dump.
1709** printclosed -- if set, print a notification even if
1710** it is closed; otherwise print nothing.
1711** logit -- if set, send output to syslog instead of stdout.
1712*/
1713
1714void
1715dumpfd(fd, printclosed, logit)
1716 int fd;
1717 bool printclosed;
1718 bool logit;
1719{
1720 register char *p;
1721 char *hp;
1722#ifdef S_IFSOCK
1723 SOCKADDR sa;
1724#endif /* S_IFSOCK */
1725 auto SOCKADDR_LEN_T slen;
1726 int i;
1727#if STAT64 > 0
1728 struct stat64 st;
1729#else /* STAT64 > 0 */
1730 struct stat st;
1731#endif /* STAT64 > 0 */
1732 char buf[200];
1733
1734 p = buf;
1735 snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
1736 p += strlen(p);
1737
1738 if (
1739#if STAT64 > 0
1740 fstat64(fd, &st)
1741#else /* STAT64 > 0 */
1742 fstat(fd, &st)
1743#endif /* STAT64 > 0 */
1744 < 0)
1745 {
1746 if (errno != EBADF)
1747 {
1748 snprintf(p, SPACELEFT(buf, p), "CANNOT STAT (%s)",
1749 errstring(errno));
1750 goto printit;
1751 }
1752 else if (printclosed)
1753 {
1754 snprintf(p, SPACELEFT(buf, p), "CLOSED");
1755 goto printit;
1756 }
1757 return;
1758 }
1759
1760 i = fcntl(fd, F_GETFL, NULL);
1761 if (i != -1)
1762 {
1763 snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
1764 p += strlen(p);
1765 }
1766
1767 snprintf(p, SPACELEFT(buf, p), "mode=%o: ", (int) st.st_mode);
1768 p += strlen(p);
1769 switch (st.st_mode & S_IFMT)
1770 {
1771#ifdef S_IFSOCK
1772 case S_IFSOCK:
1773 snprintf(p, SPACELEFT(buf, p), "SOCK ");
1774 p += strlen(p);
1775 memset(&sa, '\0', sizeof sa);
1776 slen = sizeof sa;
1777 if (getsockname(fd, &sa.sa, &slen) < 0)
1778 snprintf(p, SPACELEFT(buf, p), "(%s)",
1779 errstring(errno));
1780 else
1781 {
1782 hp = hostnamebyanyaddr(&sa);
1783 if (hp == NULL)
1784 {
1785 /* EMPTY */
1786 /* do nothing */
1787 }
1788# if NETINET
1789 else if (sa.sa.sa_family == AF_INET)
1790 snprintf(p, SPACELEFT(buf, p), "%s/%d",
1791 hp, ntohs(sa.sin.sin_port));
1792# endif /* NETINET */
1793# if NETINET6
1794 else if (sa.sa.sa_family == AF_INET6)
1795 snprintf(p, SPACELEFT(buf, p), "%s/%d",
1796 hp, ntohs(sa.sin6.sin6_port));
1797# endif /* NETINET6 */
1798 else
1799 snprintf(p, SPACELEFT(buf, p), "%s", hp);
1800 }
1801 p += strlen(p);
1802 snprintf(p, SPACELEFT(buf, p), "->");
1803 p += strlen(p);
1804 slen = sizeof sa;
1805 if (getpeername(fd, &sa.sa, &slen) < 0)
1806 snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno));
1807 else
1808 {
1809 hp = hostnamebyanyaddr(&sa);
1810 if (hp == NULL)
1811 {
1812 /* EMPTY */
1813 /* do nothing */
1814 }
1815# if NETINET
1816 else if (sa.sa.sa_family == AF_INET)
1817 snprintf(p, SPACELEFT(buf, p), "%s/%d",
1818 hp, ntohs(sa.sin.sin_port));
1819# endif /* NETINET */
1820# if NETINET6
1821 else if (sa.sa.sa_family == AF_INET6)
1822 snprintf(p, SPACELEFT(buf, p), "%s/%d",
1823 hp, ntohs(sa.sin6.sin6_port));
1824# endif /* NETINET6 */
1825 else
1826 snprintf(p, SPACELEFT(buf, p), "%s", hp);
1827 }
1828 break;
1829#endif /* S_IFSOCK */
1830
1831 case S_IFCHR:
1832 snprintf(p, SPACELEFT(buf, p), "CHR: ");
1833 p += strlen(p);
1834 goto defprint;
1835
1836 case S_IFBLK:
1837 snprintf(p, SPACELEFT(buf, p), "BLK: ");
1838 p += strlen(p);
1839 goto defprint;
1840
1841#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1842 case S_IFIFO:
1843 snprintf(p, SPACELEFT(buf, p), "FIFO: ");
1844 p += strlen(p);
1845 goto defprint;
1846#endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */
1847
1848#ifdef S_IFDIR
1849 case S_IFDIR:
1850 snprintf(p, SPACELEFT(buf, p), "DIR: ");
1851 p += strlen(p);
1852 goto defprint;
1853#endif /* S_IFDIR */
1854
1855#ifdef S_IFLNK
1856 case S_IFLNK:
1857 snprintf(p, SPACELEFT(buf, p), "LNK: ");
1858 p += strlen(p);
1859 goto defprint;
1860#endif /* S_IFLNK */
1861
1862 default:
1863defprint:
1864 /*CONSTCOND*/
1865 if (sizeof st.st_ino > sizeof (long))
1866 snprintf(p, SPACELEFT(buf, p),
1867 "dev=%d/%d, ino=%s, nlink=%d, u/gid=%d/%d, ",
1868 major(st.st_dev), minor(st.st_dev),
1869 quad_to_string(st.st_ino),
1870 (int) st.st_nlink, (int) st.st_uid,
1871 (int) st.st_gid);
1872 else
1873 snprintf(p, SPACELEFT(buf, p),
1874 "dev=%d/%d, ino=%lu, nlink=%d, u/gid=%d/%d, ",
1875 major(st.st_dev), minor(st.st_dev),
1876 (unsigned long) st.st_ino,
1877 (int) st.st_nlink, (int) st.st_uid,
1878 (int) st.st_gid);
1879 /*CONSTCOND*/
1880 if (sizeof st.st_size > sizeof (long))
1881 snprintf(p, SPACELEFT(buf, p), "size=%s",
1882 quad_to_string(st.st_size));
1883 else
1884 snprintf(p, SPACELEFT(buf, p), "size=%lu",
1885 (unsigned long) st.st_size);
1886 break;
1887 }
1888
1889printit:
1890 if (logit)
1891 sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
1892 "%.800s", buf);
1893 else
1894 printf("%s\n", buf);
1895}
1896 /*
1897** SHORTEN_HOSTNAME -- strip local domain information off of hostname.
1898**
1899** Parameters:
1900** host -- the host to shorten (stripped in place).
1901**
1902** Returns:
1903** place where string was trunacted, NULL if not truncated.
1904*/
1905
1906char *
1907shorten_hostname(host)
1908 char host[];
1909{
1910 register char *p;
1911 char *mydom;
1912 int i;
1913 bool canon = FALSE;
1914
1915 /* strip off final dot */
1916 p = &host[strlen(host) - 1];
1917 if (*p == '.')
1918 {
1919 *p = '\0';
1920 canon = TRUE;
1921 }
1922
1923 /* see if there is any domain at all -- if not, we are done */
1924 p = strchr(host, '.');
1925 if (p == NULL)
1926 return NULL;
1927
1928 /* yes, we have a domain -- see if it looks like us */
1929 mydom = macvalue('m', CurEnv);
1930 if (mydom == NULL)
1931 mydom = "";
1932 i = strlen(++p);
1933 if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 &&
1934 (mydom[i] == '.' || mydom[i] == '\0'))
1935 {
1936 *--p = '\0';
1937 return p;
1938 }
1939 return NULL;
1940}
1941 /*
1942** PROG_OPEN -- open a program for reading
1943**
1944** Parameters:
1945** argv -- the argument list.
1946** pfd -- pointer to a place to store the file descriptor.
1947** e -- the current envelope.
1948**
1949** Returns:
1950** pid of the process -- -1 if it failed.
1951*/
1952
1839int
1953pid_t
1954prog_open(argv, pfd, e)
1955 char **argv;
1956 int *pfd;
1957 ENVELOPE *e;
1958{
1845 int pid;
1959 pid_t pid;
1960 int i;
1961 int save_errno;
1962 int fdv[2];
1963 char *p, *q;
1964 char buf[MAXLINE + 1];
1965 extern int DtableSize;
1966
1967 if (pipe(fdv) < 0)
1968 {
1969 syserr("%s: cannot create pipe for stdout", argv[0]);
1970 return -1;
1971 }
1972 pid = fork();
1973 if (pid < 0)
1974 {
1975 syserr("%s: cannot fork", argv[0]);
1976 (void) close(fdv[0]);
1977 (void) close(fdv[1]);
1978 return -1;
1979 }
1980 if (pid > 0)
1981 {
1982 /* parent */
1983 (void) close(fdv[1]);
1984 *pfd = fdv[0];
1985 return pid;
1986 }
1987
1988 /* child -- close stdin */
1989 (void) close(0);
1990
1991 /* Reset global flags */
1992 RestartRequest = NULL;
1993 ShutdownRequest = NULL;
1994 PendingSignal = 0;
1995
1996 /* stdout goes back to parent */
1997 (void) close(fdv[0]);
1998 if (dup2(fdv[1], 1) < 0)
1999 {
2000 syserr("%s: cannot dup2 for stdout", argv[0]);
2001 _exit(EX_OSERR);
2002 }
2003 (void) close(fdv[1]);
2004
2005 /* stderr goes to transcript if available */
2006 if (e->e_xfp != NULL)
2007 {
2008 int xfd;
2009
2010 xfd = fileno(e->e_xfp);
2011 if (xfd >= 0 && dup2(xfd, 2) < 0)
2012 {
2013 syserr("%s: cannot dup2 for stderr", argv[0]);
2014 _exit(EX_OSERR);
2015 }
2016 }
2017
2018 /* this process has no right to the queue file */
2019 if (e->e_lockfp != NULL)
2020 (void) close(fileno(e->e_lockfp));
2021
2022 /* chroot to the program mailer directory, if defined */
2023 if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL)
2024 {
2025 expand(ProgMailer->m_rootdir, buf, sizeof buf, e);
2026 if (chroot(buf) < 0)
2027 {
2028 syserr("prog_open: cannot chroot(%s)", buf);
2029 exit(EX_TEMPFAIL);
2030 }
2031 if (chdir("/") < 0)
2032 {
2033 syserr("prog_open: cannot chdir(/)");
2034 exit(EX_TEMPFAIL);
2035 }
2036 }
2037
2038 /* run as default user */
2039 endpwent();
2040 if (setgid(DefGid) < 0 && geteuid() == 0)
2041 {
2042 syserr("prog_open: setgid(%ld) failed", (long) DefGid);
2043 exit(EX_TEMPFAIL);
2044 }
2045 if (setuid(DefUid) < 0 && geteuid() == 0)
2046 {
2047 syserr("prog_open: setuid(%ld) failed", (long) DefUid);
2048 exit(EX_TEMPFAIL);
2049 }
2050
2051 /* run in some directory */
2052 if (ProgMailer != NULL)
2053 p = ProgMailer->m_execdir;
2054 else
2055 p = NULL;
2056 for (; p != NULL; p = q)
2057 {
2058 q = strchr(p, ':');
2059 if (q != NULL)
2060 *q = '\0';
2061 expand(p, buf, sizeof buf, e);
2062 if (q != NULL)
2063 *q++ = ':';
2064 if (buf[0] != '\0' && chdir(buf) >= 0)
2065 break;
2066 }
2067 if (p == NULL)
2068 {
2069 /* backup directories */
2070 if (chdir("/tmp") < 0)
2071 (void) chdir("/");
2072 }
2073
2074 /* arrange for all the files to be closed */
2075 for (i = 3; i < DtableSize; i++)
2076 {
2077 register int j;
2078
2079 if ((j = fcntl(i, F_GETFD, 0)) != -1)
2080 (void) fcntl(i, F_SETFD, j | FD_CLOEXEC);
2081 }
2082
2083 /* now exec the process */
2084 (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
2085
2086 /* woops! failed */
2087 save_errno = errno;
2088 syserr("%s: cannot exec", argv[0]);
2089 if (transienterror(save_errno))
2090 _exit(EX_OSERR);
2091 _exit(EX_CONFIG);
2092 return -1; /* avoid compiler warning on IRIX */
2093}
2094 /*
2095** GET_COLUMN -- look up a Column in a line buffer
2096**
2097** Parameters:
2098** line -- the raw text line to search.
2099** col -- the column number to fetch.
2100** delim -- the delimiter between columns. If null,
2101** use white space.
2102** buf -- the output buffer.
2103** buflen -- the length of buf.
2104**
2105** Returns:
2106** buf if successful.
2107** NULL otherwise.
2108*/
2109
2110char *
2111get_column(line, col, delim, buf, buflen)
2112 char line[];
2113 int col;
2114 int delim;
2115 char buf[];
2116 int buflen;
2117{
2118 char *p;
2119 char *begin, *end;
2120 int i;
2121 char delimbuf[4];
2122
2123 if ((char)delim == '\0')
2124 (void) strlcpy(delimbuf, "\n\t ", sizeof delimbuf);
2125 else
2126 {
2127 delimbuf[0] = (char)delim;
2128 delimbuf[1] = '\0';
2129 }
2130
2131 p = line;
2132 if (*p == '\0')
2133 return NULL; /* line empty */
2134 if (*p == (char)delim && col == 0)
2135 return NULL; /* first column empty */
2136
2137 begin = line;
2138
2139 if (col == 0 && (char)delim == '\0')
2140 {
2141 while (*begin != '\0' && isascii(*begin) && isspace(*begin))
2142 begin++;
2143 }
2144
2145 for (i = 0; i < col; i++)
2146 {
2147 if ((begin = strpbrk(begin, delimbuf)) == NULL)
2148 return NULL; /* no such column */
2149 begin++;
2150 if ((char)delim == '\0')
2151 {
2152 while (*begin != '\0' && isascii(*begin) && isspace(*begin))
2153 begin++;
2154 }
2155 }
2156
2157 end = strpbrk(begin, delimbuf);
2158 if (end == NULL)
2159 i = strlen(begin);
2160 else
2161 i = end - begin;
2162 if (i >= buflen)
2163 i = buflen - 1;
2164 (void) strlcpy(buf, begin, i + 1);
2165 return buf;
2166}
2167 /*
2168** CLEANSTRCPY -- copy string keeping out bogus characters
2169**
2170** Parameters:
2171** t -- "to" string.
2172** f -- "from" string.
2173** l -- length of space available in "to" string.
2174**
2175** Returns:
2176** none.
2177*/
2178
2179void
2180cleanstrcpy(t, f, l)
2181 register char *t;
2182 register char *f;
2183 int l;
2184{
2185 /* check for newlines and log if necessary */
2186 (void) denlstring(f, TRUE, TRUE);
2187
2188 if (l <= 0)
2189 syserr("!cleanstrcpy: length == 0");
2190
2191 l--;
2192 while (l > 0 && *f != '\0')
2193 {
2194 if (isascii(*f) &&
2195 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
2196 {
2197 l--;
2198 *t++ = *f;
2199 }
2200 f++;
2201 }
2202 *t = '\0';
2203}
2204
2205 /*
2206** DENLSTRING -- convert newlines in a string to spaces
2207**
2208** Parameters:
2209** s -- the input string
2210** strict -- if set, don't permit continuation lines.
2211** logattacks -- if set, log attempted attacks.
2212**
2213** Returns:
2214** A pointer to a version of the string with newlines
2215** mapped to spaces. This should be copied.
2216*/
2217
2218char *
2219denlstring(s, strict, logattacks)
2220 char *s;
2221 bool strict;
2222 bool logattacks;
2223{
2224 register char *p;
2225 int l;
2226 static char *bp = NULL;
2227 static int bl = 0;
2228
2229 p = s;
2230 while ((p = strchr(p, '\n')) != NULL)
2231 if (strict || (*++p != ' ' && *p != '\t'))
2232 break;
2233 if (p == NULL)
2234 return s;
2235
2236 l = strlen(s) + 1;
2237 if (bl < l)
2238 {
2239 /* allocate more space */
2240 if (bp != NULL)
2122 free(bp);
2241 sm_free(bp);
2242 bp = xalloc(l);
2243 bl = l;
2244 }
2245 (void) strlcpy(bp, s, l);
2246 for (p = bp; (p = strchr(p, '\n')) != NULL; )
2247 *p++ = ' ';
2248
2249 if (logattacks)
2250 {
2251 sm_syslog(LOG_NOTICE, CurEnv->e_id,
2252 "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
2253 RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
2254 shortenstring(bp, MAXSHORTSTR));
2255 }
2256
2257 return bp;
2258}
2259 /*
2260** PATH_IS_DIR -- check to see if file exists and is a directory.
2261**
2262** There are some additional checks for security violations in
2263** here. This routine is intended to be used for the host status
2264** support.
2265**
2266** Parameters:
2267** pathname -- pathname to check for directory-ness.
2268** createflag -- if set, create directory if needed.
2269**
2270** Returns:
2271** TRUE -- if the indicated pathname is a directory
2272** FALSE -- otherwise
2273*/
2274
2275int
2276path_is_dir(pathname, createflag)
2277 char *pathname;
2278 bool createflag;
2279{
2280 struct stat statbuf;
2281
2282#if HASLSTAT
2283 if (lstat(pathname, &statbuf) < 0)
2284#else /* HASLSTAT */
2285 if (stat(pathname, &statbuf) < 0)
2286#endif /* HASLSTAT */
2287 {
2288 if (errno != ENOENT || !createflag)
2289 return FALSE;
2290 if (mkdir(pathname, 0755) < 0)
2291 return FALSE;
2292 return TRUE;
2293 }
2294 if (!S_ISDIR(statbuf.st_mode))
2295 {
2296 errno = ENOTDIR;
2297 return FALSE;
2298 }
2299
2300 /* security: don't allow writable directories */
2301 if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
2302 {
2303 errno = EACCES;
2304 return FALSE;
2305 }
2306
2307 return TRUE;
2308}
2309 /*
2310** PROC_LIST_ADD -- add process id to list of our children
2311**
2312** Parameters:
2313** pid -- pid to add to list.
2314** task -- task of pid.
2315** type -- type of process.
2316**
2317** Returns:
2318** none
2319*/
2320
2202static struct procs *ProcListVec = NULL;
2321static struct procs *volatile ProcListVec = NULL;
2322static int ProcListSize = 0;
2323
2324void
2325proc_list_add(pid, task, type)
2326 pid_t pid;
2327 char *task;
2328 int type;
2329{
2330 int i;
2331
2332 for (i = 0; i < ProcListSize; i++)
2333 {
2334 if (ProcListVec[i].proc_pid == NO_PID)
2335 break;
2336 }
2337 if (i >= ProcListSize)
2338 {
2339 /* probe the existing vector to avoid growing infinitely */
2340 proc_list_probe();
2341
2342 /* now scan again */
2343 for (i = 0; i < ProcListSize; i++)
2344 {
2345 if (ProcListVec[i].proc_pid == NO_PID)
2346 break;
2347 }
2348 }
2349 if (i >= ProcListSize)
2350 {
2351 /* grow process list */
2352 struct procs *npv;
2353
2354 npv = (struct procs *) xalloc((sizeof *npv) *
2355 (ProcListSize + PROC_LIST_SEG));
2356 if (ProcListSize > 0)
2357 {
2358 memmove(npv, ProcListVec,
2359 ProcListSize * sizeof (struct procs));
2241 free(ProcListVec);
2360 sm_free(ProcListVec);
2361 }
2362 for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
2363 {
2364 npv[i].proc_pid = NO_PID;
2365 npv[i].proc_task = NULL;
2366 npv[i].proc_type = PROC_NONE;
2367 }
2368 i = ProcListSize;
2369 ProcListSize += PROC_LIST_SEG;
2370 ProcListVec = npv;
2371 }
2372 ProcListVec[i].proc_pid = pid;
2373 if (ProcListVec[i].proc_task != NULL)
2255 free(ProcListVec[i].proc_task);
2374 sm_free(ProcListVec[i].proc_task);
2375 ProcListVec[i].proc_task = newstr(task);
2376 ProcListVec[i].proc_type = type;
2377
2378 /* if process adding itself, it's not a child */
2379 if (pid != getpid())
2380 CurChildren++;
2381}
2382 /*
2383** PROC_LIST_SET -- set pid task in process list
2384**
2385** Parameters:
2386** pid -- pid to set
2387** task -- task of pid
2388**
2389** Returns:
2390** none.
2391*/
2392
2393void
2394proc_list_set(pid, task)
2395 pid_t pid;
2396 char *task;
2397{
2398 int i;
2399
2400 for (i = 0; i < ProcListSize; i++)
2401 {
2402 if (ProcListVec[i].proc_pid == pid)
2403 {
2404 if (ProcListVec[i].proc_task != NULL)
2286 free(ProcListVec[i].proc_task);
2405 sm_free(ProcListVec[i].proc_task);
2406 ProcListVec[i].proc_task = newstr(task);
2407 break;
2408 }
2409 }
2410}
2411 /*
2412** PROC_LIST_DROP -- drop pid from process list
2413**
2414** Parameters:
2415** pid -- pid to drop
2416**
2417** Returns:
2418** type of process
2419**
2420** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
2421** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2422** DOING.
2423*/
2424
2425int
2426proc_list_drop(pid)
2427 pid_t pid;
2428{
2429 int i;
2430 int type = PROC_NONE;
2431
2432 for (i = 0; i < ProcListSize; i++)
2433 {
2434 if (ProcListVec[i].proc_pid == pid)
2435 {
2436 ProcListVec[i].proc_pid = NO_PID;
2437 type = ProcListVec[i].proc_type;
2438 break;
2439 }
2440 }
2441 if (CurChildren > 0)
2442 CurChildren--;
2443
2444
2445 return type;
2446}
2447 /*
2448** PROC_LIST_CLEAR -- clear the process list
2449**
2450** Parameters:
2451** none.
2452**
2453** Returns:
2454** none.
2455*/
2456
2457void
2458proc_list_clear()
2459{
2460 int i;
2461
2462 /* start from 1 since 0 is the daemon itself */
2463 for (i = 1; i < ProcListSize; i++)
2464 {
2465 ProcListVec[i].proc_pid = NO_PID;
2466 }
2467 CurChildren = 0;
2468}
2469 /*
2470** PROC_LIST_PROBE -- probe processes in the list to see if they still exist
2471**
2472** Parameters:
2473** none
2474**
2475** Returns:
2476** none
2477*/
2478
2479void
2480proc_list_probe()
2481{
2482 int i;
2483
2484 /* start from 1 since 0 is the daemon itself */
2485 for (i = 1; i < ProcListSize; i++)
2486 {
2487 if (ProcListVec[i].proc_pid == NO_PID)
2488 continue;
2489 if (kill(ProcListVec[i].proc_pid, 0) < 0)
2490 {
2491 if (LogLevel > 3)
2492 sm_syslog(LOG_DEBUG, CurEnv->e_id,
2493 "proc_list_probe: lost pid %d",
2494 (int) ProcListVec[i].proc_pid);
2495 ProcListVec[i].proc_pid = NO_PID;
2496 CurChildren--;
2497 }
2498 }
2499 if (CurChildren < 0)
2500 CurChildren = 0;
2501}
2502 /*
2503** PROC_LIST_DISPLAY -- display the process list
2504**
2505** Parameters:
2506** out -- output file pointer
2507**
2508** Returns:
2509** none.
2510*/
2511
2512void
2513proc_list_display(out)
2514 FILE *out;
2515{
2516 int i;
2517
2518 for (i = 0; i < ProcListSize; i++)
2519 {
2520 if (ProcListVec[i].proc_pid == NO_PID)
2521 continue;
2522
2523 fprintf(out, "%d %s%s\n", (int) ProcListVec[i].proc_pid,
2524 ProcListVec[i].proc_task != NULL ?
2525 ProcListVec[i].proc_task : "(unknown)",
2526 (OpMode == MD_SMTP ||
2527 OpMode == MD_DAEMON ||
2528 OpMode == MD_ARPAFTP) ? "\r" : "");
2529 }
2530}
2531 /*
2532** SM_STRCASECMP -- 8-bit clean version of strcasecmp
2533**
2534** Thank you, vendors, for making this all necessary.
2535*/
2536
2537/*
2538 * Copyright (c) 1987, 1993
2539 * The Regents of the University of California. All rights reserved.
2540 *
2541 * Redistribution and use in source and binary forms, with or without
2542 * modification, are permitted provided that the following conditions
2543 * are met:
2544 * 1. Redistributions of source code must retain the above copyright
2545 * notice, this list of conditions and the following disclaimer.
2546 * 2. Redistributions in binary form must reproduce the above copyright
2547 * notice, this list of conditions and the following disclaimer in the
2548 * documentation and/or other materials provided with the distribution.
2549 * 3. All advertising materials mentioning features or use of this software
2550 * must display the following acknowledgement:
2551 * This product includes software developed by the University of
2552 * California, Berkeley and its contributors.
2553 * 4. Neither the name of the University nor the names of its contributors
2554 * may be used to endorse or promote products derived from this software
2555 * without specific prior written permission.
2556 *
2557 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2558 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2559 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2560 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2561 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2562 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2563 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2564 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2565 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2566 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2567 * SUCH DAMAGE.
2568 */
2569
2570#if defined(LIBC_SCCS) && !defined(lint)
2571static char sccsid[] = "@(#)strcasecmp.c 8.1 (Berkeley) 6/4/93";
2572#endif /* defined(LIBC_SCCS) && !defined(lint) */
2573
2574/*
2575 * This array is designed for mapping upper and lower case letter
2576 * together for a case independent comparison. The mappings are
2577 * based upon ascii character sequences.
2578 */
2579static const u_char charmap[] = {
2580 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
2581 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
2582 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
2583 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
2584 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
2585 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
2586 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
2587 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
2588 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
2589 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
2590 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
2591 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
2592 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
2593 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
2594 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
2595 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
2596 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
2597 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
2598 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
2599 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
2600 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
2601 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
2602 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
2603 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
2604 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
2605 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
2606 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
2607 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
2608 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
2609 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
2610 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
2611 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
2612};
2613
2614int
2615sm_strcasecmp(s1, s2)
2616 const char *s1, *s2;
2617{
2618 register const u_char *cm = charmap,
2619 *us1 = (const u_char *)s1,
2620 *us2 = (const u_char *)s2;
2621
2622 while (cm[*us1] == cm[*us2++])
2623 if (*us1++ == '\0')
2624 return 0;
2625 return (cm[*us1] - cm[*--us2]);
2626}
2627
2628int
2629sm_strncasecmp(s1, s2, n)
2630 const char *s1, *s2;
2631 register size_t n;
2632{
2633 if (n != 0) {
2634 register const u_char *cm = charmap,
2635 *us1 = (const u_char *)s1,
2636 *us2 = (const u_char *)s2;
2637
2638 do {
2639 if (cm[*us1] != cm[*us2++])
2640 return (cm[*us1] - cm[*--us2]);
2641 if (*us1++ == '\0')
2642 break;
2643 } while (--n != 0);
2644 }
2645 return 0;
2646}