vrfy-990522.orig/0040700000175000017500000000000006773061000013552 5ustar herbertgreathanvrfy-990522.orig/Makefile0100600000175000017500000001205406416417670015227 0ustar herbertgreathan# @(#)Makefile e07@nikhef.nl (Eric Wassenaar) 971007 # ---------------------------------------------------------------------- # Adapt the installation directories to your local standards. # ---------------------------------------------------------------------- # This is where the vrfy executable will go. DESTBIN = /usr/local/bin # This is where the vrfy manual page will go. DESTMAN = /usr/local/man BINDIR = $(DESTBIN) MANDIR = $(DESTMAN)/man1 # ---------------------------------------------------------------------- # Special compilation options may be needed only on a few platforms. # See also the header file port.h for portability issues. # ---------------------------------------------------------------------- #if defined(_AIX) SYSDEFS = -D_BSD -D_BSD_INCLUDES -U__STR__ -DBIT_ZERO_ON_LEFT #endif #if defined(SCO) && You have either OpenDeskTop 3 or OpenServer 5 SYSDEFS = -DSYSV #endif #if defined(solaris) && You do not want to use BSD compatibility mode SYSDEFS = -DSYSV #endif #if defined(solaris) && You are using its default broken resolver library SYSDEFS = -DNO_YP_LOOKUP #endif SYSDEFS = # ---------------------------------------------------------------------- # Configuration definitions. # Compiled-in defaults can be overruled by environment variables. # See also the header file conf.h for further details. # ---------------------------------------------------------------------- #if defined(BIND_49) && __res_state is still shipped as struct state CONFIGDEFS = -DOLD_RES_STATE #endif # Define LOCALHOST if "localhost" is not running the sendmail daemon. CONFIGDEFS = -DLOCALHOST=\"mailhost\" # Define UUCPRELAY if you have a better place to send uucp addresses. CONFIGDEFS = -DUUCPRELAY=LOCALHOST # Define BITNETRELAY if you know where to send earn/bitnet addresses. CONFIGDEFS = -DBITNETRELAY=LOCALHOST # Define SINGLERELAY as the host where to send unqualified host names. CONFIGDEFS = -DSINGLERELAY=LOCALHOST CONFIGDEFS = # ---------------------------------------------------------------------- # Compilation definitions. # ---------------------------------------------------------------------- DEFS = $(CONFIGDEFS) $(SYSDEFS) COPTS = COPTS = -O CFLAGS = $(COPTS) $(DEFS) # Select your favorite compiler. CC = /usr/ucb/cc #if defined(solaris) && BSD CC = /bin/cc -arch m68k -arch i386 #if defined(next) CC = /bin/cc CC = cc # ---------------------------------------------------------------------- # Linking definitions. # libresolv.a should contain the resolver library of BIND 4.8.2 or later. # Link it in only if your default library is different. # SCO keeps its own default resolver library inside libsocket.a # # lib44bsd.a contains various utility routines, and comes with BIND 4.9.* # You may need it if you link with the 4.9.* resolver library. # # libnet.a contains the getnet...() getserv...() getproto...() calls. # It is safe to leave it out and use your default library. # With BIND 4.9.3 the getnet...() calls are in the resolver library. # ---------------------------------------------------------------------- RES = -lsocket #if defined(SCO) && default RES = RES = ../res/libresolv.a RES = -lresolv COMPLIB = ../compat/lib/lib44bsd.a COMPLIB = -lnet COMPLIB = LIBS = -lsocket -lnsl #if defined(solaris) && not BSD LIBS = LIBRARIES = $(RES) $(COMPLIB) $(LIBS) LDFLAGS = # ---------------------------------------------------------------------- # Miscellaneous definitions. # ---------------------------------------------------------------------- MAKE = make $(MFLAGS) # This assumes the BSD install. INSTALL = install -c # Grrr SHELL = /bin/sh # ---------------------------------------------------------------------- # Files. # ---------------------------------------------------------------------- HDRS = port.h conf.h exit.h defs.h vrfy.h SRCS = main.c pars.c smtp.c conn.c stat.c mxrr.c util.c vers.c OBJS = main.o pars.o smtp.o conn.o stat.o mxrr.o util.o vers.o PROG = vrfy MANS = vrfy.1 DOCS = RELEASE_NOTES FILES = Makefile $(DOCS) $(HDRS) $(SRCS) $(MANS) PACKAGE = vrfy TARFILE = $(PACKAGE).tar CLEANUP = $(PROG) $(OBJS) $(TARFILE) $(TARFILE).Z # ---------------------------------------------------------------------- # Rules for installation. # ---------------------------------------------------------------------- OWNER = root GROUP = staff MODE = 755 STRIP = -s all: $(PROG) $(PROG): $(OBJS) $(CC) $(LDFLAGS) -o $(PROG) $(OBJS) $(LIBRARIES) install: $(PROG) $(INSTALL) -m $(MODE) -o $(OWNER) -g $(GROUP) $(STRIP) $(PROG) $(BINDIR) man: $(MANS) $(INSTALL) -m 444 vrfy.1 $(MANDIR) clean: rm -f $(CLEANUP) *.o a.out core # ---------------------------------------------------------------------- # Rules for maintenance. # ---------------------------------------------------------------------- lint: lint $(DEFS) $(SRCS) alint: alint $(DEFS) $(SRCS) llint: lint $(DEFS) $(SRCS) -lresolv print: lpr -J $(PACKAGE) -p Makefile $(DOCS) $(HDRS) $(SRCS) dist: tar cf $(TARFILE) $(FILES) compress $(TARFILE) depend: mkdep $(DEFS) $(SRCS) # DO NOT DELETE THIS LINE -- mkdep uses it. # DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. vrfy-990522.orig/RELEASE_NOTES0100600000175000017500000002355106721367333015545 0ustar herbertgreathan @(#)RELEASE_NOTES e07@nikhef.nl (Eric Wassenaar) 990522 URL ftp://ftp.nikhef.nl/pub/network/vrfy.tar.Z URL http://www.nikhef.nl/user/e07 yymmdd Description of changes per release TODO Things that still need to be done WISHLIST Wishes expressed by various people # ---------------------------------------------------------------------- # Description of changes per release # ---------------------------------------------------------------------- 990522 Minor portability changes. The BIND 4.* debugging routine p_rr() has disappeared in the BIND 8.* release, and there is no simple backward compatible equivalent. First reported by David O'Brien Also noted by Dimitrios Stergiou 990511 Minor portability changes. Solaris 2.7 defines `ipaddr_t.' Can you believe it. Fix glitch for bsdi. Noted by Paul Eggert There are no functional changes in this release. 980820 Recognize :include: syntax. Don't attempt to verify :include:s at SMTP servers. They may be encountered when verifying files. Handle them locally. Requires provisions for recursive calls of the file scanning module. Recognize /pathname and "|program" syntax. Don't attempt to verify those at SMTP servers either. 980626 Recognize new SMTP reply codes. Code 521 is defined per RFC 1846 and indicates that an SMTP server does not (never) accept incoming mail. Code 571 is sometimes used to reject mail from banned domains. Strictly speaking the code is not defined and is probably mistaken for the DSN "5.7.1" status. It is intercepted to avoid "Remote protocol" errors. Minor portability changes. Move to a more appropriate place to avoid BSD mode compile errors on the solaris 2.5 platform. Don't declare sys_errlist on some more platforms. 971114 Add possibility to specify envelope sender. A new command line option -S specifies an explicit envelope sender address for use in the SMTP MAIL FROM transaction, to overrule the default empty address <>. The address must conform to elementary syntax rules. The -S option enforces the -n option, which causes the HELO/MAIL/RCPT sequence to be carried out instead of the regular VRFY or EXPN. Useful for testing purposes. Check for arbitrary relay by remote server. The facilities are available to check whether a remote SMTP server is willing to relay mail from an arbitrary sender to an arbitrary recipient. The astute reader may have figured out how to do this. Allow the use of vrfy as a filter. If the -f option is given without an explicit filename, standard input is read to fetch the target addresses. From Hallvard Furuseth Restructure handling of smtp replies. Simplify code with respect to smtp state messages. Simplify implementation on whether to process replies. Distinguish between first reply line and continuations. Take proper care of I/O output errors. Minor portability changes. Identify SVR4 platforms depending on cpp symbols, which are different for various compilers. Minor portability changes. Move to a more appropriate place to avoid BSD mode compile errors on the solaris 2.5 platform. Miscellaneous changes. Various esthetical code changes. 971007 Reconsider heuristic to determine BIND release. Some vendors still ship new BIND 4.9 releases that have the old struct ``state'' instead of ``__res_state''. This is now controlled via Makefile CONFIGDEFS. There are no functional changes in this release. 970930 Minor portability fixes. On some platforms sys_errlist is a (const char *). Use explicit casts in assignments and function returns to avoid compiler warnings. Miscellaneous changes. Minor esthetical code changes. 970922 Portability fix. On some platforms, sys_errlist has already been defined in and must not be declared again in stat.c. Reported for FreeBSD by Bruno Rohee Backward compatibility fix. Some vendors still ship old BIND 4.9 releases that have the old struct ``state'' instead of ``__res_state''. From Jan Hendrik Peters for AIX. Minor portability fixes. Properly identify certain platforms in port.h to avoid compiler warnings. 970828 Linux portability fixes. On some linux platforms h_name is a (const char *). Use explicit casts in assignments and function returns to avoid compiler warnings. Minor portability fixes. Use explicit casts for ntohs()/htons() in a few places to avoid compiler warnings on some platforms. There are no functional changes in this release. 961113 Portability fix. Trying to include was a bit over-ambitious and caused confusion on some platforms. There are no functional changes in this release. 961013 Bug fix. Fix parsespec() handling of source routes and uucp bang paths. Must copy plain address before checking domain. Bug was introduced in version 961006. Minor fixes. Improve interrupt and error handling in I/O routines. Reject MX records of the wrong resource record class. Miscellaneous changes. Attempt to further reduce lint clutter. It is difficult to eliminate this altogether. There are too many subtle differences between various platforms. 961006 Support for ESMTP EHLO command. Optionally use the ESMTP EHLO command instead of HELO. This is forced via the new -H command line option. Support for ESMTP ETRN command. A new -T option enables special ETRN processing at the mx hosts for a given domain. See RFC 1985 for details. Suggested by Robert Harker Configurable default options. Use an environment variable VRFY_DEFAULTS to set default options and parameters. These are interpolated in front of the command line arguments before scanning. Syntax is the same as the command line syntax. More environment variables. Use environment variables to overrule the compiled-in names of the local mail host and several relay hosts. More configurable defaults. The default compiled-in connect and read timeout are now configurable in conf.h. Properly handle tcp packet overflow. If the supplied answer buffer space is insufficient to store the entire answer, res_send() is supposed to return the length of the entire untruncated answer, not the number of bytes that are actually available. Increase MAXPACKET packet buffer size. The traditional value 1024 for the (tcp) packet size is no longer sufficient (see moderators.uu.net MX records). This is now set to the maximum value used internally by the BIND named. The same value is used by dig. Although still static, it should be sufficient. Extra safety checks. After a positive return from an ordinary gethostbyname or gethostbyaddr we cannot be absolutely sure that the size of the canonical h_name is within bounds. Attempt to recognize list (group) syntax. Not many smtp servers are able to handle this properly. Try to extract the pure address list from this syntax. Better diagnose SMTP reply codes. Code 252 was added per RFC 1123 to reject an SMTP VRFY command. This has been implemented by sendmail V8. Consider only 250 and 251 as valid responses. Minor changes. Check some more SMTP reply codes. Guard against potential buffer overflow in some places. Make some extra domain name syntax checks. Miscellaneous cleanup. Update pieces of code and header files to be similar to those used in the host/ping/traceroute utilities. Add various BIND portability fixes. Update manual page. Document the old -h option and the new -H option. Document the new -T option for ETRN processing. Document the new environment variables. 950410 Increase various constants. Change MAXSPEC from 256 to 512. Change MAXMXHOSTS from 10 to 20 conforming to sendmail. Refine test for invalid address. Disallow only unquoted control characters. No embedded newlines without linear white space. Add smtprset(). Issue an SMTP RSET command after MAIL and RCPT during the alternative protocol suite, to reset the session. Miscellaneous glitches. Properly allocate static storage for mx host names. Allow DNS name expansion up to MAXHOST characters. Explicitly call res_init() early in the game. Added this RELEASE_NOTES file. 940929 Various portability changes. Avoid use of sizeof() for all entities that have a fixed field width, and use predefined constants instead. This is necessary for systems without 16 or 32 bit integers. Fix use of ipaddr_t and struct in_addr appropriately. All this makes the utility portable to e.g. Cray. 940525 General portability changes. Adapt for DEC Alpha OSF/1, and BIND 4.9. Add header files port.h conf.h exit.h Miscellaneous additions. Configure relay host for unresolved single hostnames. Handle 8-bit characters and sendmail V8 meta-chars. Some error messages slightly modified. In exit status, temp failures override hard failures. 921021 Miscellaneous declaration changes. Add -e option for expn instead of vrfy. Various sanity checks. Fix bug in recursion: save old host. Add version number to all files. 920229 Support parsing of full addresses. Add -f option to verify address files. Add -l option to handle errors locally. Implement recursive mode. Detect forwarding loops. Add -s option to strip comments. Improve recursive loop strategy. Add undocumented -h -o -m -r options. Add -n option for alternative suite. 911216 Fetch MX records and verify remotely. Catch special pseudo-domains. Add -c option to set connect timeout. Add -p option to ping mx hosts. 910617 Add -t option to set read timeout. Save errno across smtpquit() calls. # ---------------------------------------------------------------------- # TODO # ---------------------------------------------------------------------- # ---------------------------------------------------------------------- # WISHLIST # ---------------------------------------------------------------------- vrfy-990522.orig/port.h0100600000175000017500000000623306715662750014731 0ustar herbertgreathan/* ** Various portability definitions. ** ** @(#)port.h e07@nikhef.nl (Eric Wassenaar) 990511 */ #if defined(__SVR4) || defined(__svr4__) #define SVR4 #endif #if defined(SYSV) || defined(SVR4) #define SYSV_MALLOC #define SYSV_MEMSET #define SYSV_STRCHR #define SYSV_SETVBUF #endif #if defined(__hpux) || defined(hpux) #define SYSV_MALLOC #define SYSV_SETVBUF #endif #if defined(sgi) #define SYSV_MALLOC #endif #if defined(linux) #define SYSV_MALLOC #endif #if defined(bsdi) || defined(__bsdi__) #define SYSV_MALLOC #endif #if defined(NeXT) #define SYSV_MALLOC #endif /* ** Special definitions for certain platforms. */ #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define ERRLIST_DEFINED /* don't declare sys_errlist */ #endif #if defined(linux) || defined(__bsdi__) #define ERRLIST_DEFINED /* don't declare sys_errlist */ #endif /* ** Distinguish between various BIND releases. */ #if defined(RES_PRF_STATS) #define BIND_49 #else #define BIND_48 #endif #if defined(BIND_49) && defined(__BIND) #define BIND_493 #endif /* ** Define constants for fixed sizes. */ #ifndef INT16SZ #define INT16SZ 2 /* for systems without 16-bit ints */ #endif #ifndef INT32SZ #define INT32SZ 4 /* for systems without 32-bit ints */ #endif #ifndef INADDRSZ #define INADDRSZ 4 /* for sizeof(struct inaddr) != 4 */ #endif /* ** The following should depend on existing definitions. */ typedef int bool; /* boolean type */ #define TRUE 1 #define FALSE 0 #if defined(BIND_48) || defined(OLD_RES_STATE) typedef struct state res_state_t; #else typedef struct __res_state res_state_t; #endif #if defined(BIND_493) typedef u_char qbuf_t; #else typedef char qbuf_t; #endif #if defined(BIND_493) typedef char nbuf_t; #else typedef u_char nbuf_t; #endif #ifndef _IPADDR_T #if defined(__alpha) || defined(BIND_49) typedef u_int ipaddr_t; #else typedef u_long ipaddr_t; #endif #endif #if defined(apollo) || defined(_BSD_SIGNALS) typedef int sigtype_t; #else typedef void sigtype_t; #endif #ifdef SYSV_MALLOC typedef void ptr_t; /* generic pointer type */ typedef u_int siz_t; /* general size type */ typedef void free_t; #else typedef char ptr_t; /* generic pointer type */ typedef u_int siz_t; /* general size type */ typedef int free_t; #endif #ifdef SYSV_MEMSET #define bzero(a,n) (void) memset(a,'\0',n) #define bcopy(a,b,n) (void) memcpy(b,a,n) #endif #ifdef SYSV_STRCHR #define index strchr #define rindex strrchr #endif #ifdef SYSV_SETVBUF #define linebufmode(a) (void) setvbuf(a, (char *)NULL, _IOLBF, BUFSIZ); #else #define linebufmode(a) (void) setlinebuf(a); #endif #if defined(sun) && defined(NO_YP_LOOKUP) #define gethostbyname (struct hostent *)res_gethostbyname #define gethostbyaddr (struct hostent *)res_gethostbyaddr #endif #if defined(SVR4) #define jmp_buf sigjmp_buf #define setjmp(e) sigsetjmp(e,1) #define longjmp(e,n) siglongjmp(e,n) #endif /* ** No prototypes yet. */ #define PROTO(TYPES) () #if !defined(__STDC__) || defined(apollo) #define Proto(TYPES) () #else #define Proto(TYPES) TYPES #endif #if !defined(__STDC__) || defined(apollo) #define const #endif #if defined(__STDC__) && defined(BIND_49) #define CONST const #else #define CONST #endif vrfy-990522.orig/conf.h0100600000175000017500000000456606225557560014677 0ustar herbertgreathan/* ** Various configuration definitions. ** ** @(#)conf.h e07@nikhef.nl (Eric Wassenaar) 961006 */ /* * This is your nearest host running the sendmail daemon. * It is contacted in case local addresses without a domain are given. * Also when domain parsing errors were encountered, assuming that * this host can give a more appropriate error message. * This definition is overruled by the VRFY_LOCALHOST environment variable. */ #ifndef LOCALHOST #define LOCALHOST "localhost" /* redefine if not running sendmail */ #endif /* * This host is contacted when a .uucp address is specified. * You probably won't get much useful information. * This definition is overruled by the VRFY_UUCPRELAY environment variable. */ #ifndef UUCPRELAY #define UUCPRELAY LOCALHOST /* where to send pure uucp addresses */ #endif /* * This host is contacted when a .bitnet or .earn address is specified. * You probably won't get much useful information. * This definition is overruled by the VRFY_BITNETRELAY environment variable. */ #ifndef BITNETRELAY #define BITNETRELAY LOCALHOST /* where to send earn/bitnet addresses */ #endif /* * This host is contacted when a single unqualified host name * could not be resolved to a fully qualified MX domain host. * It is assumed that single hosts in your own domain can be * resolved, i.e. they have an MX record. * It depends on your local strategy for unqualified hosts what they * mean: a .uucp host, a .bitnet host, or just a local host without MX. * This definition is overruled by the VRFY_SINGLERELAY environment variable. */ #ifndef SINGLERELAY #define SINGLERELAY LOCALHOST /* where to send single host addresses */ #endif /* * Various constants. */ #define MAXSPEC 512 /* maximum size of single address spec */ #define MAXHOST 256 /* maximum size of an hostname */ #define MAXREPLY 1200 /* maximum number of replies per query */ #define MAXHOP 17 /* default maximum recursion level */ #define MAXLOOP 50 /* maximum useable recursion level */ #define MAXMXHOSTS 20 /* maximum number of mx hosts */ #define MAXADDRS 35 /* max address count from gethostnamadr.c */ /* * Default timeout values. */ #define CONNTIMEOUT 6 /* connect timeout (seconds) */ #define READTIMEOUT 60 /* read timeout (seconds) */ /* * Prefix for messages on stdout in debug mode. */ #if defined(BIND_49) #define DBPREFIX ";; " #else #define DBPREFIX "" #endif vrfy-990522.orig/exit.h0100600000175000017500000000206306227161414014700 0ustar herbertgreathan/* ** Various exit codes. ** ** They come from ** Defined here to avoid including /usr/ucbinclude on solaris 2.x ** ** @(#)exit.h e07@nikhef.nl (Eric Wassenaar) 961010 */ #undef EX_OK /* defined in on SVR4 */ #define EX_SUCCESS 0 /* successful termination */ #define EX_USAGE 64 /* command line usage error */ #define EX_DATAERR 65 /* data format error */ #define EX_NOINPUT 66 /* cannot open input */ #define EX_NOUSER 67 /* addressee unknown */ #define EX_NOHOST 68 /* host name unknown */ #define EX_UNAVAILABLE 69 /* service unavailable */ #define EX_SOFTWARE 70 /* internal software error */ #define EX_OSERR 71 /* system error (e.g., can't fork) */ #define EX_OSFILE 72 /* critical OS file missing */ #define EX_CANTCREAT 73 /* can't create (user) output file */ #define EX_IOERR 74 /* input/output error */ #define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */ #define EX_PROTOCOL 76 /* remote error in protocol */ #define EX_NOPERM 77 /* permission denied */ #define EX_CONFIG 78 /* local configuration error */ vrfy-990522.orig/defs.h0100600000175000017500000000673006433007733014657 0ustar herbertgreathan/* ** Declaration of functions. ** ** @(#)defs.h e07@nikhef.nl (Eric Wassenaar) 971114 */ /* ** Internal declarations of the vrfy utility ** ----------------------------------------- */ /* main.c */ int main PROTO((int, char **)); void set_defaults PROTO((char *, int, char **)); int getval PROTO((char *, char *, int, int)); void fatal PROTO((char *, ...)); void error PROTO((char *, ...)); void usrerr PROTO((char *, ...)); void message PROTO((char *, ...)); void response PROTO((char *)); void show PROTO((int, char *)); void loop PROTO((char *, char *)); void file PROTO((char *)); void list PROTO((char *)); void vrfy PROTO((char *, char *)); void etrn PROTO((char *, char *)); void ping PROTO((char *)); int verify PROTO((char *, char *)); int vrfyhost PROTO((char *, char *)); int expnhost PROTO((char *, char *)); int rcpthost PROTO((char *, char *)); int etrnhost PROTO((char *, char *)); int pinghost PROTO((char *)); int getmxhosts PROTO((char *)); char *setsender PROTO((char *)); /* pars.c */ char *parselist PROTO((char *)); char *parsespec PROTO((char *, char *, char *)); char *parseaddr PROTO((char *)); char *parsehost PROTO((char *)); char *find_delim PROTO((char *, char)); bool invalidaddr PROTO((char *)); bool invalidhost PROTO((char *)); bool invalidloop PROTO((char *)); char *cataddr PROTO((char *, char *, char *)); /* smtp.c */ int smtpinit PROTO((char *)); int smtphelo PROTO((char *, bool)); int smtpehlo PROTO((char *)); int smtponex PROTO((void)); int smtpverb PROTO((char *)); int smtpetrn PROTO((char *)); int smtprset PROTO((void)); int smtpmail PROTO((char *)); int smtprcpt PROTO((char *)); int smtpexpn PROTO((char *)); int smtpvrfy PROTO((char *)); int smtpdata PROTO((void)); int smtpbody PROTO((void)); int smtpquit PROTO((void)); void smtpmessage PROTO((char *, ...)); int smtpreply PROTO((char *, bool)); /* conn.c */ sigtype_t timer PROTO((int)); char *sfgets PROTO((char *, int, FILE *)); int makeconnection PROTO((char *, char **, char **)); void setmyhostname PROTO((void)); int getmyhostname PROTO((char *)); bool internet PROTO((char *)); ipaddr_t numeric_addr PROTO((char *)); /* stat.c */ char *statstring PROTO((int)); char *errstring PROTO((int)); void giveresponse PROTO((int)); /* mxrr.c */ int getmxbyname PROTO((char *)); /* util.c */ void fixcrlf PROTO((char *, bool)); char *maxstr PROTO((char *, int, bool)); char *printable PROTO((char *)); ptr_t *xalloc PROTO((ptr_t *, siz_t)); char *itoa PROTO((int)); /* ** External library functions ** -------------------------- */ /* extern */ #if !defined(NO_INET_H) #include #else ipaddr_t inet_addr PROTO((CONST char *)); char *inet_ntoa PROTO((struct in_addr)); #endif /* avoid */ #if !defined(index) char *index PROTO((const char *, int)); char *rindex PROTO((const char *, int)); #endif /* */ #if !defined(NO_STRING_H) #include #else char *strcpy PROTO((char *, const char *)); char *strcat PROTO((char *, const char *)); char *strncpy PROTO((char *, const char *, siz_t)); #endif /* */ #if defined(__STDC__) && !defined(apollo) #include #else char *getenv PROTO((const char *)); ptr_t *malloc PROTO((siz_t)); ptr_t *realloc PROTO((ptr_t *, siz_t)); free_t free PROTO((ptr_t *)); void exit PROTO((int)); #endif /* */ #if defined(__STDC__) && !defined(apollo) #include #else unsigned int alarm PROTO((unsigned int)); #endif vrfy-990522.orig/vrfy.h0100600000175000017500000000447006721365321014723 0ustar herbertgreathan/* ** Master include file of the vrfy utility. ** ** @(#)vrfy.h e07@nikhef.nl (Eric Wassenaar) 990522 */ #if defined(apollo) && defined(lint) #define __attribute(x) #endif #undef obsolete /* old code left as a reminder */ #undef notyet /* new code for possible future use */ #include #include #include #include #include #include /* not always automatically included */ #include #include #include #include #undef NOERROR /* in on solaris 2.x */ #include #include #include "port.h" /* various portability definitions */ #include "conf.h" /* various configuration definitions */ #include "exit.h" /* exit codes come from */ #define NOT_DOTTED_QUAD ((ipaddr_t)-1) #ifdef lint #define EXTERN #else #define EXTERN extern #endif EXTERN int errno; EXTERN int h_errno; /* defined in the resolver library */ EXTERN res_state_t _res; /* defined in res_init.c */ #include "defs.h" /* declarations of functions */ /* memory allocation */ #define newlist(a,n,t) (t *)xalloc((ptr_t *)a, (siz_t)((n)*sizeof(t))) #define newstruct(t) (t *)xalloc((ptr_t *)NULL, (siz_t)(sizeof(t))) #define newstring(s) (char *)xalloc((ptr_t *)NULL, (siz_t)(strlen(s)+1)) #define newstr(s) strcpy(newstring(s), s) #define xfree(a) (void) free((ptr_t *)a) /* some string functions */ #define sameword(a,b) (strcasecmp(a,b) == 0) #define strlength(a) (int)strlen(a) /* character checking */ #define is_alnum(c) (isascii(c) && isalnum(c)) #define is_digit(c) (isascii(c) && isdigit(c)) #define is_space(c) (isascii(c) && isspace(c)) /* check for linear white space */ #define is_lwsp(c) (((c) == ' ') || ((c) == '\t')) /* sendmail V8 meta-characters */ #define is_meta(c) (((c) & 0340) == 0200) /* special address syntax */ #define file_addr(a) ((a)[0] == '/') #define prog_addr(a) ((a)[0] == '|' || ((a)[0] == '"' && (a)[1] == '|')) #define incl_addr(a) (strncmp(a, ":include:", 9) == 0) #ifdef DEBUG #define assert(condition)\ {\ if (!(condition))\ {\ (void) fprintf(stderr, "assertion botch: ");\ (void) fprintf(stderr, "%s(%d): ", __FILE__, __LINE__);\ (void) fprintf(stderr, "%s\n", "condition");\ exit(EX_SOFTWARE);\ }\ } #else #define assert(condition) #endif vrfy-990522.orig/main.c0100600000175000017500000012033406566752474014672 0ustar herbertgreathan/* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ /* * Since this program contains concepts and possibly pieces of code * from the sendmail sources, the above copyright notice is included. */ /* * Written by Eric Wassenaar, Nikhef-H, * * The officially maintained source of this program is available * via anonymous ftp from machine 'ftp.nikhef.nl' [192.16.199.1] * in the directory '/pub/network' as 'vrfy.tar.Z' * * You are kindly requested to report bugs and make suggestions * for improvements to the author at the given email address, * and to not re-distribute your own modifications to others. */ #ifndef lint static char Version[] = "@(#)vrfy.c e07@nikhef.nl (Eric Wassenaar) 980820"; #endif #include "vrfy.h" /* * Overview * * vrfy is a tool to verify electronic mail addresses. * It recognizes elementary syntax errors, but can do a lot more, * up to complex tasks such as recursively expand mailing lists * and detect mail forwarding loops. * * In its simplest form, vrfy accepts an electronic mail address * "user@domain" for which it will figure out the mx hosts for * "domain", set up an SMTP session with the primary mx host, * and issue the SMTP VRFY command with the given mail address. * The reply from the remote host to the VRFY command is printed. * * If no mx hosts exist, it will try to contact "domain" itself. * In case there is no "domain", the address is supposed to * represent a local recipient which is verified at "localhost". * * By default only the primary mx host is contacted, hoping that * "user" is local to that machine so that some extra information * may be retrieved, such as full name, forwarding, aliasing, or * mailing list expansion. * With an option one may choose to also query the other mx hosts. * * For pseudo domains like "uucp" or "bitnet" one can compile in * explicit servers to be contacted. They default to "localhost". * Not many servers will tell what they are actually going to do * with such addresses. * * Instead of an electronic mail address one can specify the name * of a file containing address lists, e.g. mailing list recipient * files or .forward files. Verification of all recipients in the * file is then attempted. * * If an explicit additional host name is specified on the command * line, verification is carried out at that host, and the input * addresses are passed to the host without further parsing. * * Various levels of verbose output can be selected. Very verbose * mode prints all SMTP transactions with the remote host in detail. * When even more verbose, an additional SMTP VERB command will be * issued, resulting in the display of all actions taken by the * remote host when verifying the address. This can be fun. * * In the special ping mode, the mail exchangers for the specified * electronic mail domain will be contacted to check whether they * do respond to SMTP requests. No address verification is done. * * vrfy has built in the basic address parsing rules of sendmail, * so it can determine the "domain" part in complicated addresses * "comment \"comment\" comment" * * Elementary syntax errors are caught locally. If the domain part * could not be parsed beyond doubt, the address is passed on to * "localhost" hoping to get detailed 'official' error messages. * My sendmail.cf has an enormous amount of syntax checking rules. * With an option one can reject invalid addresses internally. * * Another option lets you recursively verify the received replies * to the original verified address. This is handy for mailing list * expansions, and also to detect possible mail forwarding loops. * This works only by the grace of sendmail and other MTAs sending * formal address specifications in the VRFY replies. * * Recursion stops automatically if a local recipient address is * received, or if a mail loop is detected. If the received reply * is the same as the address that was asked for (modulo comments) * the request is retried at its domain itself, unless this was the * machine we just queried, or it is not an internet domain host. * * The default recursion level is set to the MAXHOP value (17) as * used by sendmail, but this can be overruled (smaller or larger). */ /* * Limitations * * Many non-sendmail SMTP servers have a lousy VRFY handling. * Sometimes this command is not implemented at all. Other servers * are only willing to VRFY local recipient names without a domain * part (PMDF, VM, MVS). * Furthermore, the sendmail V8 server can be configured to refuse * the VRFY or EXPN command for privacy reasons. * * For those hosts there is an option to use the RCPT instead of * the VRFY command. This does not give the same information, but * it is better than nothing. Usually the HELO and MAIL commands * are required as well. Recursive mode is not possible. * * Some hosts refuse to VRFY the bracketed > * but accept the same address without the outermost brackets. * * Usually hosts return addresses with an abundant amount of nested * brackets if you present a bracketed address with comments. * Note that sendmail returns "q_paddr" within a new set of brackets. * It would be more illustrative if "q_user" were returned instead. * * An option will strip all comments from addresses to be verified, * to avoid accumulating brackets during recursive verification. * This is now the default when using the default recursion mode. * * Some hosts return an error message, but with the 250 status code. * As long as there is no '@' in the message, it can do no harm. * * Some mailing lists have CNAME addresses, but we can now handle * these, and do not get into infinite recursion. * * Some hosts return an unqualified address for local recipients. * This is acceptable if it consists only of the pure "local part" * but sometimes it is of the form which is difficult * to trace further. * * MX records may direct mail to a central mail host. Tracing down * may yield an address at a host which is not reachable from the * outside world. */ /* * Compilation options * * This program usually compiles without special compilation options, * but for some platforms you may have to define special settings. * See the Makefile and the header file port.h for details. */ /* * Usage: vrfy [options] address [host] * * This section is still to be supplied. * For now, refer to the manual page. */ static char Usage[] = "\ Usage: vrfy [options] [-v] address [host]\n\ File: vrfy [options] [-v] -f [file] [host]\n\ Ping: vrfy [options] [-v] -p domain\n\ Etrn: vrfy [options] [-v] -T domain [name]\n\ Options: [-a] [-d] [-l] [-s] [-c secs] [-t secs]\n\ Special: [-L level] [-R] [-S sender] [-n] [-e] [-h] [-H]\ "; char **optargv = NULL; /* argument list including default options */ int optargc = 0; /* number of arguments in new argument list */ char *FromAddr = NULL; /* -S explicit envelope sender address */ char *HostSpec = NULL; /* explicit host to be queried */ char *AddrSpec = NULL; /* address being processed */ char *FileName = NULL; /* name of file being processed */ int LineNumber = 0; /* line number into file */ int ExitStat = EX_SUCCESS; /* overall result status */ bool SuprErrs = FALSE; /* suppress parsing errors, if set */ int debug = 0; /* -d debugging level */ int verbose = 0; /* -v verbosity level */ int recursive = 0; /* -L recursive mode maximum level */ bool stripit = FALSE; /* -s strip comments, if set */ bool vrfyall = FALSE; /* -a query all mx hosts found, if set */ bool localerr = FALSE; /* -l handle errors locally, if set */ bool etrnmode = FALSE; /* -T etrn mx hosts, if set */ bool pingmode = FALSE; /* -p ping mx hosts, if set */ bool filemode = FALSE; /* -f verify file or stdin, if set */ bool helomode = FALSE; /* -h issue HELO command, if set */ bool ehlomode = FALSE; /* -H issue EHLO/HELO command, if set */ bool onexmode = FALSE; /* -o issue ONEX command, if set */ bool expnmode = FALSE; /* -e use EXPN instead of VRFY, if set */ bool rcptmode = FALSE; /* -r use RCPT instead of VRFY, if set */ bool datamode = FALSE; /* -M add DATA after MAIL/RCPT, if set */ char *ReplyList[MAXREPLY]; /* saved address expansions */ int ReplyCount = 0; /* number of valid replies */ char *AddrChain[MAXLOOP]; /* address chain in recursive mode */ char *localhost = LOCALHOST; /* nearest sendmail daemon */ char *uucprelay = UUCPRELAY; /* uucp relay host */ char *bitnetrelay = BITNETRELAY; /* bitnet relay host */ char *singlerelay = SINGLERELAY; /* unqualified host relay */ extern char *MxHosts[MAXMXHOSTS]; /* names of mx hosts found */ extern int ConnTimeout; /* -c timeout in secs for connect() */ extern int ReadTimeout; /* -t timeout in secs for sfgets() */ extern char *MyHostName; /* my own fully qualified host name */ extern char *version; /* program version number */ /* ** MAIN -- Start of program vrfy ** ----------------------------- ** ** Exits: ** Various possibilities from sysexits.h */ int main(argc, argv) int argc; char *argv[]; { register char *option; assert(sizeof(u_int) >= 4); /* probably paranoid */ #ifdef obsolete assert(sizeof(u_short) == 2); /* perhaps less paranoid */ assert(sizeof(ipaddr_t) == 4); /* but this is critical */ #endif /* * Synchronize stdout and stderr in case output is redirected. */ linebufmode(stdout); /* * Initialize resolver. Set new defaults. * The old defaults are (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH) */ (void) res_init(); _res.options |= RES_DEFNAMES; /* qualify single names */ _res.options &= ~RES_DNSRCH; /* dotted names are qualified */ /* * Overrule compiled-in defaults. */ option = getenv("VRFY_LOCALHOST"); if (option != NULL) localhost = maxstr(newstr(option), MAXHOST, FALSE); option = getenv("VRFY_UUCPRELAY"); if (option != NULL) uucprelay = maxstr(newstr(option), MAXHOST, FALSE); option = getenv("VRFY_BITNETRELAY"); if (option != NULL) bitnetrelay = maxstr(newstr(option), MAXHOST, FALSE); option = getenv("VRFY_SINGLERELAY"); if (option != NULL) singlerelay = maxstr(newstr(option), MAXHOST, FALSE); /* * Interpolate default options and parameters. */ if (argc < 1 || argv[0] == NULL) fatal(Usage); option = getenv("VRFY_DEFAULTS"); if (option != NULL) { set_defaults(option, argc, argv); argc = optargc; argv = optargv; } /* * Fetch command line options. */ while (argc > 1 && argv[1] != NULL && argv[1][0] == '-') { for (option = &argv[1][1]; *option; option++) { switch (*option) { case 'd': /* increment debugging level */ debug++; break; case 'v': /* increment verbosity level */ verbose++; break; case 'a': /* query all mx hosts */ vrfyall = TRUE; break; case 'l': /* handle errors locally */ localerr = TRUE; break; case 's': /* strip comments from address */ stripit = TRUE; break; case 'H': /* issue EHLO/HELO command */ ehlomode = TRUE; /*FALLTHROUGH*/ case 'h': /* issue HELO command */ helomode = TRUE; break; case 'o': /* issue ONEX command */ onexmode = TRUE; break; case 'e': /* use EXPN instead of VRFY */ expnmode = TRUE; break; case 'S': /* explicit envelope sender address */ if (argv[2] == NULL || argv[2][0] == '-') fatal("Missing sender address"); FromAddr = setsender(argv[2]); if (FromAddr == NULL) fatal("Invalid sender address"); argc--, argv++; /*FALLTHROUGH*/ case 'n': /* use alternative protocol suite */ ehlomode = TRUE; helomode = TRUE; /*FALLTHROUGH*/ case 'r': /* use MAIL/RCPT instead of VRFY */ rcptmode = TRUE; recursive = 0; break; case 'M': /* add DATA after MAIL/RCPT */ datamode = TRUE; break; case 'f': /* verify file */ filemode = TRUE; if (etrnmode) fatal("-f conflicts with -T"); if (pingmode) fatal("-f conflicts with -p"); break; case 'p': /* ping mx hosts */ pingmode = TRUE; if (etrnmode) fatal("-p conflicts with -T"); if (filemode) fatal("-p conflicts with -f"); break; case 'T': /* etrn mx hosts */ etrnmode = TRUE; if (pingmode) fatal("-T conflicts with -p"); if (filemode) fatal("-T conflicts with -f"); break; case 'c': /* set connect timeout */ ConnTimeout = getval(argv[2], "timeout value", 1, 0); --argc; argv++; break; case 't': /* set read timeout */ ReadTimeout = getval(argv[2], "timeout value", 1, 0); --argc; argv++; break; case 'L' : /* set recursion level */ recursive = getval(argv[2], "recursion level", 1, MAXLOOP); rcptmode = FALSE; --argc; argv++; break; case 'R' : /* set default recursion level */ if (recursive == 0) recursive = MAXHOP; stripit = TRUE; rcptmode = FALSE; break; case 'V' : printf("Version %s\n", version); exit(EX_SUCCESS); default: fatal(Usage); } } --argc; argv++; } /* * Scan remaining command line arguments. */ /* must have at least one argument */ if (!filemode && (argc < 2 || argv[1] == NULL)) fatal(Usage); /* set optional explicit host to be queried */ if (argc > 2 && argv[2] != NULL) HostSpec = maxstr(argv[2], MAXHOST, TRUE); /* rest is undefined */ if (argc > 3) fatal(Usage); /* * Miscellaneous initialization. */ /* reset control variables that may have been used */ AddrSpec = NULL; SuprErrs = FALSE; /* get own host name before turning on debugging */ if (helomode || etrnmode || pingmode) setmyhostname(); /* set proper parameter for etrn mode */ if (etrnmode) option = (HostSpec != NULL) ? HostSpec : MyHostName; /* * Set proper resolver options. */ /* set nameserver debugging on, if requested */ if (debug == 2) _res.options |= RES_DEBUG; /* * All set. Execute the required function. */ if (etrnmode) /* etrn the given domain */ etrn(argv[1], option); else if (pingmode) /* ping the given domain */ ping(argv[1]); else if (filemode) /* verify the given file */ file(argv[1]); else /* verify the address list */ list(argv[1]); return(ExitStat); /*NOTREACHED*/ } /* ** SET_DEFAULTS -- Interpolate default options and parameters in argv ** ------------------------------------------------------------------ ** ** The VRFY_DEFAULTS env variable gives customized options. ** ** Returns: ** None. ** ** Outputs: ** Creates ``optargv'' vector with ``optargc'' arguments. */ void set_defaults(option, argc, argv) char *option; /* option string */ int argc; /* original command line arg count */ char *argv[]; /* original command line arguments */ { register char *p, *q; register int i; /* * Allocate new argument vector. */ optargv = newlist(NULL, 2, char *); optargv[0] = argv[0]; optargc = 1; /* * Construct argument list from option string. */ for (q = newstr(option), p = q; *p != '\0'; p = q) { while (is_space(*p)) p++; if (*p == '\0') break; for (q = p; *q != '\0' && !is_space(*q); q++) continue; if (*q != '\0') *q++ = '\0'; optargv = newlist(optargv, optargc+2, char *); optargv[optargc] = p; optargc++; } /* * Append command line arguments. */ for (i = 1; i < argc && argv[i] != NULL; i++) { optargv = newlist(optargv, optargc+2, char *); optargv[optargc] = argv[i]; optargc++; } /* and terminate */ optargv[optargc] = NULL; } /* ** GETVAL -- Decode parameter value and perform range check ** -------------------------------------------------------- ** ** Returns: ** Parameter value if successfully decoded. ** Aborts in case of syntax or range errors. */ int getval(optstring, optname, minvalue, maxvalue) char *optstring; /* parameter from command line */ char *optname; /* descriptive name of option */ int minvalue; /* minimum value for option */ int maxvalue; /* maximum value for option */ { register int optvalue; if (optstring == NULL || optstring[0] == '-') fatal("Missing %s", optname); optvalue = atoi(optstring); if (optvalue == 0 && optstring[0] != '0') fatal("Invalid %s %s", optname, optstring); if (optvalue < minvalue) fatal("Minimum %s %s", optname, itoa(minvalue)); if (maxvalue > 0 && optvalue > maxvalue) fatal("Maximum %s %s", optname, itoa(maxvalue)); return(optvalue); } /* ** FATAL -- Abort program when illegal option encountered ** ------------------------------------------------------ ** ** Returns: ** Aborts after issuing error message. */ void /*VARARGS1*/ fatal(fmt, a, b, c, d) char *fmt; /* format of message */ char *a, *b, *c, *d; /* optional arguments */ { (void) fprintf(stderr, fmt, a, b, c, d); (void) fprintf(stderr, "\n"); exit(EX_USAGE); } /* ** ERROR -- Issue error message to error output ** -------------------------------------------- ** ** Returns: ** None. */ void /*VARARGS1*/ error(fmt, a, b, c, d) char *fmt; /* format of message */ char *a, *b, *c, *d; /* optional arguments */ { (void) fprintf(stderr, fmt, a, b, c, d); (void) fprintf(stderr, "\n"); } /* ** USRERR -- Issue error message during address parsing ** ---------------------------------------------------- ** ** Returns: ** None. ** ** Called from various parsing routines when an error is found. ** Printing of the message is suppressed if necessary. */ void /*VARARGS1*/ usrerr(fmt, a, b, c, d) char *fmt; /* format of message */ char *a, *b, *c, *d; /* optional arguments */ { char msg[BUFSIZ]; /* status message buffer */ /* suppress message if requested */ if (SuprErrs) return; /* issue message with fatal error status */ (void) sprintf(msg, "554 %s", fmt); message(msg, a, b, c, d); } /* ** MESSAGE -- Issue a status message in special format ** --------------------------------------------------- ** ** Returns: ** None. ** ** The status message should begin with 3-digit status code. */ void /*VARARGS1*/ message(msg, a, b, c, d) char *msg; /* status message */ char *a, *b, *c, *d; /* optional arguments */ { char *fmt = &msg[4]; /* format of actual message */ /* do not print informational messages if not verbose */ if ((msg[0] == '0' || msg[0] == '1') && !verbose) return; /* prepend with filename and line number if appropriate */ if (FileName != NULL) printf("%s: line %d: ", FileName, LineNumber); /* print the address being processed */ if (AddrSpec != NULL && *AddrSpec != '\0') printf("%s ... ", printable(AddrSpec)); /* print message itself */ printf(fmt, a, b, c, d); printf("\n"); } /* ** RESPONSE -- Process reply message from smtp vrfy request ** -------------------------------------------------------- ** ** Returns: ** None. ** ** Side effects: ** Valid replies may be saved for later recursion. ** ** Called from smtpreply() for each reply line received in the ** vrfy wait phase. Status code 2xx indicates a message with a ** valid address expansion. More than one such line may arrive. ** ** Strictly speaking only 250 and 251 are valid for VRFY, EXPN, ** and RCPT. Code 252 is added per RFC 1123 to reject VRFY. ** ** Note that we must have an AddrSpec in order to do recursion. */ void response(msg) char *msg; /* status message from reply */ { char *address = &msg[4]; /* address expansion in reply */ /* print the text of the reply */ printf("%s\n", address); /* skip if this is not a standard reply line */ if (!is_digit(msg[0]) || (msg[3] != ' ' && msg[3] != '-')) return; /* skip if this is not a valid address expansion */ if (msg[0] != '2') return; /* only allow 250 and 251 -- explicitly skip 252 */ if (msg[1] != '5' || (msg[2] != '0' && msg[2] != '1')) return; /* save the reply for later recursion processing */ if (recursive && AddrSpec != NULL && ReplyCount < MAXREPLY) { ReplyList[ReplyCount] = newstr(address); ReplyCount++; } } /* ** SHOW -- Print a detailed result status of a transaction ** ------------------------------------------------------- ** ** Returns: ** None. ** ** Side effects: ** Updates the overall result status. ** ** In recursive mode, all received replies are verified in turn. ** Note that we must have an AddrSpec in order to do recursion. */ #define tempfail(x) (x == EX_TEMPFAIL || x == EX_OSERR || x == EX_IOERR) void show(status, host) int status; /* result status of operation */ char *host; /* remote host that was queried */ { /* save result status, keeping previous failures */ if (status != EX_SUCCESS && !tempfail(ExitStat)) ExitStat = status; /* this must be an internal programming error */ if (status == EX_SUCCESS && host == NULL) status = EX_SOFTWARE; /* display the appropriate error message */ if (status != EX_SUCCESS) giveresponse(status); /* special message in etrn mode */ else if (etrnmode) printf("%s responded\n", host); /* special message in ping mode */ else if (pingmode) printf("%s is alive\n", host); /* recursively verify the received replies */ else if (recursive && AddrSpec != NULL && ReplyCount > 0) loop(AddrSpec, host); } /* ** LOOP -- Recursively verify received address expansions ** ------------------------------------------------------ ** ** Returns: ** None. ** ** Called from show() to reprocess the saved valid replies ** after the smtp session is terminated. */ int recursion_level = 0; /* current recursion level */ void loop(address, host) char *address; /* address we have just verified */ char *host; /* remote host that was queried */ { char *replylist[MAXREPLY]; /* local copy of replies */ int replycount = ReplyCount; /* number of replies received */ char *oldhost = host; /* host queried for old address */ char oldaddr[MAXSPEC+1]; /* parsed original address */ char newaddr[MAXSPEC+1]; /* parsed reply to address */ char hostbuf[MAXHOST+1]; /* local copy of domain part */ char *domain; /* domain part of address */ char *SaveFile; int SaveLine; register int i; /* * Save state. */ SaveFile = FileName; SaveLine = LineNumber; /* * Save local copy of replies. */ for (i = 0; i < replycount; i++) replylist[i] = ReplyList[i]; /* * Parse original address and save local copy. Do not report errors. * Loop over the replies if not yet at the maximum recursion level. */ AddrSpec = NULL; SuprErrs = TRUE; domain = parsespec(address, oldaddr, (char *)NULL); if (domain != NULL && recursive && recursion_level < recursive) { /* put the current address on the chain */ AddrChain[recursion_level] = oldaddr; FileName = oldaddr; LineNumber = 0; for (i = 0; i < replycount; i++) { LineNumber++; address = replylist[i]; /* always process address parsing errors */ AddrSpec = address; SuprErrs = FALSE; /* make sure it is a single address */ address = parselist(address); if (address == NULL) continue; /* skip if address cannot be parsed */ domain = parsespec(address, newaddr, hostbuf); if (domain == NULL) continue; /* skip if this is a local recipient address */ if (sameword(domain, "localhost")) continue; /* retry if this is not the same address */ if (!sameword(newaddr, oldaddr)) host = NULL; /* skip if this host was just queried */ else if (sameword(domain, oldhost)) continue; /* skip if this is not an internet host */ else if (!internet(domain)) continue; /* give explicit host a try */ else host = hostbuf; /* skip if forwarding loop is detected */ if (host == NULL && invalidloop(newaddr)) continue; /* reset for message */ AddrSpec = NULL; /* display address */ message("250 %s", address); /* recursively verify the reply */ recursion_level++; vrfy(address, host); recursion_level--; } } /* * Replies were allocated dynamically. */ for (i = 0; i < replycount; i++) xfree(replylist[i]); /* * Restore state. */ FileName = SaveFile; LineNumber = SaveLine; } /* ** FILE -- Process a file containing address lists ** ----------------------------------------------- ** ** Returns: ** None. ** ** Side effects: ** Shows the status for each transaction done. */ void file(filename) char *filename; /* name of file to be verified */ { char *addrlist; /* address list to be verified */ int status; /* result status */ FILE *fp; char buf[BUFSIZ]; char *SaveFile; int SaveLine; register char *p; /* * Save state across recursive calls. */ SaveFile = FileName; SaveLine = LineNumber; /* * Allow the use of vrfy -f as a filter. */ if (filename == NULL || *filename == '\0') filename = "stdin"; if (sameword(filename, "stdin")) fp = stdin; else fp = fopen(filename, "r"); /* * Terminate if the file could not be opened. */ if (fp == NULL) { perror(filename); status = EX_NOINPUT; show(status, (char *)NULL); return; } /* truncate to basename to shorten message */ p = rindex(filename, '/'); if (p++ != NULL) filename = p; /* * Process each line in the file, skipping comment lines. */ FileName = filename; LineNumber = 0; while (fgets(buf, sizeof(buf), fp) != NULL) { LineNumber++; addrlist = buf; /* skip line terminator */ p = index(addrlist, '\n'); if (p != NULL) *p = '\0'; /* skip leading whitespace */ while (is_space(*addrlist)) addrlist++; /* skip comment lines */ if (*addrlist == '\0' || *addrlist == '#') continue; /* reset for message */ AddrSpec = NULL; /* display address list */ if (recursive) message("250 %s", addrlist); else message("050 %s", addrlist); /* verify list of addresses */ list(addrlist); } if (fp != stdin) (void) fclose(fp); /* * Restore state. */ FileName = SaveFile; LineNumber = SaveLine; } /* ** LIST -- Verify an address list at appropriate smtp hosts ** -------------------------------------------------------- ** ** Returns: ** None. ** ** Side effects: ** Shows the status for each transaction done. ** ** If an explicit HostSpec was given, we don't set AddrSpec since ** it may be an entire list. This effectively disables recursion. */ void list(addrlist) char *addrlist; /* address list to be verified */ { char *address; /* single address to be verified */ char *host; /* remote host to be queried */ int status; /* result status */ extern char *DelimAddr; /* position of next address in list */ /* * Query explicit host if specified. * No parsing of the address list will be done in this case. * This requires some extra sanity checks to be done in verify. */ if (HostSpec != NULL) { host = HostSpec; status = verify(addrlist, host); show(status, host); return; } /* * Extract individual addresses from address list. */ while (addrlist != NULL) { /* always process list parsing errors */ AddrSpec = addrlist; SuprErrs = FALSE; /* scan next address; abort on syntax error */ address = parselist(addrlist); if (address == NULL) { status = EX_UNAVAILABLE; show(status, (char *)NULL); break; } /* move to the following address */ addrlist = DelimAddr; /* verify single address */ vrfy(address, (char *)NULL); } } /* ** VRFY -- Verify a single address at appropriate smtp hosts ** --------------------------------------------------------- ** ** Returns: ** None. ** ** Side effects: ** Shows the status for each transaction done. ** ** This routine is not used if an explicit HostSpec was given. */ void vrfy(address, host) char *address; /* address to be verified */ char *host; /* remote host to be queried */ { char addrbuf[MAXSPEC+1]; /* plain address without comment */ char hostbuf[MAXHOST+1]; /* local copy of domain part */ char *domain; /* domain part of address */ char *mxhosts[MAXMXHOSTS]; /* local copy of mx hosts */ int nmx; /* number of mx hosts found */ int status; /* result status */ register int n; /* * Reset count of valid replies received. */ ReplyCount = 0; /* * Check for invalid control characters in address. * Perform also sanity length check to skip nonsense addresses. * Always handle errors locally, to avoid fooling sendmail. */ AddrSpec = address; SuprErrs = FALSE; if (invalidaddr(address)) { status = EX_UNAVAILABLE; show(status, (char *)NULL); return; } /* * Query local host if the domain could not be parsed properly, * but only if parsing errors are not handled locally (default). */ AddrSpec = address; SuprErrs = !localerr; domain = parsespec(address, addrbuf, hostbuf); if (domain == NULL) { if (!SuprErrs) { status = EX_UNAVAILABLE; show(status, (char *)NULL); return; } host = localhost; status = verify(address, host); show(status, host); return; } /* * Verify plain address without comment if requested. */ if (stripit) address = addrbuf; /* * Query explicit host if requested. */ if (host != NULL) { status = verify(address, host); show(status, host); return; } /* * Query local host for local addresses without domain part. * Handle special /filename, "|program", and :include: syntax locally. */ if (sameword(domain, "localhost")) { if (file_addr(addrbuf) || prog_addr(addrbuf)) { printf("%s\n", addrbuf); return; } if (incl_addr(addrbuf)) { file(&addrbuf[9]); return; } host = localhost; status = verify(address, host); show(status, host); return; } /* * Query address host itself if no mx hosts found. */ nmx = getmxhosts(domain); if (nmx < 1) { host = hostbuf; status = verify(address, host); show(status, host); return; } /* * Query all mx hosts found. Use local copy of mx host names. * Query primary mx host only, if not doing all. */ for (n = 0; n < nmx; n++) mxhosts[n] = newstr(MxHosts[n]); for (n = 0; n < nmx; n++) { host = mxhosts[n]; status = verify(address, host); show(status, host); } for (n = 0; n < nmx; n++) xfree(mxhosts[n]); } /* ** ETRN -- Etrn the mx hosts for a given domain ** -------------------------------------------- ** ** Returns: ** None. ** ** Side effects: ** Shows the status for each transaction done. */ void etrn(domain, name) char *domain; /* remote domain to be etrned */ char *name; /* domain name for the ETRN command */ { char *host; /* remote host to be queried */ int nmx; /* number of mx hosts found */ int status; /* result status */ register int n; /* * Validate domain syntax. */ AddrSpec = domain; SuprErrs = FALSE; if (invalidhost(domain)) { status = EX_UNAVAILABLE; show(status, (char *)NULL); return; } /* * Etrn address host itself if no mx hosts found. */ nmx = getmxhosts(domain); if (nmx < 1) { host = domain; status = etrnhost(name, host); show(status, host); return; } /* * Etrn all mx hosts found. No need for local copy. * Etrn primary mx host only, if not doing all. */ for (n = 0; n < nmx; n++) { host = MxHosts[n]; status = etrnhost(name, host); show(status, host); } } /* ** PING -- Ping the mx hosts for a given domain ** -------------------------------------------- ** ** Returns: ** None. ** ** Side effects: ** Shows the status for each transaction done. */ void ping(domain) char *domain; /* remote domain to be pinged */ { char *host; /* remote host to be queried */ int nmx; /* number of mx hosts found */ int status; /* result status */ register int n; /* * Validate domain syntax. */ AddrSpec = domain; SuprErrs = FALSE; if (invalidhost(domain)) { status = EX_UNAVAILABLE; show(status, (char *)NULL); return; } /* * Ping address host itself if no mx hosts found. */ nmx = getmxhosts(domain); if (nmx < 1) { host = domain; status = pinghost(host); show(status, host); return; } /* * Ping all mx hosts found. No need for local copy. * Ping primary mx host only, if not doing all. */ for (n = 0; n < nmx; n++) { host = MxHosts[n]; status = pinghost(host); show(status, host); } } /* ** VERIFY -- Verify an address at a given remote smtp host ** ------------------------------------------------------- ** ** Returns: ** Status code of smtp transaction. ** ** The address may be an entire address list in case an explicit ** HostSpec was given. Parsing of the list is skipped in that case. */ int verify(address, host) char *address; /* address to be verified */ char *host; /* remote host to be queried */ { int status; /* result status */ /* * Kludge, most hosts cannot handle empty address lists. However, * if we pass the full address specification, we cannot detect an * address list consisting only of comments such as '(comment)'. */ while (is_space(*address) || *address == ',') address++; if (*address == '\0') return(EX_SUCCESS); /* * Perform extra sanity check to skip nonsense addresses. */ if (strlength(address) > MAXSPEC) return(EX_USAGE); /* * Carry out the RCPT or EXPN or VRFY protocol as requested. */ if (rcptmode) status = rcpthost(address, host); else if (expnmode) status = expnhost(address, host); else status = vrfyhost(address, host); return(status); } /* ** VRFYHOST -- Verify an address at a given remote smtp host ** --------------------------------------------------------- ** ** Returns: ** Status code of smtp transaction. */ int vrfyhost(address, host) char *address; /* address to be verified */ char *host; /* remote host to be queried */ { register int reply; /* * Show which address we are going to verify at which host. */ if (verbose || debug) printf("vrfy '%s' at '%s'\n", address, host); if (debug >= 3) return(EX_SUCCESS); /* * Carry out the smtp protocol suite using VRFY. * Note that smtponex returns ok if ONEX is not supported remotely. * Note that smtpverb returns ok if VERB is not supported remotely. * Some hosts require a parameter for the VERB command. */ reply = smtpinit(host); if (reply == EX_SUCCESS && helomode) reply = smtphelo(MyHostName, ehlomode); if (reply == EX_SUCCESS && onexmode) reply = smtponex(); if (reply == EX_SUCCESS && verbose >= 3) reply = smtpverb("on"); if (reply == EX_SUCCESS) reply = smtpvrfy(address); (void) smtpquit(); return(reply); } /* ** EXPNHOST -- Verify an address at a given remote smtp host ** --------------------------------------------------------- ** ** Returns: ** Status code of smtp transaction. */ int expnhost(address, host) char *address; /* address to be verified */ char *host; /* remote host to be queried */ { register int reply; /* * Show which address we are going to verify at which host. */ if (verbose || debug) printf("expn '%s' at '%s'\n", address, host); if (debug >= 3) return(EX_SUCCESS); /* * Carry out the smtp protocol suite using EXPN. * Note that smtponex returns ok if ONEX is not supported remotely. * Note that smtpverb returns ok if VERB is not supported remotely. * Some hosts require a parameter for the VERB command. */ reply = smtpinit(host); if (reply == EX_SUCCESS && helomode) reply = smtphelo(MyHostName, ehlomode); if (reply == EX_SUCCESS && onexmode) reply = smtponex(); if (reply == EX_SUCCESS && verbose >= 3) reply = smtpverb("on"); if (reply == EX_SUCCESS) reply = smtpexpn(address); (void) smtpquit(); return(reply); } /* ** RCPTHOST -- Verify an address at a given remote smtp host ** --------------------------------------------------------- ** ** Returns: ** Status code of smtp transaction. */ int rcpthost(address, host) char *address; /* address to be verified */ char *host; /* remote host to be queried */ { register int reply; /* * Show which address we are going to verify at which host. */ if (verbose || debug) printf("rcpt '%s' at '%s'\n", address, host); if (debug >= 3) return(EX_SUCCESS); /* * Carry out the smtp protocol suite using MAIL/RCPT. * Only malconfigured hosts do not accept an empty sender address. */ reply = smtpinit(host); if (reply == EX_SUCCESS && helomode) reply = smtphelo(MyHostName, ehlomode); if (reply == EX_SUCCESS && onexmode) reply = smtponex(); if (reply == EX_SUCCESS && verbose >= 3) reply = smtpverb("on"); if (reply == EX_SUCCESS) reply = smtpmail((FromAddr == NULL) ? "" : FromAddr); if (reply == EX_SUCCESS) reply = smtprcpt(address); if (reply == EX_SUCCESS && datamode) (void) smtpdata(); else (void) smtprset(); (void) smtpquit(); return(reply); } /* ** ETRNHOST -- Issue an ETRN command at a given remote smtp host ** ------------------------------------------------------------- ** ** Returns: ** Status code of smtp transaction. */ int etrnhost(name, host) char *name; /* domain name for the ETRN command */ char *host; /* remote host to be queried */ { register int reply; /* * Show which name we are going to etrn at which host. */ if (verbose || debug) printf("etrn '%s' at '%s'\n", name, host); if (debug >= 3) return(EX_SUCCESS); /* * Carry out the smtp protocol suite. */ reply = smtpinit(host); if (reply == EX_SUCCESS && helomode) reply = smtphelo(MyHostName, ehlomode); if (reply == EX_SUCCESS && verbose >= 3) reply = smtpverb("on"); if (reply == EX_SUCCESS) reply = smtpetrn(name); (void) smtpquit(); return(reply); } /* ** PINGHOST -- Ping a given remote host to check smtp connectivity ** --------------------------------------------------------------- ** ** Returns: ** Status code of smtp transaction. */ int pinghost(host) char *host; /* remote host to be queried */ { register int reply; /* * Show which host we are going to ping. */ if (verbose || debug) printf("ping '%s'\n", host); if (debug >= 3) return(EX_SUCCESS); /* * Carry out the smtp protocol suite. */ reply = smtpinit(host); if (reply == EX_SUCCESS && (helomode || verbose >= 3)) reply = smtphelo(MyHostName, ehlomode); (void) smtpquit(); return(reply); } /* ** GETMXHOSTS -- Fetch mx hosts for a given domain ** ----------------------------------------------- ** ** Returns: ** Number of mx hosts successfully found. ** ** Outputs: ** Global table MxHosts[] contains list of host names. */ int getmxhosts(domain) char *domain; /* domain to get mx hosts for */ { int nmx; /* number of mx hosts found */ register char *dot; /* * Catch special pseudo-domains that cannot be mapped. */ dot = rindex(domain, '.'); if (dot != NULL) { if (sameword(dot, ".uucp")) { MxHosts[0] = uucprelay; nmx = 1; return(nmx); } if (sameword(dot, ".bitnet") || sameword(dot, ".earn")) { MxHosts[0] = bitnetrelay; nmx = 1; return(nmx); } } /* * Try to find any mx hosts. * This is supposed to catch undomained local hosts. * Return only primary mx host if not verifying all. */ nmx = getmxbyname(domain); if (nmx > 0) return(vrfyall ? nmx : 1); /* * Unresolved single undomained hosts go to the single relay. * How these should be interpreted depends on your local strategy: * they could default to uucp addresses, bitnet addresses, or just * local hosts without an mx record. */ dot = rindex(domain, '.'); if (dot == NULL) { MxHosts[0] = singlerelay; nmx = 1; return(nmx); } /* * No mx hosts found, and not a special case. */ return(nmx); } /* ** SETSENDER -- Define an explicit envelope sender address ** ------------------------------------------------------- ** ** Returns: ** The parsed plain address. ** NULL in case of elementary syntax errors. */ char * setsender(address) char *address; /* potential envelope sender */ { static char addrbuf[MAXSPEC+1]; /* parsed plain address */ char *domain; /* domain part of address */ /* always process address parsing errors */ AddrSpec = address; SuprErrs = FALSE; /* make sure it is a single address */ address = parselist(address); if (address == NULL) return(NULL); /* skip if address cannot be parsed */ domain = parsespec(address, addrbuf, (char *)NULL); if (domain == NULL) return(NULL); return(addrbuf); } vrfy-990522.orig/pars.c0100600000175000017500000003462006230253022014662 0ustar herbertgreathan/* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint static char Version[] = "@(#)pars.c e07@nikhef.nl (Eric Wassenaar) 961013"; #endif #include "vrfy.h" /* ** PARSELIST -- Isolate a single address in an address list ** -------------------------------------------------------- ** ** Returns: ** Beginning of first address in the list. ** NULL in case of elementary syntax errors. ** ** Side effects: ** The address is terminated with a null byte, ** clobbering the original address list. ** An error message is generated if invalid. ** ** DelimAddr will point to the beginning of the next address ** in the list, or is set to NULL if this was the last address. */ char *DelimAddr = NULL; /* position of next address in list */ char * parselist(addrspec) char *addrspec; { char delimiter; /* delimiter between addresses */ register char *p, *q; /* * Do a rudimentary attempt to recognize list (group) syntax. * Not many SMTP servers are capable to handle this properly. * The general syntax is "comment: address1, address2;" * An empty address list may be specified as "comment:;" */ p = rindex(addrspec, ';'); if (p != NULL && p[1] == '\0') { q = find_delim(addrspec, ':'); if (q != NULL && *q == ':') { *p = '\0'; addrspec = q+1; } } /* * Always search for comma as address delimiter. * Old style address lists with space delimiters are not applicable here. */ #ifdef notdef delimiter = ' '; if (index(addrspec, ',') != NULL || index(addrspec, '<') != NULL || index(addrspec, '(') != NULL || index(addrspec, ';') != NULL) #endif delimiter = ','; /* * Move to the beginning of the first address in the list. */ while (is_space(*addrspec) || *addrspec == ',') addrspec++; /* * Scan address list for delimiter. Abort on syntax errors. */ DelimAddr = find_delim(addrspec, delimiter); if (DelimAddr == NULL) return(NULL); /* * Move to the beginning of the next following address. */ while (is_space(*DelimAddr) || *DelimAddr == ',') *DelimAddr++ = '\0'; /* * Return the first address and the position of the next address. */ if (*DelimAddr == '\0') DelimAddr = NULL; return(addrspec); } /* ** PARSESPEC -- Extract and validate plain address and domain part ** ---------------------------------------------------------------- ** ** Returns: ** Pointer to the domain part, if present, or ** "localhost" in case there is no domain part. ** NULL in case of elementary syntax errors. ** NULL in case of probably invalid domain. ** ** Side effects: ** An error message is generated if invalid. ** ** Outputs: ** A copy of the parsed components is saved, if requested. ** The parsed components must fit into the provided storage. */ char * parsespec(addrspec, copya, copyd) char *addrspec; /* full address specification */ char *copya; /* buffer to store plain address */ char *copyd; /* buffer to store domain part */ { char *address; /* plain address without comment */ char *domain; /* domain part of address */ /* * Make some elementary syntax checks. * Abort if this is an invalid address. */ if (invalidaddr(addrspec)) return(NULL); /* * Extract plain address from full address specification. * Abort if address could not be parsed properly. */ address = parseaddr(addrspec); if (address == NULL) return(NULL); /* save plain address if requested */ if (copya != NULL) (void) strcpy(copya, address); /* * Extract the domain part from the plain address. * Abort if domain could not be parsed properly. */ domain = parsehost(address); if (domain == NULL) return(NULL); /* * Validate the domain part. Make some basic checks. * Abort if this is an invalid domain name. */ if (invalidhost(domain)) return(NULL); /* save domain part if requested */ if (copyd != NULL) (void) strcpy(copyd, domain); /* * Looks like a valid address specification. */ return(domain); } /* ** PARSEADDR -- Construct a plain address without comments ** ------------------------------------------------------- ** ** Returns: ** The plain address as saved in static storage. ** NULL in case of elementary syntax errors. ** ** Side Effects: ** An error message is generated if invalid. ** ** We assume the parsed address will fit into the local storage. ** ** Comments outside brackets, and the brackets, are eliminated. ** Comments within parentheses, and the parens, are eliminated. ** The remaining address parts are concatenated without blanks. ** ** Note for the insiders: this is not 100% conforming to sendmail ** since we disregard the type of individual tokens, and do not ** insert SpaceSub characters between atoms. We don't care here. */ char * parseaddr(addrspec) char *addrspec; /* full address specification */ { register char *address; /* plain address without comment */ static char buf[MAXSPEC+1]; /* saved plain address */ register char *p, *q; /* * Check if we have anything in angle brackets. If so, reprocess * the part between the brackets. Abort in case of syntax errors. */ p = find_delim(addrspec, '<'); if (p != NULL && *p == '<') { q = find_delim(p+1, '>'); if (q != NULL && *q == '>') { *q = '\0'; address = parseaddr(p+1); *q = '>'; return(address); } } if (p == NULL || *p != '\0') return(NULL); /* * Strip out comments between parentheses. * Concatenate the real address parts. Abort on syntax errors. */ address = buf; rescan: p = find_delim(addrspec, '('); if (p != NULL && *p == '(') { q = find_delim(p+1, ')'); if (q != NULL && *q == ')') { *p = '\0'; address = cataddr(buf, address, addrspec); *p = '('; addrspec = q+1; goto rescan; } } if (p == NULL || *p != '\0') return(NULL); address = cataddr(buf, address, addrspec); *address = '\0'; return(buf); } /* ** PARSEHOST -- Extract the domain part from a plain address ** ---------------------------------------------------------- ** ** Returns: ** Pointer to the domain part, if present, or ** "localhost" in case there is no domain part. ** NULL in case of elementary syntax errors. ** ** Side effects: ** Domain part may be terminated with a null ** byte, thereby clobbering the address. ** An error message is generated if invalid. ** ** In addresses with both '!' and '@' precedence is ** given to the '@' part: 'foo!user@bar' goes to bar. ** Source routes have the highest priority. */ char * parsehost(address) char *address; /* plain address without comment */ { register char *delim; /* * RFC822 source route. * Note that it should have been specified between brackets. */ if (*address == '@') { delim = find_delim(address, ','); if (delim == NULL || *delim == '\0') delim = find_delim(address, ':'); if (delim == NULL || *delim == '\0') { usrerr("Invalid source route"); return(NULL); } *delim = '\0'; return(address+1); } /* * Ordinary RFC822 internet address. * Note that we scan for the first '@' and not for the last. */ delim = find_delim(address, '@'); if (delim != NULL && *delim != '\0') return(delim+1); /* * Old fashioned uucp path. */ delim = find_delim(address, '!'); if (delim != NULL && *delim != '\0') { *delim = '\0'; return(address); } /* * Everything else is local. */ return("localhost"); } /* ** FIND_DELIM -- Find the position of a delimiter in an address ** ------------------------------------------------------------ ** ** Returns: ** Position of delimiter in address if found. ** Position of null byte after address if not found. ** NULL in case of elementary syntax errors. ** ** Side Effects: ** An error message is generated if invalid. ** ** Used by parselist() to locate address delimiters in address lists. ** Used by parseaddr() to locate brackets and parens in addresses. ** Used by parsehost() to locate domain separators in domain names. ** ** It will search for an unquoted delimiter outside comments ** in complicated full address specifications like ** "comment \"comment\" comment"
*/ char * find_delim(addrspec, delimiter) char *addrspec; /* full address specification */ char delimiter; /* delimiter char to search for */ { bool backslash = FALSE; /* set if need to escape next char */ bool quoting = FALSE; /* set if within quoted string */ int comment = 0; /* level of parenthesized comments */ int bracket = 0; /* level of bracketed addresses */ register char *p; register char c; /* * Scan address list, and break when delimiter found. */ for (p = addrspec; (c = *p) != '\0'; p++) { if (backslash) backslash = FALSE; else if (c == '\\') backslash = TRUE; else if (c == '"') quoting = !quoting; else if (quoting) continue; else if (c == delimiter && bracket == 0 && comment == 0) break; else if (c == '(') comment++; else if (c == ')') comment--; else if (comment > 0) continue; else if (c == '<') bracket++; else if (c == '>') bracket--; if (bracket < 0 || comment < 0) break; } /* * Check for elementary syntax errors. * If ok, p points to the delimiter, or to a null byte. */ if (quoting) usrerr("Unbalanced '\"'"); else if (comment > 0) usrerr("Unbalanced '('"); else if (comment < 0) usrerr("Unbalanced ')'"); else if (bracket > 0) usrerr("Unbalanced '<'"); else if (bracket < 0) usrerr("Unbalanced '>'"); else return(p); return(NULL); } /* ** INVALIDADDR -- check an address for invalid control characters ** -------------------------------------------------------------- ** ** Returns: ** TRUE if address string could cause problems. ** FALSE otherwise. ** ** Side Effects: ** An error message is generated if invalid. ** ** Called before any parsing is attempted. */ bool invalidaddr(addrspec) char *addrspec; /* address specification */ { bool backslash = FALSE; /* set if need to escape next char */ bool quoting = FALSE; /* set if within quoted string */ register char *p; register char c; for (p = addrspec; (c = *p) != '\0'; p++) { /* always reject special metacharacters */ if (is_meta(c)) break; /* reject embedded newlines without lwsp */ if (c == '\n' && !is_lwsp(p[1])) break; /* check for unquoted control characters */ if (backslash) backslash = FALSE; else if (c == '\\') backslash = TRUE; else if (c == '"') quoting = !quoting; else if (quoting) continue; else { /* non-ascii for ordinary 8-bit characters */ if (!isascii(c)) continue; /* reject non-harmless control characters */ if (iscntrl(c) && !isspace(c)) break; } } /* abort if embedded control character found */ if (*p != '\0') { usrerr("Invalid control character in address"); return(TRUE); } /* avoid possible future buffer overflow */ if (strlength(addrspec) > MAXSPEC) { usrerr("Address too long"); return(TRUE); } /* so far so good */ return(FALSE); } /* ** INVALIDHOST -- check for invalid domain name specification ** ---------------------------------------------------------- ** ** Returns: ** TRUE if the domain name is (probably) invalid. ** FALSE otherwise. ** ** Side Effects: ** An error message is generated if invalid. */ bool invalidhost(domain) char *domain; /* domain name to be checked */ { register char *p; register int n; /* pickup domain name length */ n = strlength(domain); /* must not be of zero length */ if (n < 1) { usrerr("Invalid null domain"); return(TRUE); } /* must not be too long */ if (n > MAXHOST) { usrerr("Domain name too long"); return(TRUE); } /* must not end with a dot */ if (domain[n-1] == '.') { usrerr("Illegal trailing dot"); return(TRUE); } /* must not be a plain dotted quad */ if (inet_addr(domain) != NOT_DOTTED_QUAD) { usrerr("Illegal dotted quad"); return(TRUE); } /* but may be a dotted quad between brackets */ if (numeric_addr(domain) != NOT_DOTTED_QUAD) return(FALSE); /* check for invalid embedded characters */ for (p = domain; *p != '\0'; p++) { /* only alphanumeric plus dot and dash allowed */ if (!is_alnum(*p) && *p != '.' && *p != '-') { usrerr("Invalid domain name"); return(TRUE); } } /* looks like a valid domain name */ return(FALSE); } /* ** INVALIDLOOP -- Check whether an address is present in chain ** ----------------------------------------------------------- ** ** Returns: ** TRUE if address is found in the chain. ** FALSE otherwise. ** ** Side effects: ** An error message is generated if invalid. */ bool invalidloop(address) char *address; { extern char *AddrChain[]; /* addresses in chain */ extern int recursion_level; /* current limit */ register int j; for (j = 0; j < recursion_level; j++) { if (sameword(address, AddrChain[j])) { usrerr("Mail forwarding loop"); return(TRUE); } } return(FALSE); } /* ** CATADDR -- Append an address part to an address buffer ** ------------------------------------------------------ ** ** Returns: ** Next free position in address buffer. ** ** Used by parseaddr() to construct an address without comments. ** Address parts are concatenated without embedded blanks. ** Trailing ';' characters used in group syntax are skipped. */ char * cataddr(buf, address, addrspec) char *buf; /* start of address buffer */ register char *address; /* buf position to append to */ register char *addrspec; /* address spec to fetch from */ { /* skip leading whitespace */ while (is_space(*addrspec)) addrspec++; /* copy address part */ while (*addrspec != '\0') *address++ = *addrspec++; /* remove trailing whitespace and trailing ';' */ while (address > buf && (is_space(address[-1]) || address[-1] == ';')) address--; /* return next free position */ return(address); } vrfy-990522.orig/smtp.c0100600000175000017500000003511106544656615014723 0ustar herbertgreathan/* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint static char Version[] = "@(#)smtp.c e07@nikhef.nl (Eric Wassenaar) 980626"; #endif #include "vrfy.h" extern int verbose; extern int debug; #define REPLYTYPE(r) ((r)/100) /* first digit of smtp reply code */ #define SMTPCLOSING 421 /* "Service Shutting Down" temp failure */ #define SMTP_CLOSED 0 /* connection is closed */ #define SMTP_OPEN 1 /* connection is open for business */ #define SMTP_SSD 2 /* service shutting down */ char SmtpMsgBuffer[BUFSIZ]; /* buffer for outgoing commands */ char SmtpReplyBuffer[BUFSIZ]; /* buffer for incoming replies (first line) */ char SmtpContBuffer[BUFSIZ]; /* buffer for incoming replies (continuation) */ char SmtpErrorBuffer[BUFSIZ]; /* saved temporary failure error messages */ FILE *SmtpOut = NULL; /* smtp output channel */ FILE *SmtpIn = NULL; /* smtp input channel */ char *SmtpPhase = NULL; /* connection state message */ int SmtpState = SMTP_CLOSED; /* current connection state */ int SmtpErrno = 0; /* saved errno from system calls */ char *SmtpCrLf = "\r\n"; /* smtp end-of-line terminator */ /* ** SMTPINIT -- Initiate SMTP connection with remote host ** ----------------------------------------------------- ** ** Returns: ** Status code indicating success or failure. ** ** Outputs: ** Sets SmtpIn and SmtpOut to input and output channel. ** Sets SmtpErrno appropriately. */ int smtpinit(host) char *host; /* remote host to be contacted */ { register int r; SmtpIn = SmtpOut = NULL; SmtpState = SMTP_CLOSED; SmtpErrorBuffer[0] = '\0'; SmtpErrno = 0; SmtpPhase = "connect"; if (debug) printf("smtp phase %s\n", SmtpPhase); r = makeconnection(host, &SmtpOut, &SmtpIn); SmtpErrno = errno; if (r != EX_SUCCESS) return(r); SmtpState = SMTP_OPEN; r = smtpreply("greeting wait", FALSE); if (r < 0 || REPLYTYPE(r) == 4) return(EX_TEMPFAIL); if (REPLYTYPE(r) == 2) return(EX_SUCCESS); if (REPLYTYPE(r) == 5) return(EX_UNAVAILABLE); return(EX_TEMPFAIL); } /* ** SMTPHELO -- Issue the HELO command ** ---------------------------------- ** ** In ESMTP mode, first try the EHLO command. ** ** Returns: ** Status code indicating success or failure. */ int smtphelo(name, esmtp) char *name; /* my own fully qualified hostname */ bool esmtp; /* try EHLO first, if set */ { register int r; if (esmtp) { r = smtpehlo(name); if (r != EX_UNAVAILABLE) return(r); } smtpmessage("HELO %s", name); r = smtpreply("HELO wait", FALSE); if (r < 0 || REPLYTYPE(r) == 4) return(EX_TEMPFAIL); if (REPLYTYPE(r) == 2) return(EX_SUCCESS); if (REPLYTYPE(r) == 5) return(EX_UNAVAILABLE); return(EX_TEMPFAIL); } /* ** SMTPEHLO -- Issue the EHLO command ** ---------------------------------- ** ** Returns: ** Status code indicating success or failure. */ int smtpehlo(name) char *name; /* my own fully qualified hostname */ { register int r; smtpmessage("EHLO %s", name); r = smtpreply("EHLO wait", FALSE); if (r < 0 || REPLYTYPE(r) == 4) return(EX_TEMPFAIL); if (REPLYTYPE(r) == 2) return(EX_SUCCESS); if (REPLYTYPE(r) == 5) return(EX_UNAVAILABLE); return(EX_TEMPFAIL); } /* ** SMTPONEX -- Issue the ONEX command ** ---------------------------------- ** ** Returns: ** Status code indicating success or failure. ** ** It is not an error if the remote host does not support this. */ int smtponex() { register int r; smtpmessage("ONEX"); r = smtpreply("ONEX wait", FALSE); if (r < 0 || REPLYTYPE(r) == 4) return(EX_TEMPFAIL); return(EX_SUCCESS); } /* ** SMTPVERB -- Issue the VERB command ** ---------------------------------- ** ** Returns: ** Status code indicating success or failure. ** ** It is not an error if the remote host does not support this. ** ** Note. Some systems require an 'on' or 'off' parameter. ** Systems that do not require a parameter won't object. */ int smtpverb(onoff) char *onoff; /* some hosts require parameter */ { register int r; smtpmessage("VERB %s", onoff); r = smtpreply("VERB wait", FALSE); if (r < 0 || REPLYTYPE(r) == 4) return(EX_TEMPFAIL); return(EX_SUCCESS); } /* ** SMTPETRN -- Issue the ETRN command ** ---------------------------------- ** ** Returns: ** Status code indicating success or failure. */ int smtpetrn(name) char *name; /* domain name for the ETRN command */ { register int r; smtpmessage("ETRN %s", name); r = smtpreply("ETRN wait", FALSE); if (r < 0 || REPLYTYPE(r) == 4) return(EX_TEMPFAIL); if (REPLYTYPE(r) == 2) return(EX_SUCCESS); if (REPLYTYPE(r) == 5) return(EX_UNAVAILABLE); return(EX_PROTOCOL); } /* ** SMTPRSET -- Issue the RSET command ** ---------------------------------- ** ** Returns: ** Status code indicating success or failure. */ int smtprset() { register int r; smtpmessage("RSET"); r = smtpreply("RSET wait", FALSE); if (r < 0 || REPLYTYPE(r) == 4) return(EX_TEMPFAIL); if (REPLYTYPE(r) == 2) return(EX_SUCCESS); if (REPLYTYPE(r) == 5) return(EX_UNAVAILABLE); return(EX_PROTOCOL); } /* ** SMTPMAIL -- Issue the MAIL command ** ---------------------------------- ** ** Returns: ** Status code indicating success or failure. */ int smtpmail(address) char *address; /* sender address specification */ { register int r; smtpmessage("MAIL From:<%s>", address); r = smtpreply("MAIL wait", FALSE); if (r < 0 || REPLYTYPE(r) == 4) return(EX_TEMPFAIL); if (r == 250) return(EX_SUCCESS); if (r == 552 || r == 554) return(EX_UNAVAILABLE); if (r == 550 || r == 551 || r == 553) return(EX_UNAVAILABLE); if (r == 500 || r == 501 || r == 503) return(EX_UNAVAILABLE); if (r == 521) return(EX_UNAVAILABLE); if (r == 571) return(EX_UNAVAILABLE); return(EX_PROTOCOL); } /* ** SMTPRCPT -- Issue the RCPT command ** ---------------------------------- ** ** Returns: ** Status code indicating success or failure. */ int smtprcpt(address) char *address; /* recipient address specification */ { register int r; smtpmessage("RCPT To:<%s>", address); r = smtpreply("RCPT wait", TRUE); if (r < 0 || REPLYTYPE(r) == 4) return(EX_TEMPFAIL); if (r == 250 || r == 251) return(EX_SUCCESS); if (r == 550 || r == 551 || r == 553) return(EX_NOUSER); if (r == 552 || r == 554) return(EX_UNAVAILABLE); if (r == 500 || r == 501 || r == 503) return(EX_UNAVAILABLE); if (r == 521) return(EX_UNAVAILABLE); if (r == 571) return(EX_UNAVAILABLE); return(EX_PROTOCOL); } /* ** SMTPEXPN -- Issue the EXPN command ** ---------------------------------- ** ** Returns: ** Status code indicating success or failure. */ int smtpexpn(address) char *address; /* address to be verified */ { register int r; smtpmessage("EXPN %s", address); r = smtpreply("EXPN wait", TRUE); if (r < 0 || REPLYTYPE(r) == 4) return(EX_TEMPFAIL); if (REPLYTYPE(r) == 2) /* address was verified ok */ return(EX_SUCCESS); if (r == 550 || r == 551 || r == 553) /* local address but unknown or ambiguous user */ return(EX_NOUSER); if (r == 552 || r == 554) /* address could not be verified */ return(EX_UNAVAILABLE); if (r == 500 || r == 501 || r == 502 || r == 504) /* command not implemented */ return(EX_UNAVAILABLE); if (r == 521) /* not a real mail server */ return(EX_UNAVAILABLE); return(EX_PROTOCOL); } /* ** SMTPVRFY -- Issue the VRFY command ** ---------------------------------- ** ** Returns: ** Status code indicating success or failure. */ int smtpvrfy(address) char *address; /* address to be verified */ { register int r; smtpmessage("VRFY %s", address); r = smtpreply("VRFY wait", TRUE); if (r < 0 || REPLYTYPE(r) == 4) return(EX_TEMPFAIL); if (REPLYTYPE(r) == 2) /* address was verified ok */ return(EX_SUCCESS); if (r == 550 || r == 551 || r == 553) /* local address but unknown or ambiguous user */ return(EX_NOUSER); if (r == 552 || r == 554) /* address could not be verified */ return(EX_UNAVAILABLE); if (r == 500 || r == 501 || r == 502 || r == 504) /* command not implemented */ return(EX_UNAVAILABLE); if (r == 521) /* not a real mail server */ return(EX_UNAVAILABLE); return(EX_PROTOCOL); } /* ** SMTPDATA -- Issue the DATA command ** ---------------------------------- ** ** Returns: ** Status code indicating success or failure. */ int smtpdata() { register int r; /* * Issue the DATA command, and wait for the go-ahead. */ smtpmessage("DATA"); r = smtpreply("DATA wait", FALSE); if (r < 0 || REPLYTYPE(r) == 4) return(EX_TEMPFAIL); if (r == 552 || r == 554) return(EX_UNAVAILABLE); if (r == 500 || r == 501 || r == 503) return(EX_UNAVAILABLE); if (r == 521) return(EX_UNAVAILABLE); if (r != 354) return(EX_PROTOCOL); /* * Transmit the message body. This fails only on I/O errors. */ if (smtpbody() != EX_SUCCESS) return(EX_TEMPFAIL); /* * Terminate the message, and wait for acceptance. */ smtpmessage("."); r = smtpreply("accept wait", FALSE); if (r < 0 || REPLYTYPE(r) == 4) return(EX_TEMPFAIL); if (r == 552 || r == 554) return(EX_UNAVAILABLE); if (r == 500 || r == 501 || r == 503) return(EX_UNAVAILABLE); if (r == 521) return(EX_UNAVAILABLE); if (r != 250) return(EX_PROTOCOL); return(EX_SUCCESS); } /* ** SMTPBODY -- Transmit the message itself ** --------------------------------------- ** ** Returns: ** Status code indicating success or failure. */ int smtpbody() { return(EX_SUCCESS); } /* ** SMTPQUIT -- Issue the QUIT command, and reset connection ** -------------------------------------------------------- ** ** Returns: ** Status code indicating success or failure. ** ** This routine may be called recursively. ** Save the previous state across calls. */ int smtpquit() { int SaveErrno = SmtpErrno; /* save across recursive calls */ char *SavePhase = SmtpPhase; /* save across recursive calls */ if (SmtpIn == NULL && SmtpOut == NULL) return(EX_SUCCESS); if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) { smtpmessage("QUIT"); (void) smtpreply("QUIT wait", FALSE); if (SmtpState == SMTP_CLOSED) { SmtpErrno = SaveErrno; SmtpPhase = SavePhase; return(EX_SUCCESS); } } if (SmtpIn != NULL) (void) fclose(SmtpIn); if (SmtpOut != NULL) (void) fclose(SmtpOut); SmtpIn = SmtpOut = NULL; SmtpState = SMTP_CLOSED; SmtpErrno = SaveErrno; SmtpPhase = SavePhase; return(EX_SUCCESS); } /* ** SMTPMESSAGE -- Output an SMTP command ** ------------------------------------- ** ** Returns: ** None. ** ** Outputs: ** Saves the command in SmtpMsgBuffer. ** ** The command is always followed by a CR/LF combination. */ void /*VARARGS1*/ smtpmessage(fmt, a, b, c, d) char *fmt; /* format of message */ char *a, *b, *c, *d; /* optional arguments */ { if (SmtpOut != NULL) { /* construct the output message */ (void) sprintf(SmtpMsgBuffer, fmt, a, b, c, d); /* display the output in verbose mode */ if (verbose >= 2 || debug) printf(">>> %s\n", SmtpMsgBuffer); /* send the message over the channel */ (void) fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, SmtpCrLf); } } /* ** SMTPREPLY -- Read an SMTP reply to a command ** -------------------------------------------- ** ** Returns: ** The SMTP reply code if the reply has been received. ** -1 on I/O errors or timeout. ** ** Outputs: ** Saves temporary failures in SmtpErrorBuffer. ** Sets SmtpErrno appropriately. ** ** Side effects: ** Calls response() to process response if requested. */ int smtpreply(phase, check) char *phase; /* new connection state message */ bool check; /* process response, if set */ { register int r; register char *p; char *buf; /* current smtp reply buffer */ /* * Define the new SMTP connection state message. */ SmtpPhase = phase; if (debug) printf("smtp phase %s\n", SmtpPhase); /* * Force the previous SMTP message to be written out. * Make sure the connection is still open, and check for errors. */ if (SmtpOut != NULL) { if (fflush(SmtpOut) || ferror(SmtpOut)) { if (errno == 0) errno = EIO; SmtpErrno = errno; SmtpState = SMTP_CLOSED; (void) smtpquit(); return(-1); } } /* * Read the response to the SMTP message. * Only the first line is saved in the main reply buffer. */ for (buf = SmtpReplyBuffer;; buf = SmtpContBuffer) { /* if we are in the process of closing just give the code */ if (SmtpState == SMTP_CLOSED || SmtpIn == NULL) { /* make sure we have a meaningful error message */ if (SmtpErrorBuffer[0] == '\0') (void) strcpy(SmtpErrorBuffer, "Connection closed"); /* return a valid reply code */ SmtpErrno = 0; return(SMTPCLOSING); } /* get the line from the other side */ p = sfgets(buf, BUFSIZ, SmtpIn); if (p == NULL) { /* if the remote end closed early, fake an error */ if (errno == 0) errno = ECONNRESET; SmtpErrno = errno; SmtpState = SMTP_CLOSED; (void) smtpquit(); return(-1); } /* remove cr/lf combination */ fixcrlf(buf, TRUE); /* display the input in verbose mode */ if (verbose >= 2 || debug) printf("<<< %s\n", buf); /* if continuation is required, we can go on */ if (!is_digit(buf[0])) continue; /* decode the reply code */ r = atoi(buf); /* extra semantics: 0xx codes are "informational" */ if (r < 100) continue; /* process response if requested */ if (check) response(buf); /* if continuation is required, we can go on */ if (buf[3] == '-') continue; /* save temporary failure messages for posterity */ if (SmtpReplyBuffer[0] == '4' && SmtpErrorBuffer[0] == '\0') (void) strcpy(SmtpErrorBuffer, &SmtpReplyBuffer[4]); /* reply code 421 is "Service Shutting Down" */ if (r == SMTPCLOSING && SmtpState != SMTP_SSD) { /* send the quit protocol */ SmtpState = SMTP_SSD; (void) smtpquit(); } /* valid reply code received */ SmtpErrno = 0; return(r); } /*NOTREACHED*/ } vrfy-990522.orig/conn.c0100600000175000017500000002064506400616646014672 0ustar herbertgreathan/* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint static char Version[] = "@(#)conn.c e07@nikhef.nl (Eric Wassenaar) 970826"; #endif #include "vrfy.h" extern int verbose; extern int debug; #define incopy(a) *((struct in_addr *)(a)) #define setalarm(n) (void) alarm((unsigned int)(n)) char *CurHostName = NULL; /* remote host we are connecting to */ char *MyHostName = NULL; /* my own fully qualified host name */ int ConnTimeout = CONNTIMEOUT; /* timeout in secs for connect */ int ReadTimeout = READTIMEOUT; /* timeout in secs for read reply */ /* ** SFGETS -- Read an input line, using timeout ** ------------------------------------------- ** ** Returns: ** Pointer to start of input line. ** NULL on error (including timeout). */ static jmp_buf Timeout; sigtype_t /*ARGSUSED*/ timer(sig) int sig; { longjmp(Timeout, 1); /*NOTREACHED*/ } char * sfgets(buf, size, fp) char *buf; /* input buffer */ int size; /* size of input buffer */ FILE *fp; /* input channel */ { register char *p = NULL; if (setjmp(Timeout) != 0) { errno = ETIMEDOUT; return(NULL); } (void) signal(SIGALRM, timer); setalarm(ReadTimeout); while ((p == NULL) && !feof(fp) && !ferror(fp)) { errno = 0; p = fgets(buf, size, fp); if (errno == EINTR) clearerr(fp); } setalarm(0); if ((p == NULL) && feof(fp) && (errno == 0)) errno = ECONNRESET; if ((p == NULL) && ferror(fp) && (errno == 0)) errno = EIO; return(p); } /* ** MAKECONNECTION -- Establish SMTP connection to remote host ** ---------------------------------------------------------- ** ** Returns: ** Status code indicating success or failure. ** ** Outputs: ** Sets outfile and infile to the output and input channel. ** Sets CurHostName to the name of the remote host. */ int makeconnection(host, outfile, infile) char *host; /* host name to connect to */ FILE **outfile; /* smtp output channel */ FILE **infile; /* smtp input channel */ { struct hostent *hp; struct servent *sp; struct sockaddr_in sin; static char hostname[MAXHOST+1]; struct in_addr inaddr[MAXADDRS]; int naddrs; ipaddr_t addr; int sock; register int i; /* * Reset state. */ bzero((char *)&sin, sizeof(sin)); CurHostName = NULL; errno = 0; h_errno = 0; if (host == NULL || host[0] == '\0') host = "localhost"; /* * Check for dotted quad, potentially within brackets. */ addr = numeric_addr(host); if (addr == NOT_DOTTED_QUAD) addr = inet_addr(host); /* * Fetch the ip addresses of the given host. */ if (addr != NOT_DOTTED_QUAD) { inaddr[0].s_addr = addr; naddrs = 1; hp = gethostbyaddr((char *)&inaddr[0], INADDRSZ, AF_INET); if (hp != NULL) host = (char *)hp->h_name; } else { hp = gethostbyname(host); if (hp == NULL) { /* cannot contact nameserver, force retry */ if (errno == ETIMEDOUT || errno == ECONNREFUSED) h_errno = TRY_AGAIN; /* nameserver could not resolve name properly */ if (h_errno == TRY_AGAIN) return(EX_TEMPFAIL); /* no address found by nameserver */ return(EX_NOHOST); } host = (char *)hp->h_name; for (i = 0; i < MAXADDRS && hp->h_addr_list[i]; i++) inaddr[i] = incopy(hp->h_addr_list[i]); naddrs = i; } (void) strncpy(hostname, host, MAXHOST); hostname[MAXHOST] = '\0'; CurHostName = hostname; /* * Try to make connection to each of the addresses in turn. */ sp = getservbyname("smtp", "tcp"); if (sp == NULL) return(EX_OSERR); for (i = 0; i < naddrs; i++) { sin.sin_family = AF_INET; sin.sin_port = sp->s_port; sin.sin_addr = inaddr[i]; sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) return(EX_TEMPFAIL); if (verbose >= 2 || debug) { printf("connecting to %s (%s) port %d\n", CurHostName, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); } if (setjmp(Timeout) != 0) { (void) close(sock); errno = ETIMEDOUT; continue; } (void) signal(SIGALRM, timer); setalarm(ConnTimeout); if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { int err = errno; setalarm(0); (void) close(sock); errno = err; if (errno == EINTR) errno = ETIMEDOUT; if (errno == ECONNREFUSED) return(EX_TEMPFAIL); continue; } setalarm(0); *outfile = fdopen(sock, "w"); *infile = fdopen(dup(sock), "r"); if (*infile == NULL || *outfile == NULL) { int err = errno; if (*infile != NULL) (void) fclose(*infile); if (*outfile != NULL) (void) fclose(*outfile); errno = err; return(EX_TEMPFAIL); } if (debug) printf("connected to %s\n", CurHostName); errno = 0; h_errno = 0; return(EX_SUCCESS); } return(EX_TEMPFAIL); } /* ** SETMYHOSTNAME -- Determine own fqdn hostname ** -------------------------------------------- ** ** Returns: ** None. ** ** Outputs: ** Sets MyHostName to the local hostname. */ void setmyhostname() { static char hostname[MAXHOST+1]; int status; if (MyHostName == NULL) { status = getmyhostname(hostname); if (status != EX_SUCCESS) { giveresponse(status); exit(status); } MyHostName = hostname; } } /* ** GETMYHOSTNAME -- Determine own fqdn hostname ** -------------------------------------------- ** ** Returns: ** Status code indicating success or failure. ** ** Outputs: ** Stores hostname in given buffer. */ int getmyhostname(hostname) char *hostname; /* buffer to store host name */ { struct hostent *hp; errno = 0; h_errno = 0; if (gethostname(hostname, MAXHOST) < 0) { perror("gethostname"); return(EX_OSERR); } hostname[MAXHOST] = '\0'; hp = gethostbyname(hostname); if (hp == NULL) { /* cannot contact nameserver, force retry */ if (errno == ETIMEDOUT || errno == ECONNREFUSED) h_errno = TRY_AGAIN; /* nameserver could not resolve name properly */ if (h_errno == TRY_AGAIN) return(EX_TEMPFAIL); /* no address found by nameserver */ return(EX_NOHOST); } (void) strncpy(hostname, hp->h_name, MAXHOST); hostname[MAXHOST] = '\0'; return(EX_SUCCESS); } /* ** INTERNET -- Check whether given name has an internet address ** ------------------------------------------------------------ ** ** Returns: ** TRUE if an internet address exists. ** FALSE otherwise. ** ** The given name can be a dotted quad, perhaps between ** square brackets. If not, an A resource record must exist. ** ** Note that we do not check the status after a negative return ** from gethostbyname. Failure can be due to nameserver timeout, ** in which case the result is still undecided. ** Currently we consider this an error, so that we won't retry ** such host during recursive lookups. */ bool internet(host) char *host; /* host name to check */ { ipaddr_t addr; struct hostent *hp; /* check dotted quad between brackets */ addr = numeric_addr(host); if (addr != NOT_DOTTED_QUAD) return(TRUE); /* check plain dotted quad */ addr = inet_addr(host); if (addr != NOT_DOTTED_QUAD) return(TRUE); /* check if nameserver can resolve it */ hp = gethostbyname(host); if (hp != NULL) return(TRUE); /* probably not, but could be nameserver timeout */ return(FALSE); } /* ** NUMERIC_ADDR -- Check if we have a dotted quad between brackets ** --------------------------------------------------------------- ** ** Returns: ** The numeric address if yes. ** NOT_DOTTED_QUAD if not. */ ipaddr_t numeric_addr(host) char *host; /* host name to check */ { ipaddr_t addr; register char *p; if (host[0] != '[') return(NOT_DOTTED_QUAD); p = index(host+1, ']'); if (p == NULL || p[1] != '\0') return(NOT_DOTTED_QUAD); *p = '\0'; addr = inet_addr(host+1); *p = ']'; return(addr); } vrfy-990522.orig/stat.c0100600000175000017500000001226606412663470014710 0ustar herbertgreathan/* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint static char Version[] = "@(#)stat.c e07@nikhef.nl (Eric Wassenaar) 970926"; #endif #include "vrfy.h" #if !defined(ERRLIST_DEFINED) extern char *sys_errlist[]; extern int sys_nerr; #endif extern int SmtpErrno; /* saved errno from system calls */ extern char *SmtpPhase; /* connection state message */ extern char *CurHostName; /* remote host that is being contacted */ extern char SmtpErrorBuffer[]; /* smtp temporary failure message */ /* * Status messages. */ static char *SysExMsg[] = { /* 64 EX_USAGE */ "500 Bad usage", /* 65 EX_DATAERR */ "501 Data format error", /* 66 EX_NOINPUT */ "550 Cannot open input", /* 67 EX_NOUSER */ "550 User unknown", /* 68 EX_NOHOST */ "550 Host unknown", /* 69 EX_UNAVAILABLE */ "554 Service unavailable", /* 70 EX_SOFTWARE */ "554 Internal error", /* 71 EX_OSERR */ "451 Operating system error", /* 72 EX_OSFILE */ "554 System file missing", /* 73 EX_CANTCREAT */ "550 Can't create output", /* 74 EX_IOERR */ "451 I/O error", /* 75 EX_TEMPFAIL */ "250 Deferred", /* 76 EX_PROTOCOL */ "554 Remote protocol error", /* 77 EX_NOPERM */ "550 Insufficient permission", /* 78 EX_CONFIG */ "554 Local configuration error", #ifdef notdef /* 79 EX_AMBUSER */ "550 User ambiguous" #endif }; static int N_SysEx = sizeof(SysExMsg) / sizeof(SysExMsg[0]); #define EX__BASE EX_USAGE /* ** STATSTRING -- Fetch message describing result status ** ---------------------------------------------------- ** ** Returns: ** Pointer to appropriate status message. */ char * statstring(stat) int stat; /* result status */ { static char buf[BUFSIZ]; if (stat == EX_SUCCESS) { (void) sprintf(buf, "250 Ok"); return(buf); } stat -= EX__BASE; if (stat < 0 || stat >= N_SysEx) { (void) sprintf(buf, "554 Unknown status %d", stat + EX__BASE); return(buf); } return(SysExMsg[stat]); } /* ** ERRSTRING -- Fetch message describing system call errors ** -------------------------------------------------------- ** ** Returns: ** Pointer to appropriate error message. */ char * errstring(err) int err; /* errno from system calls */ { static char buf[BUFSIZ]; switch (err) { case ETIMEDOUT: case ECONNRESET: case EIO: if (err == ECONNRESET) (void) strcpy(buf, "Connection reset"); else (void) strcpy(buf, sys_errlist[err]); if (SmtpPhase != NULL) { (void) strcat(buf, " during "); (void) strcat(buf, SmtpPhase); } if (CurHostName != NULL) { (void) strcat(buf, " with "); (void) strcat(buf, CurHostName); } return(buf); case EHOSTUNREACH: case EHOSTDOWN: case ENETUNREACH: case ENETDOWN: if (CurHostName == NULL) break; (void) sprintf(buf, "Host %s is unreachable", CurHostName); return(buf); case ECONNREFUSED: if (CurHostName == NULL) break; (void) sprintf(buf, "Connection refused by %s", CurHostName); return(buf); } if (err > 0 && err < sys_nerr) return((char *)sys_errlist[err]); (void) sprintf(buf, "Error %d", err); return(buf); } /* ** GIVERESPONSE -- Issue status message about transaction result ** ------------------------------------------------------------- ** ** Returns: ** None. */ void giveresponse(stat) int stat; /* result status */ { char buf[BUFSIZ]; register char *p; if (stat == EX_TEMPFAIL) { if (h_errno == TRY_AGAIN) /* temporary nameserver failure */ p = "Hostname lookup failure"; else if (SmtpErrno != 0) /* non-fatal system call failure */ p = errstring(SmtpErrno); else /* temporary smtp failure reply message received */ p = SmtpErrorBuffer; /* add extra information for temporary failures */ if (p == NULL || p[0] == '\0') p = "Transient failure"; (void) sprintf(buf, "%s: %s", statstring(stat), p); } else if (stat == EX_NOHOST && h_errno != 0) { /* add extra information from nameserver */ if (h_errno == HOST_NOT_FOUND) p = "Not registered in DNS"; else if (h_errno == NO_ADDRESS) p = "No address or MX record"; else p = "Nameserver lookup failure"; (void) sprintf(buf, "%s (%s)", statstring(stat), p); } else (void) sprintf(buf, "%s", statstring(stat)); /* issue status message */ message(buf); } vrfy-990522.orig/mxrr.c0100600000175000017500000001111606721365455014723 0ustar herbertgreathan/* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint static char Version[] = "@(#)mxrr.c e07@nikhef.nl (Eric Wassenaar) 990522"; #endif #include "vrfy.h" extern int verbose; extern int debug; #if PACKETSZ > 8192 #define MAXPACKET PACKETSZ /* PACKETSZ should be the max udp size (512) */ #else #define MAXPACKET 8192 /* but tcp packets can be considerably larger */ #endif typedef union { HEADER hdr; u_char buf[MAXPACKET]; } querybuf; #ifndef HFIXEDSZ #define HFIXEDSZ 12 /* actually sizeof(HEADER) */ #endif #define MAXMXBUFSIZ (MAXMXHOSTS * (MAXHOST+1)) static char hostbuf[MAXMXBUFSIZ]; char *MxHosts[MAXMXHOSTS]; /* list of names of mx hosts found */ char *dbprefix = DBPREFIX; /* prefix for debug messages to stdout */ /* ** GETMXBYNAME -- Fetch mx hosts for a domain ** ------------------------------------------ ** ** Returns: ** Number of mx hosts found. ** ** Outputs: ** The global array MxHosts contains the mx names. */ int getmxbyname(domain) char *domain; /* domain to get mx hosts for */ { querybuf answer; /* answer buffer from nameserver */ HEADER *hp; /* answer buffer header */ int ancount, qdcount; /* answer count and query count */ u_char *msg, *eom, *cp; /* answer buffer positions */ int type, class, dlen; /* record type, class and length */ u_short pref; /* mx preference value */ u_short prefer[MAXMXHOSTS]; /* saved preferences of mx records */ char *bp; /* hostbuf pointer */ int nmx; /* number of mx hosts found */ register int i; register int j; register int n; /* * Query the nameserver to retrieve mx records for the given domain. */ errno = 0; /* reset before querying nameserver */ h_errno = 0; n = res_search(domain, C_IN, T_MX, (u_char *)&answer, sizeof(answer)); if (n < 0) { if (_res.options & RES_DEBUG) printf("%sres_search failed\n", dbprefix); return(0); } errno = 0; /* reset after we got an answer */ if (n < HFIXEDSZ) { h_errno = NO_RECOVERY; return(0); } /* avoid problems after truncation in tcp packets */ if (n > sizeof(answer)) n = sizeof(answer); /* * Valid answer received. Skip the query record. */ hp = (HEADER *)&answer; qdcount = ntohs((u_short)hp->qdcount); ancount = ntohs((u_short)hp->ancount); msg = (u_char *)&answer; eom = (u_char *)&answer + n; cp = (u_char *)&answer + HFIXEDSZ; while (qdcount-- > 0 && cp < eom) { n = dn_skipname(cp, eom); if (n < 0) return(0); cp += n; cp += QFIXEDSZ; } /* * Loop through the answer buffer and extract mx records. */ nmx = 0; bp = hostbuf; while (ancount-- > 0 && cp < eom && nmx < MAXMXHOSTS) { #ifdef obsolete if (verbose >= 4 || debug) (void) p_rr((qbuf_t *)cp, (qbuf_t *)msg, stdout); #endif /*obsolete*/ n = dn_expand(msg, eom, cp, (nbuf_t *)bp, MAXHOST); if (n < 0) break; cp += n; type = _getshort(cp); cp += INT16SZ; class = _getshort(cp); cp += INT16SZ; /* ttl = _getlong(cp); */ cp += INT32SZ; dlen = _getshort(cp); cp += INT16SZ; if (type != T_MX || class != C_IN) { cp += dlen; continue; } pref = _getshort(cp); cp += INT16SZ; n = dn_expand(msg, eom, cp, (nbuf_t *)bp, MAXHOST); if (n < 0) break; cp += n; prefer[nmx] = pref; MxHosts[nmx] = bp; nmx++; n = strlength(bp) + 1; bp += n; } /* * Sort all records by preference. */ for (i = 0; i < nmx; i++) { for (j = i + 1; j < nmx; j++) { if (prefer[i] > prefer[j]) { register u_short tmppref; register char *tmphost; tmppref = prefer[i]; prefer[i] = prefer[j]; prefer[j] = tmppref; tmphost = MxHosts[i]; MxHosts[i] = MxHosts[j]; MxHosts[j] = tmphost; } } } return(nmx); } vrfy-990522.orig/util.c0100600000175000017500000001034406432700202014670 0ustar herbertgreathan/* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint static char Version[] = "@(#)util.c e07@nikhef.nl (Eric Wassenaar) 971113"; #endif #include "vrfy.h" /* ** FIXCRLF -- Fix trailing combination in line ** ---------------------------------------------------- ** ** Unconditionally strip a trailing from an input line, ** and optionally strip a preceding . ** Both characters must be removed during SMTP input, since ** they are part of the protocol. ** But when reading local input, we must just remove the ** trailing , being the UNIX canonical character, ** and preserve a possible . This guarantees transparent ** transmission of files ending in . ** ** Returns: ** None. ** ** Side effects: ** The line is changed in place. */ void fixcrlf(line, stripcr) char *line; /* the input line to fix */ bool stripcr; /* also strip CR, if set */ { register char *p; p = index(line, '\n'); if (p != NULL) { /* move back to preceding CR if necessary */ if (stripcr && ((p > line) && (p[-1] == '\r'))) p--; /* properly terminate the line */ *p = '\0'; } } /* ** MAXSTR -- Ensure string does not exceed maximum size ** ---------------------------------------------------- ** ** Returns: ** Pointer to the (possibly truncated) string. ** ** If necessary, a new string is allocated, and is then ** truncated, and the original string is left intact. ** Otherwise the original string is truncated in place. ** */ char * maxstr(string, n, save) char *string; /* the string to check */ int n; /* the maximum allowed size */ bool save; /* allocate new string, if set */ { if (strlength(string) > n) { if (save) string = newstr(string); string[n] = '\0'; } return(string); } /* ** PRINTABLE -- Expand quote bits/control chars in a string ** -------------------------------------------------------- ** ** Returns: ** Pointer to static buffer containing the expansion. ** ** The expanded string is silently truncated if it gets too long. */ char * printable(string) char *string; /* the string to expand */ { static char buf[BUFSIZ]; /* expanded string buffer */ register char *p = buf; register char *s = string; register char c; while ((c = *s++) != '\0') { if (p >= buf + sizeof(buf) - 4) break; if (!isascii(c)) { *p++ = '\\'; c &= 0177; } if (iscntrl(c) && !isspace(c)) { *p++ = '^'; c ^= 0100; } *p++ = c; } *p = '\0'; return(buf); } /* ** XALLOC -- Allocate or reallocate additional memory ** -------------------------------------------------- ** ** Returns: ** Pointer to (re)allocated buffer space. ** Aborts if the requested memory could not be obtained. */ ptr_t * xalloc(buf, size) register ptr_t *buf; /* current start of buffer space */ siz_t size; /* number of bytes to allocate */ { if (buf == NULL) buf = malloc(size); else buf = realloc(buf, size); if (buf == NULL) { error("Out of memory"); exit(EX_OSERR); } return(buf); } /* ** ITOA -- Convert integer value to ascii string ** --------------------------------------------- ** ** Returns: ** Pointer to static storage containing string. */ char * itoa(n) int n; /* value to convert */ { static char buf[30]; /* sufficient for 64-bit values */ (void) sprintf(buf, "%d", n); return(buf); } vrfy-990522.orig/vers.c0100600000175000017500000000025006721365522014702 0ustar herbertgreathan#ifndef lint static char Version[] = "@(#)vers.c e07@nikhef.nl (Eric Wassenaar) 990522"; #endif char *version = "990522"; #if defined(apollo) int h_errno = 0; #endif vrfy-990522.orig/vrfy.10100600000175000017500000002666606432721154014646 0ustar herbertgreathan.\" .\" @(#)vrfy.1 e07@nikhef.nl (Eric Wassenaar) 971114 .\" .TH vrfy 1 "971114" .SH NAME vrfy \- Verify electronic mail addresses .SH SYNTAX .B vrfy [\fIoptions\fP] [\fB\-v\fP] \fIaddress\fP [\fIhost\fP] .br .B vrfy [\fIoptions\fP] [\fB\-v\fP] \fB\-f\fP [\fIfile\fP] [\fIhost\fP] .br .B vrfy [\fIoptions\fP] [\fB\-v\fP] \fB\-p\fP \fIdomain\fP .br .B vrfy [\fIoptions\fP] [\fB\-v\fP] \fB\-T\fP \fIdomain\fP [\fIname\fP] .SH DESCRIPTION .I vrfy is a tool to verify electronic mail addresses. The only required parameter is the \fIaddress\fP to be verified. The actual verification will be carried out at the remote machine where mail to the given address would normally be delivered. This may be a mail exchange host and not the final destination host. If an explicit host is given as the second argument on the command line, the specified address is verified at that machine. .PP The output of the program shows whether or not the address can be handled at the contacted host. Error messages will be printed if the address is invalid. If the address is recognized, the output shows the remote host's notion of the (possibly translated) address. If it represents a local user at that host, additional information about the user may be displayed. .PP Note that if the address is accepted by the contacted host, but this is not the final destination host, one still cannot be sure that the address represents a valid recipient. .PP It is a handy tool to verify local mail addresses. .br If you have set up a .forward file, you can verify your own address to check that the forwarding is done properly. .br If you specify an arbitrary name, you can check whether this resolves to any valid recipients. .SH "SPECIAL OPERATION MODES" Some flags put the program in a special operation mode. .SS "FILE MODE" If the \fB\-f\fP option is specified, the program enters a special file mode. The (optional) parameter is the name of a \fIfile\fP containing electronic mail addresses, such as used for mailing lists. Verification of all addresses present in the file is attempted. If no filename is specified, vrfy acts as a filter and the addresses to be verified are read from standard input. .SS "PING MODE" If the \fB\-p\fP option is specified, the program enters a special ping mode. The required parameter is an electronic mail \fIdomain\fP for which it will be verified whether the corresponding mail exchangers are currently responding to SMTP requests. .SS "ETRN MODE" If the \fB\-T\fP option is specified, the program enters a special etrn mode. The required parameter is an electronic mail \fIdomain\fP. An ESMTP ETRN command will be delivered to its corresponding mail exchangers. An optional \fIname\fP is used as the ETRN parameter. If not specified, the local host name is used instead. ETRN is defined in RFC 1985, and is implemented in sendmail 8.8. .SS "RECURSIVE MODE" If the \fB\-L\fP\ \fIlevel\fP option is specified, the received replies to an address verification are resursively verified up to \fIlevel\fP levels deep. This is handy for checking mailing list expansions, and detecting possible mail forwarding loops. .sp Recursion stops automatically if the reply represents a local recipient address, or in case a forwarding loop is detected. If the received reply is the same (modulo comments) as the address that was asked for, the request is retried at its domain itself, unless this was the machine we just queried, or it is not an internet domain host. .sp The option \fB\-R\fP is equivalent to ``\fB\-L\fP\ \fI17\fP''. This is the maximum hop count normally used during actual mail relaying. This option also implies ``\fB\-s\fP''. .SH OPTIONS .TP 8 .B \-v Sets verbose mode. Prints the action the program is going to take. .br If specified as \fB\-vv\fP, very verbose output is printed. The SMTP transactions with the remote host are shown in detail. .br If the verbose mode level is set to 3 with \fB\-vvv\fP, the actions taken by the remote host are shown, such as alias and forward expansions. This can be quite amusing. The remote host must support the SMTP VERB command for this to work. .br In ping mode, the verbose mode level 3 will cause an extra SMTP command HELO to be generated. .TP .B \-d Prints debugging output showing the various phases of the SMTP connection with the remote host. This is normally not of any interest. Also prints various messages that are otherwise shown in very verbose mode. .br If specified as \fB\-dd\fP, in addition all nameserver query transactions are shown in detail. .br A level 3 debug mode \fB\-ddd\fP will not make any SMTP connections at all. .TP .B \-a If multiple mail exchange hosts were found, this option will cause the operation to be carried out at each of these hosts. Normally only the primary mail exchanger is contacted. .TP .B \-l Sets local mode for error handling. Address syntax and parsing errors are handled entirely by the program. The default is to pass faulty addresses to the local host to get the official error messages. .TP .B \-s Strips all comments from full address specifications, and presents only the plain address to the remote host to be verified. Some hosts may get confused when a multiple bracketed address is presented. This option is implied when \fB\-R\fP is given. .TP .B \-e Uses the EXPN command instead of the VRFY command. Some systems may have different treatment for these commands, so this option may be a possible alternative. By sendmail V5 they are usually treated the same, but not by the sendmail V8 versions. In case treatment is different, the VRFY command will just show whether the presented address is valid or not, but the EXPN command will return alias expansions, if appropriate. Some systems allow VRFY, but disallow EXPN for privacy reasons. .TP .B \-h This option causes an SMTP HELO command to be issued to the contacted server before subsequent commands are attempted. Servers can be configured to refuse commands unless a HELO command is given first. The HELO is not issued by default, since old versions of sendmail refuse SMTP connections originating on the same machine (i.e. if the domain name in the HELO command is its own name). .TP .B \-H Similar to the \fB\-h\fP option, but now the ESMTP EHLO command is tried first. If the contacted server does not understand this, the SMTP HELO command is tried next. .TP .B \-n Many non-sendmail hosts do not, or incorrectly or incompletely, implement the VRFY command. Other systems have VRFY or EXPN disabled for privacy reasons. This option uses an alternative protocol suite with the regular HELO, MAIL, RCPT and RSET commands. This gives only a global indication whether the recipient is valid. Recursive mode is not possible, and will be disabled. .TP .BI \-S " sender" Defines an explicit envelope sender address to be used in the MAIL command during the alternative protocol suite, to overrule the default empty sender address ``<>''. This option implies ``\fB\-n\fP''. .TP .BI \-c " secs" Sets a connect timeout value to override the default timeout of 6 seconds. This may be necessary if it takes considerable time to connect to the remote host. .TP .BI \-t " secs" Sets a read timeout value to override the default timeout of 60 seconds. This may be necessary if it takes considerable time for the remote host to assemble all addresses in long mailing lists. .SH "DEFAULT OPTIONS" Default options and parameters can be preset in an environment variable \fBVRFY_DEFAULTS\fP using the same syntax as on the command line. They will be evaluated before the command line arguments. .SH DIAGNOSTICS .I vrfy sets up an SMTP connection with the remote host where the verifying is to be carried out. SMTP is the TCP/IP protocol for electronic mail exchange. .sp If the given host does not speak SMTP, the error message "Connection refused" is printed. .sp If the SMTP protocol is blocked, a "Host is unreachable" message is printed. .sp If the remote host doesn't respond in time, you will get a "Connection timed out" message. .sp The remote host must support the SMTP VRFY command for doing the verification. If not, a "Command not implemented" is printed. .SH STRATEGY The following strategy is used to find the remote host where the verification is to be carried out. .TP 4 .B a. If an explicit verification host is specified, that host is contacted unconditionally. .TP .B b. If the address to be verified has no domain part after an '@' sign, it is supposed to be a local recipient and will be verified at the local host. .TP .B c. If the address contains a domain part after an '@' sign, mail exchange hosts will be fetched for that domain. If no such mail exchangers are found, the domain part is supposed to represent a specific host, and that host itself will be contacted. .TP .B d. If mail exchangers were found, the verification will be carried out at the primary mail exchange host. .TP .B e. If the \fB\-a\fP option was given, and multiple mail exchangers exist, the verification is also carried out at all other mail exchange hosts. .TP .B f. For the pseudo domains ".uucp" and ".bitnet" a specific server can be compiled-in. The default is to contact the local host. .TP .B g. If no mail exchangers were found for an unqualified single hostname, the local host is contacted by default. The actual meaning of such addresses depend on your local strategy. .SH ENVIRONMENT Environment variables can be used to overrule several compiled-in defaults. .SS VRFY_LOCALHOST This is your nearest host running the sendmail daemon. It is contacted in case local addresses without a domain are given. Also (unless the \fB\-l\fP is given) when domain parsing errors were encountered, assuming that this host can give a more appropriate error message. The default value is \fBlocalhost\fP. .SS VRFY_UUCPRELAY This host is contacted when a .uucp address is specified. You probably won't get much useful information. The default value is \fBlocalhost\fP. .SS VRFY_BITNETRELAY This host is contacted when a .bitnet or .earn address is specified. You probably won't get much useful information. The default value is \fBlocalhost\fP. .SS VRFY_SINGLERELAY This host is contacted when a single unqualified host name could not be resolved to a fully qualified MX domain host. It is assumed that single hosts in your own domain can be resolved, i.e. they have an MX record. It depends on your local strategy for unqualified hosts what they mean: a .uucp host, a .bitnet host, or just a local host without MX. The default value is \fBlocalhost\fP. .SH LIMITATIONS Some hosts have a lousy VRFY handling. Sometimes the command is not implemented at all. Other hosts are willing to verify only local recipients. The ``-n'' option may be necessary. .sp Other servers may refuse the VRFY command for privacy reasons. .sp If the verification is not performed at the final destination host, one cannot be sure that the given address is valid. .SH "MAILING LISTS" Note the following subtle differences if you want to check an existing mailing list \fIlist\fP. Suppose that ``\fIlist\fP\-users'' is the local alias to include the actual file ``/mail/lists/\fIlist\fP\-users'' with recipients. .sp The command ``vrfy\ \fIlist\fP\-users'' will verify the proper expansion of the mailing list at the local host. No remote hosts are contacted to verify addresses. .sp The command ``vrfy\ \-f\ /mail/lists/\fIlist\fP\-users'' will verify each individual address at the appropriate remote hosts. .sp The same effect can be reached when you give the command ``vrfy\ \-L\ 1\ \fIlist\fP\-users''. Use a higher recursion level if you want to see further expansion of the mailing list. .SH AUTHOR Eric Wassenaar, Nikhef-H,