                                       -           Message Exchange Programmer's Guide                  October, 1998       8           This manual describes how to customize Message'           Exchange through programming.         A           Revision/Update Information:  This is a revised manual.   =           Operating System and Version: VAX/VMS V5.2 or later   =                                         OpenVMS Alpha V1.5 or -                                         later   =           Software Version:             Message Exchange V5.1     )           Matt Madison and Hunter Goatley            MadGoat Software         "           17_October_1998_________  ?           The information in this document is subject to change 9           without notice and should not be construed as a :           commitment by MadGoat Software. MadGoat Software;           assumes no responsibility for any errors that may "           appear in this document.  8           No part of this publication may be reproduced,9           transmitted, transcribed, stored in a retrieval =           system, or translated into any language or computer ;           language, in any form or by any means electronic, ?           mechanical, magnetic, optical, chemical, or otherwise 9           without the prior written permission of MadGoat            Software.   ;           Use of this software and documentation is subject >           to the terms and conditions set forth in the License           Agreement.  =           The Licensed Materials are provided with RESTRICTED 8           RIGHTS. Use, duplication, or disclosure by the<           Government is subject to restrictions as set forth?           in subparagraph (c)(1)(ii) of the Rights in Technical >           Data and Computer Software clause at DFARS 252.227-@           7013 or subparagraphs (c)(1) and (2) of the Commercial@           Computer Software-Restricted Rights at 48 CFR 52.227-           19, as applicable.  =           MadGoat, Message Exchange, and MX are trademarks of            MadGoat Software.   ;           The following are trademarks of Digital Equipment            Corporation:  7           DEC                DECnet              P.S.I. ;           ULTRIX             VAX                 VAXcluster ;           VMS                AXP                 VMScluster   @           Jnet is a registered trademark of Wingra Technologies,           Inc.  ;           MultiNet and TCPware are registered trademarks of '           Process Software Corporation.   6           LISTSERV is a registered trademark of L-Soft           International.  :           WIN/TCP and Pathway are registered trademarks of           Attachmate, Inc.             __________@           Copyright 1998 MadGoat Software. ALL RIGHTS RESERVED.                       A           _______________________________________________________              Contents  A                 _________________________________________________ A                 PREFACE                                         v   A           _______________________________________________________ A           CHAPTER 1  THE SITE TRANSPORT INTERFACE             1-1   A                 _________________________________________________ A                 1.1   THE SITE DELIVERY INTERFACE             1-1   A                 _________________________________________________ A                 1.2   SITE MESSAGE ENTRY                      1-2     A           _______________________________________________________ A           CHAPTER 2  ADDRESS MODIFICATION INTERFACE           2-1   A                 _________________________________________________ A                 2.1   ADDRESS REWRITING                       2-2 :                 INIT                                   2-3:                 REWRITE_HEADER                         2-5:                 REWRITE_ENVELOPE                       2-8:                 CLEANUP                               2-10  A                 _________________________________________________ A                 2.2   HOST NAME EXPANSION                    2-12 :                 INIT                                  2-13:                 EXPAND                                2-15:                 CLEANUP                               2-17  A                 _________________________________________________ A                 2.3   NAME CONVERSION                        2-19 :                 INIT                                  2-20:                 CONVERT                               2-22:                 CLEANUP                               2-25:                 FULL_CONVERT                          2-27      A                                                               iii                     Contents          A           _______________________________________________________ A           APPENDIX A  ADDRESS REWRITER EXAMPLE                A-1     A           _______________________________________________________ A           APPENDIX B  DOMAIN EXPANSION EXAMPLE                B-1     A           _______________________________________________________ A           APPENDIX C  NAME CONVERSION EXAMPLE                 C-1     A           _______________________________________________________            EXAMPLES  A                 1-1       Sample SITE_DELIVER.COM  _______    1-3                                                            iv                   A           _______________________________________________________              Preface   5           Message Exchange (MX) provides two forms of 9           customization: an interface for a site-specific <           transport, and interfaces for modifying addresses.8           This manual describes how to write routines or>           programs to use these interfaces for customizing MX.    L           __________________________________________________________________             Intended Audience   =           This manual is intended for systems programmers who /           will be writing code to customize MX.   L           __________________________________________________________________             Document Structure  5           This guide consists of two chapters and two <           appendices. Chapter 1 describes the SITE transport4           interface. Chapter 2 describes the address=           modification interfaces. The two appendices include ?           sample code for illustrating the address modification            interfaces.   L           __________________________________________________________________             Related Documents   >           You can find additional information in the following           documents:  >           o  Message Exchange Installation Guide describes the               installation of MX.  <           o  Message Exchange Management Guide describes the,              management and operation of MX.  ?           o  Message Exchange Release Notes contain information A              and updates not included in this manual. The release =              notes are part of the software distribution kit.   A                                                                 v                      A           _______________________________________________________   &    1      The SITE Transport Interface      >           MX provides delivery agents and message entry agents=           for various transports, including TCP/IP, Jnet, and <           UUCP. If your site has some network transport that>           is not supported by MX, you can interface it with MX/           through the SITE transport interface.   L           __________________________________________________________________  %    1.1    The SITE Delivery Interface   @           When you use the MCP DEFINE PATH command to route mail<           to the SITE path, the MX_SITE delivery agent takes<           messages routed to that path and feeds them into a?           subprocess that executes a command procedure you must ;           provide. The command procedure must be called MX_ ?           EXE:SITE_DELIVER.COM and must accept four parameters.   =           The first parameter is the "route" parameter, which <           is either the host name part of the address or the<           value of the /ROUTE qualifier from the DEFINE PATH>           command that routed the message to the SITE delivery@           agent. This parameter can be used to distinguish among=           several installed site-specific delivery agents, if            needed.   >           The second parameter is the name of a temporary file@           that contains the message, including all of the RFC822<           headers (corresponding to the DATA part of an SMTP:           transaction). The third parameter is the name of>           another temporary file that contains the recipient's<           address, corresponding to the RCPT TO addresses of:           an SMTP transaction. The fourth parameter is the:           RFC822 address of the originator of the message,;           corresponding to the MAIL FROM address of an SMTP            transaction.  A                                                               1-1          &           The SITE Transport Interface          =           Your delivery procedure and the programs it invokes 9           must not cause the subprocess to terminate, nor ;           should they rely on specifics about the filenames :           provided. The procedure MUST exit with a success9           status code to let MX know that the message was >           delivered successfully. If there was an error in the@           delivery and you wish MX to return an error message to?           the sender, you should exit with a non-success status =           code. If the severity of the status is SEVERE (also ?           called FATAL), the SITE delivery agent will return an =           error message to the sender. Otherwise, the message >           will be queued for another attempt. The MCP SET SITE@           command controls how many attempts will be made before>           the delivery agent gives up; the default is 96, with*           30 minutes between each attempt.  L           __________________________________________________________________      1.2    SITE Message Entry  ?           The SITE message entry program should be used by your =           SITE transport agent to enter a message into the MX =           message queue. The program is called MX_SITE_IN and :           resides in MX_EXE. It should be invoked as a DCL           foreign command:  <                            $ MX_ENTER = "$MX_EXE:MX_SITE_IN"V                            $ MX_ENTER  msg-file-spec  dest-file-spec  [origin-address]  8           MX_SITE_IN takes up to three parameters, which@           correspond exactly to the last three parameters passed@           out by the MX_SITE delivery agent. The first parameter=           should be the name of a file containing a properly- ?           formatted RFC822 message. The second parameter should ;           be the name of a file containing a list of RFC822 ?           route addresses (they must have the surrounding angle >           brackets, just as in an SMTP transaction). The third<           parameter, which is optional, should be the RFC8229           route address of the sender (also including the   
           1-2          A                                      The SITE Transport Interface         .           Example 1-1  Sample SITE_DELIVER.COMA           _______________________________________________________   U           $! Simple SITE_DELIVER.COM which invokes a real program to do all the work. T           $! This file must be placed in MX_EXE: for use with the MX SITE interface.+           $! It is invoked by MX_SITE with:            $!U           $!  @MX_EXE:SITE_DELIVER  route msg-file-spec dest-file-spec origin-address            $!K           $! The originator address is stuck in a file since it can contain S           $! characters that might confuse DCL when we invoke the delivery program.            $!P           $! This is a simple procedure which ignores the "route" parameter.  IfW           $! you have multiple SITE delivery paths available, use the "route" parameter F           $! to route the message to the appropriate delivery program.           $!           $ SET NOON           $!5           $ DELIVER = "$my_mail_system:enter_message"            $!)           $ CREATE my_temp_dir:SENDER.TMP 2           $ OPEN/APPEND TMP my_temp_dir:SENDER.TMP           $ WRITE TMP P4           $ CLOSE TMP            $!2           $ DELIVER 'P2 'P3 my_temp_dir:SENDER.TMP           $ STAT = $STATUS+           $ DELETE my_temp_dir:SENDER.TMP;* A           $_EXIT_'STAT___________________________________________   @           surrounding angle brackets). If the third parameter is>           omitted, the address of the user running the program4           will be used as the origin of the message.                A                                                               1-3                      A           _______________________________________________________   (    2      Address Modification Interface      8           MX provides an interface for altering envelope:           addresses. This interface allows you to add your<           own routines for performing two different types of?           address modifications. For each type of modification, ;           the routines must be part of a shareable library, ?           which gets mapped into the appropriate parts of MX at .           run-time with LIB$FIND_IMAGE_SYMBOL.  ?           The address modification routines are located through #           the use of logical names.   A           _______________________________________________________ A           Logical_name____________Modification_type______________   @           MX_SITE_ADDRESS_        Modifying headers and envelope<           REWRITER                addresses for outgoing and/                                   incoming mail   =           MX_SITE_DOM_EXPANSION   Modifying or expanding host '                                   names   >           MX_SITE_NAME_           Translating local aliases orA           CONVERSION______________performing_directory_lookups___   8           In each case, the logical name must be defined8           /SYSTEM/EXEC and must translate to the name of:           an image that has been linked /SHARE/NOTRACE and<           INSTALLed on the system. If you name the shareable<           images ADDRESS_REWRITER.EXE, DOMAIN_EXPANSION.EXE,:           and NAME_CONVERSION.EXE, respectively, and place=           them in the MX_ROOT:[EXE] directory, the MX startup ?           procedure will automatically create the logical names 3           and INSTALL the shareable images for you.   A                                                               2-1          (           Address Modification Interface          5           Examples of routines for performing address 9           modifications are included in the directory MX_ 8           ROOT:[EXAMPLES] (if they have been installed).  L           __________________________________________________________________      2.1    Address Rewriting   >           The site address rewriter routines are called by the@           Router process to allow RFC822 header address rewrites@           on all outgoing mail, regardless of its origin, and on@           envelope addresses for incoming mail. The main purpose?           for these routines is to allow site-specific user and "           host naming conventions.  4           The name conversion routines, described in<           Section 2.3, provide a means for implementing user<           aliases, but it does not affect domain names. With;           the address rewriter routines, both usernames and <           host names can be modified. To ease mail delivery,=           many sites prefer to hide the various machines used ?           at that site by supplying a generic site name for the >           address. For example, the generic domain MADGOAT.COM:           might be used for all addresses, even though the=           machines in use may be named HUNTER.MADGOAT.COM and            MATT.MADGOAT.COM.   >           A sample address rewriter is provided in Appendix A.=           This example converts RFC822 "From:" addresses to a @           format like "First.Lastname@Generic.Node" and envelope;           addresses from that format to the actual user andu           node.   :           The routines that must be provided by an address8           rewriter are described on the following pages.          
           2-2          B                                     Address Modification InterfaceA                                                              INIT         A           _______________________________________________________              INIT  !           Initialization routine.t  A           _______________________________________________________u             FORMAT             INIT  contexts  A           _______________________________________________________e             RETURNS            VMS Usage: cond_value (           type:      longword (unsigned)           access:    write onlyo           mechanism: by valuee  @           The INIT routine must return a success status value in>           order for the other address rewriting routines to be           used.a  A           _______________________________________________________l             ARGUMENTSe           context              VMS Usage: context(           type:      longword (unsigned)           access:    modifyt!           mechanism: by referenceu@           This is a longword passed by reference to your routine@           that you may use for any purpose, such as allocating a=           block of memory for keeping contextual information.         A                                                               2-3t r       (           Address Modification Interface           INIT      A           _______________________________________________________              DESCRIPTION ?           This routine is called by the Router before any callst>           to the REWRITE_HEADER and REWRITE_ENVELOPE routines.;           You may use this routine to set up any context oro>           perform any housekeeping tasks needed to prepare for9           the subsequent calls to the REWRITE_* routines.   ;           Since your routines must be reentrant, you should ;           not use static storage for keeping track of state ?           information or other contextual information. Instead, ;           you should allocate a block of dynamic memory and (           return its address in context.                                                      
           2-4i         B                                     Address Modification InterfaceA                                                    REWRITE_HEADER         A           _______________________________________________________              REWRITE_HEADER  <           Routine to rewrite an address in an RFC822 header.  A           _______________________________________________________              FORMAT  ?           REWRITE_HEADER  context, inaddr, outaddr, header_codeR  A           ________________________________________________________             RETURNS_           VMS Usage: cond_value (           type:      longword (unsigned)           access:    write only            mechanism: by value_  @           To indicate a successful rewrite, return SS$_NORMAL or@           some other success status code. If you do not return a>           success status code, the caller will assume that the            rewrite did not occur.  A           _______________________________________________________              ARGUMENTSD           contextN             VMS Usage: context(           type:      longword (unsigned)           access:    modify !           mechanism: by reference <           This is the same value that was passed to the INIT           routine.             inaddr              VMS Usage: char_string%           type:      character string            access:    read only2           mechanism:  by descriptor (fixed-length)4           The RFC822 header address to be rewritten.  A                                                               2-5     2    (           Address Modification Interface           REWRITE_HEADER                   outaddr               VMS Usage: char_string%           type:      character string            access:    write only_"           mechanism: by descriptor:           A string into which your routine should copy the=           rewritten address, if expansion was successful. You >           must use the STR$ string routines (such as STR$COPY_4           DX) to copy the string into this argument.             header_code   (           type:      longword (unsigned)           access:    read only           mechanism: by value :           A value representing one of the following RFC822           header types:_  A           _______________________________________________________ A           Description____Symbolic_name___________Value____________  2           From:          MX_K_HDR_FROM           1  2           Sender:        MX_K_HDR_SENDER         2  2           To:            MX_K_HDR_TO             3  2           Resent-To:     MX_K_HDR_R_TO           4  2           CC:            MX_K_HDR_CC             5  2           Resent-CC:     MX_K_HDR_R_CC           6  2           BCC:           MX_K_HDR_BCC            7  2           Resent-BBC:    MX_K_HDR_R_BCC          8  3           Reply-To:      MX_K_HDR_REPLY_TO       17   3           Resent-        MX_K_HDR_R_REPLY_TO     19_           Reply-To:   3           Resent-From:   MX_K_HDR_R_FROM         20r  3           Resent-        MX_K_HDR_R_SENDER       21fA           Sender:________________________________________________o  ;           The symbolic names are defined in MX_HDR.H in MX_e9           ROOT:[EXAMPLES], if you installed the examples.r  
           2-6          B                                     Address Modification InterfaceA                                                    REWRITE_HEADERe      A           _______________________________________________________z             DESCRIPTION_@           This routine is called to rewrite an address appearing>           in an RFC822 header on all outgoing mail, regardless=           of its origin. The address of the context block you =           allocated in the INIT routine is passed in here fore;           any information you need to keep track of betweene:           calls. This routine may be called more than once5           between one pair of INIT and CLEANUP calls._                                                              A                                                               2-7o    o    (           Address Modification Interface           REWRITE_ENVELOPE        A           _______________________________________________________              REWRITE_ENVELOPE  8           Routine to rewrite an RFC821 envelope address.  A           _______________________________________________________i             FORMAT  4           REWRITE_ENVELOPE  context, inaddr, outaddr  A           _______________________________________________________              RETURNS            VMS Usage: cond_value (           type:      longword (unsigned)           access:    write only_           mechanism: by value   @           To indicate a successful rewrite, return SS$_NORMAL or@           some other success status code. If you do not return a>           success status code, the caller will assume that the            rewrite did not occur.  A           _______________________________________________________              ARGUMENTSI           contexta             VMS Usage: context(           type:      longword (unsigned)           access:    modifyI!           mechanism: by reference <           This is the same value that was passed to the INIT           routine.             inaddr              VMS Usage: char_string%           type:      character stringa           access:    read only2           mechanism:  by descriptor (fixed-length)  
           2-8o u  m    B                                     Address Modification InterfaceA                                                  REWRITE_ENVELOPEt        =           The RFC821 envelope address to be rewritten. RFC821e<           addresses are enclosed in angle brackets (<>). For9           example, "<GENE@MADGOAT.COM>" is a valid RFC821i           envelope address.a             outaddro              VMS Usage: char_string%           type:      character stringt           access:    write only "           mechanism: by descriptor:           A string into which your routine should copy the=           rewritten address, if expansion was successful. Yout>           must use the STR$ string routines (such as STR$COPY_4           DX) to copy the string into this argument.  <           Note: The rewritten address must be a valid RFC8210           address, including the angle brackets.  A           _______________________________________________________              DESCRIPTIONs>           This routine is called to rewrite an RFC821 envelope>           address on incoming mail. Envelope addresses are the=           addresses of the actual recipients of incoming mailE>           and may or may not correspond directly to the RFC822           headers.  ?           The address of the context block you allocated in theo@           INIT routine is passed in here for any information you?           need to keep track of between calls. This routine may ?           be called more than once between one pair of INIT ande           CLEANUP calls.              A                                                               2-9e g  o    (           Address Modification Interface           CLEANUP         A           _______________________________________________________              CLEANUPT  "           Context cleanup routine.  A           _______________________________________________________              FORMAT             CLEANUP  context  A           _______________________________________________________              RETURNSy           VMS Usage: cond_value6(           type:      longword (unsigned)           access:    write only_           mechanism: by value_  >           This routine should return a status value indicating>           the success or failure of the cleanup operation. The:           caller may or may not ignore the returned value.  A           _______________________________________________________              ARGUMENTS            contexts             VMS Usage: context(           type:      longword (unsigned)           access:    modify !           mechanism: by referencem?           The address of the context block you allocated in theT           INIT routine.r  A           _______________________________________________________t             DESCRIPTION ;           This routine is called to clean up after a series =           of REWRITE_* calls. You should clean up the context @           information and deallocate the context block allocated           by the INIT routine.             2-10         B                                     Address Modification InterfaceA                                                           CLEANUPe        =           If you did not allocate a context block in the INITf>           routine, you must still have a CLEANUP routine, even+           if it just returns to the caller.                                                                           A                                                              2-11_         (           Address Modification Interface        L           __________________________________________________________________      2.2    Host Name Expansionf  >           The site host name routines are called by the Router;           process just before path identification. The mainf=           purpose for these routines is to expand abbreviateda<           host names into full host names that will properly@           match one of the paths defined in the MX configuration           file.   :           When you install SMTP support with MX, host name<           expansion routines are automatically provided that8           call on the underlying TCP/IP package to catch<           abbreviated host names that might be recognized by=           the TCP/IP name resolver but are not defined in the >           MX configuration file. The source for these routines@           is included in MX_ROOT:[EXAMPLES] for you to modify if           needed.E  =           Another possible use for host name expansion is for =           sites running Jnet. Normally, the Router identifies >           a BITNET-destined message by looking for the .BITNET=           suffix on the host name. A local host name expander_=           could be used to allow users to just use the BITNET =           node name without a suffix. Each host name could beg<           checked by the expander against a BITNET host name=           table; a matching name would get the .BITNET suffix            appended.-  @           A sample host name expander is provided in Appendix B.  ;           The routines that must be provided by a host namet8           expander are described on the following pages.                         2-12 .  i    B                                     Address Modification InterfaceA                                                              INIT         A           _______________________________________________________l             INIT  !           Initialization routine.a  A           _______________________________________________________              FORMAT             INIT  context   A           ________________________________________________________             RETURNS_           VMS Usage: cond_value (           type:      longword (unsigned)           access:    write only            mechanism: by valuei  @           The INIT routine must return a success status value in<           order for the other expansion routines to be used.  A           _______________________________________________________h             ARGUMENTS            contexte             VMS Usage: context(           type:      longword (unsigned)           access:    modify_!           mechanism: by reference @           This is a longword passed by reference to your routine@           that you may use for any purpose, such as allocating a=           block of memory for keeping contextual information.   A           _______________________________________________________W             DESCRIPTIONO?           This routine is called by the Router before any callse<           to the EXPAND routine. You may use this routine to>           set up any context or perform any housekeeping tasks?           needed to prepare for the subsequent calls to EXPAND.   A                                                              2-13          (           Address Modification Interface           INIT        ;           Since your routines must be reentrant, you shouldr;           not use static storage for keeping track of statei?           information or other contextual information. Instead,_;           you should allocate a block of dynamic memory ands(           return its address in context.                                                                                 2-14 r       B                                     Address Modification InterfaceA                                                            EXPANDe        A           _______________________________________________________              EXPAND  (           Routine to expand a host name.  A           _______________________________________________________e             FORMAT  ,           EXPAND  context, hostname, expname  A           _______________________________________________________              RETURNSn           VMS Usage: cond_valuer(           type:      longword (unsigned)           access:    write only            mechanism: by valuee  ?           To indicate a successful expansion, return SS$_NORMALi:           or some other success status code. If you do not>           return a success status code, the caller will assume'           that expansion did not occur.O  A           _______________________________________________________i             ARGUMENTSa           context2             VMS Usage: context(           type:      longword (unsigned)           access:    modifyd!           mechanism: by referenceu<           This is the same value that was passed to the INIT           routine.             hostname              VMS Usage: char_string%           type:      character string            access:    read only2           mechanism:  by descriptor (fixed-length)'           The host name to be expanded.   A                                                              2-15_ _  _    (           Address Modification Interface           EXPAND                   expname_              VMS Usage: char_string%           type:      character string            access:    write only "           mechanism: by descriptor:           A string into which your routine should copy the>           expanded host name, if expansion was successful. You>           must use the STR$ string routines (such as STR$COPY_4           DX) to copy the string into this argument.  A           _______________________________________________________s             DESCRIPTION 7           This routine is called to perform a host name_9           expansion. The address of the context block you =           allocated in the INIT routine is passed in here forg;           any information you need to keep track of betweene:           calls. This routine may be called more than once5           between one pair of INIT and CLEANUP calls.o                                                   2-16 p       B                                     Address Modification InterfaceA                                                           CLEANUPa        A           ________________________________________________________             CLEANUP_  "           Context cleanup routine.  A           _______________________________________________________              FORMAT             CLEANUP  context  A           _______________________________________________________              RETURNSo           VMS Usage: cond_valuee(           type:      longword (unsigned)           access:    write only            mechanism: by valuee  >           This routine should return a status value indicating>           the success or failure of the cleanup operation. The:           caller may or may not ignore the returned value.  A           _______________________________________________________              ARGUMENTS            context              VMS Usage: context(           type:      longword (unsigned)           access:    modify !           mechanism: by reference ?           The address of the context block you allocated in the_           INIT routine._  A           _______________________________________________________e             DESCRIPTIONh;           This routine is called to clean up after a series_:           of EXPAND calls. You should clean up the context@           information and deallocate the context block allocated           by the INIT routine.  A                                                              2-17          (           Address Modification Interface           CLEANUPl        =           If you did not allocate a context block in the INITl>           routine, you must still have a CLEANUP routine, even+           if it just returns to the caller.u                                                                                     2-18         A                                    Address Modification InterfaceR        L           __________________________________________________________________      2.3    Name Conversionc  <           The local name conversion routines are used by the?           MX_MAILSHR VMS Mail interface to translate a usernamet>           into an alias and by the Router to translate aliases9           back into real usernames. This can be used, forr?           example, to map usernames into "real" names and vice-            versa.  8           A sample name conversion module is provided in           Appendix C.   @           The following pages describe the routines that must be5           provided for the name conversion interface.   9           In addition to the required CONVERT routine, ana@           optional FULL_CONVERT routine may be provided to allow@           for conversion of a username to a full RFC822 address,3           as opposed to just username substitution.o                                        A                                                              2-19p h  t    (           Address Modification Interface           INIT        A           _______________________________________________________              INIT  !           Initialization routine.e  A           ________________________________________________________             FORMAT             INIT  context_  A           ________________________________________________________             RETURNS            VMS Usage: cond_value (           type:      longword (unsigned)           access:    write only            mechanism: by value   @           The INIT routine must return a success status value in<           order for the other expansion routines to be used.  A           _______________________________________________________C             ARGUMENTS            contextM             VMS Usage: context(           type:      longword (unsigned)           access:    modify !           mechanism: by reference @           This is a longword passed by reference to your routine@           that you may use for any purpose, such as allocating a=           block of memory for keeping contextual information._  A           _______________________________________________________              DESCRIPTIONi?           This routine is called by the Router before any calls =           to the CONVERT routine. You may use this routine to >           set up any context or perform any housekeeping tasks@           needed to prepare for the subsequent calls to CONVERT.             2-20    E    B                                     Address Modification InterfaceA                                                              INITs        ;           Since your routines must be reentrant, you should ;           not use static storage for keeping track of state ?           information or other contextual information. Instead, ;           you should allocate a block of dynamic memory and (           return its address in context.                                                                      A                                                              2-21  o       (           Address Modification Interface           CONVERTE        A           ________________________________________________________             CONVERTR  ?           Routine to convert a username to an alias or an aliasr           to a username.  A           _______________________________________________________              FORMAT  1           CONVERT  context, code, inname, outname_  A           _______________________________________________________N             RETURNSg           VMS Usage: cond_value (           type:      longword (unsigned)           access:    write onlys           mechanism: by valued  =           On successful conversion, return SS$_NORMAL or someo;           other success status code. If you do not return au:           success status code, the caller will assume that"           expansion did not occur.  A           _______________________________________________________              ARGUMENTS            context              VMS Usage: context(           type:      longword (unsigned)           access:    modify !           mechanism: by reference <           This is the same value that was passed to the INIT           routine.             code  &           VMS Usage: longword_unsigned(           type:      longword (unsigned)           access:    read only!           mechanism: by reference              2-22         B                                     Address Modification InterfaceA                                                           CONVERT         >           This argument indicates what type of name conversion9           should occur. It will have one of the followingr           values:,  A           _______________________________________________________aA           Value______Meaning_____________________________________   :           1          Perform alias-to-username conversion.  A           2__________Perform_username-to-alias_conversion._______              inname              VMS Usage: char_string%           type:      character string            access:    read only2           mechanism:  by descriptor (fixed-length)#           The name to be converted.              outnamet              VMS Usage: char_string%           type:      character stringb           access:    write only_"           mechanism: by descriptor:           A string into which your routine should copy the;           result. This is only used if you return a successi           status code.  A           _______________________________________________________o             DESCRIPTION >           This routine is called to perform a name conversion.@           For alias-to-username translation, a string containing9           the potential alias is passed in inname. If thef>           conversion succeeds, the address returned in outname#           must be in RFC821 format:o  <                                          <username@hostname>  9           This format must be used even if the address is &           intended for the local host.  A                                                              2-23          (           Address Modification Interface           CONVERT         ;           For username-to-alias conversion, the username to_<           be converted is passed in inname. If no conversion9           is performed, return a non-success status code;_<           otherwise, provide a result in outname. The result>           should be only the local part of an address; no host<           name should be appended nor any punctuation added.  ?           This routine may be called more than once between ones)           pair of INIT and CLEANUP calls.s                                                                         2-24 e  e    B                                     Address Modification InterfaceA                                                           CLEANUP         A           _______________________________________________________              CLEANUP   "           Context cleanup routine.  A           _______________________________________________________              FORMAT             CLEANUP  context  A           _______________________________________________________              RETURNSa           VMS Usage: cond_value (           type:      longword (unsigned)           access:    write onlyn           mechanism: by valuen  >           This routine should return a status value indicating>           the success or failure of the cleanup operation. The:           caller may or may not ignore the returned value.  A           _______________________________________________________t             ARGUMENTSe           contexto             VMS Usage: context(           type:      longword (unsigned)           access:    modify !           mechanism: by reference ?           The address of the context block you allocated in the            INIT routine.   A           _______________________________________________________              DESCRIPTION_;           This routine is called to clean up after a series ;           of CONVERT calls. You should clean up the contexte@           information and deallocate the context block allocated           by the INIT routine.  A                                                              2-25t m  i    (           Address Modification Interface           CLEANUPe        =           If you did not allocate a context block in the INITh>           routine, you must still have a CLEANUP routine, even+           if it just returns to the caller.                                                                                      2-26 a  i    B                                     Address Modification InterfaceA                                                      FULL_CONVERT         A           _______________________________________________________              FULL_CONVERT  >           Routine to convert a username to an alias (as a full           RFC822 address).  A           _______________________________________________________l             FORMAT  6           FULL_CONVERT  context, code, inname, outname  A           _______________________________________________________E             RETURNSm           VMS Usage: cond_valuee(           type:      longword (unsigned)           access:    write only            mechanism: by valuew  =           On successful conversion, return SS$_NORMAL or some ;           other success status code. If you do not return a :           success status code, the caller will assume that#           conversion did not occur.o  A           _______________________________________________________              ARGUMENTS            contextn             VMS Usage: context(           type:      longword (unsigned)           access:    modify_!           mechanism: by reference_<           This is the same value that was passed to the INIT           routine.             code  &           VMS Usage: longword_unsigned(           type:      longword (unsigned)           access:    read only!           mechanism: by reference_  A                                                              2-27     e    (           Address Modification Interface           FULL_CONVERT        >           This argument indicates what type of name conversion:           should occur. Only the following value should be           accepted:   A           _______________________________________________________ A           Value______Meaning_____________________________________c  A           2__________Perform_username-to-alias_conversion._______   =           All other values for this argument are reserved for            future use.r             inname              VMS Usage: char_string%           type:      character string            access:    read only2           mechanism:  by descriptor (fixed-length)#           The name to be converted._             outnameI              VMS Usage: char_string%           type:      character string            access:    write onlyu"           mechanism: by descriptor:           A string into which your routine should copy the;           result. This is only used if you return a success            status code.  A           _______________________________________________________r             DESCRIPTIONe?           This routine is called to perform a username-to-full-e:           address conversion. The username to be converted=           is passed in inname. If no conversion is performed,a>           return a non-success status code. Unlike the CONVERT9           routine, the result you provide in outname on at:           successful conversion must be a full RFC822-type%           address (user@host format).   ?           This routine may be called more than once between onea?           pair of INIT and CLEANUP calls, and may be intermixedP           with CONVERT calls._             2-28 _  _                A           _______________________________________________________m  "    A      Address Rewriter Example      ;           This is an example of an address rewriter module, :           written in C by Andrew Greer and Hunter Goatley.  .           #define module_name ADDRESS_REWRITER%           #define module_ident "V1.0"c           /*
           !++            !u,           ! MODULE:       ADDRESS_REWRITER.C           !nC           ! ABSTRACT:     Example of site-installable rewrite rulesn           ! 9           ! AUTHOR: Andrew Greer <Andrew.Greer@vuw.ac.nz> 7           !  Hunter Goatley <goathunter@WKUVX1.WKU.EDU>sF           !  Copyright  1994, MadGoat Software.  All rights reserved.           !_           ! MODULE DESCRIPTION:            !US           !   This module contains routines for use by MX modules (specifically thewF           !   MX_ROUTER agent process) for rewriting RFC822 addresses.           !            !   To build it, use:            ! !           ! $ CC ADDRESS_REWRITER H           ! $ LINK/NOTRACE/SHARE ADDRESS_REWRITER.OBJ, SYS$INPUT:/OPTION'           ! SYS$SHARE:VAXCRTL.EXE/SHAREaB           ! UNIVERSAL=INIT,REWRITE_HEADER,REWRITE_ENVELOPE,CLEANUP           ! ^Z
           ! $            !            !   On AXP, use:           ! !           ! $ CC ADDRESS_REWRITER   A                                                               A-1  n  _    "           Address Rewriter Example          H           ! $ LINK/NOTRACE/SHARE ADDRESS_REWRITER.OBJ, SYS$INPUT:/OPTION           ! SYMBOL_VECTOR=(-#           !    INIT   = PROCEDURE,-s+           !    REWRITE_HEADER = PROCEDURE,- -           !    REWRITE_ENVELOPE = PROCEDURE,-t$           !    CLEANUP  = PROCEDURE)           ! ^Z
           ! $p           !iR           !   Then copy it to MX_EXE: and make it available to the Router with the!           !   following commands:            !u5           !       $ COPY ADDRESS_REWRITER.EXE MX_EXE:sW           !       $ DEFINE/SYSTEM/EXEC MX_SITE_ADDRESS_REWRITER MX_EXE:ADDRESS_REWRITERs$           !       $ MCP RESET ROUTER           !d)           !   Format of the file used is:.           !            ! USERNAME ALIAS           !eT           !   where the username has a maximum length of 12 characters and the alias/           !   has a maximum length of 33 chars.            !d           !   For example:           ! %           ! goathunter Hunter.Goatley            !aX           ! A lot of this is stolen directory from the NAME_CONVERSION routines provided5           ! as an example of CONVERT and FULL_CONVERT            !_U           ! Basically rewrite the FROM/RESENT_FROM headers to match the Email address W           ! format we use. Also rewrite the Envelope so that the mail gets delivered to 9           ! the username that matches that Email address.e           !rQ           ! E.g. mail from andrew@matai.vuw.ac.nz will get the From: rewritten as $           !   Andrew.Greer@vuw.ac.nzM           ! And mail coming into Andrew.Greer@vuw.ac.nz will get delivered too"           ! andrew@matai.vuw.ac.nz           !e4           !  The following logicals must be defined:  
           A-2_ _  _    A                                          Address Rewriter Exampleg                     !y@           ! MX_NODE_NAME  - The node name (e.g., WKUVX1.WKU.EDU)F           ! MX_SITE_GENERIC  - The generic node name for outgoing mail'           !      (for example, WKU.EDU)u<           ! MX_SITE_ALIASES_TMP - The name of the alias file           !e
           !--E           */  2           #if defined(__DECC) || defined(__DECCXX)1           #pragma module module_name module_identp           #else *           #module module_name module_ident           #endif             #include <ctype.h>           #include <descrip.h>           #include <lnmdef.h>2           #include <stdio.h>           #include <string.h>            #include <ssdef.h>#           #include <lib$routines.h>x#           #include <str$routines.h>n             #include "mx_hdr.h"r  -           #define MIN(a,b)    (a < b ? a : b)   *           static $DESCRIPTOR(lbrack, "<");*           static $DESCRIPTOR(rbrack, ">");*           static $DESCRIPTOR(atsign, "@");             struct context {4            struct dsc$descriptor localnode, generic;            int num_names;_            struct {                 char user[13];                 char alias[34];                char ret[34];                   } names[100];f           };  H           /* init_dynamic_descriptor - initializes dynamic descriptor */  A                                                               A-3     m    "           Address Rewriter Example          G           static void init_dynamic_descriptor(struct dsc$descriptor *d)e           { 1                   d->dsc$b_dtype = DSC$K_DTYPE_T;c1                   d->dsc$b_class = DSC$K_CLASS_D;f&                   d->dsc$w_length = 0;*                   d->dsc$a_pointer = NULL;           }   F           /* init_static_descriptor - initializes static descriptor */  T           static void init_static_descriptor(struct dsc$descriptor *d, char *string)           {i1                   d->dsc$b_dtype = DSC$K_DTYPE_T; 1                   d->dsc$b_class = DSC$K_CLASS_S; 3                   d->dsc$w_length = strlen(string);i,                   d->dsc$a_pointer = string;           }_  M           int check_name(struct context **ctx, struct dsc$descriptor *inname,a8                          struct dsc$descriptor *outname)           {_             int idx;             char user[64];)             struct dsc$descriptor tmpdsc;_  H             strncpy (user, inname->dsc$a_pointer, inname->dsc$w_length);/             user [inname->dsc$w_length] = '\0';r  3             for(idx=0; idx < strlen(user); idx++) { %               if (isupper(user[idx])) /                 user[idx] = tolower(user[idx]);n
             }e  8             for(idx=0; idx < ((*ctx)->num_names); idx++)              {;               if (!strcmp ((*ctx)->names[idx].alias, user))T                {S                 init_static_descriptor(&tmpdsc, (char *) &(*ctx)->names[idx].user); .                 str$copy_dx(outname, &tmpdsc);"                 return SS$_NORMAL;                }              } /* for */  
           A-4m u  f    A                                          Address Rewriter Examplep                       return 0;              } /* check_name */  L           int get_alias(struct context **ctx, struct dsc$descriptor *inname,8                          struct dsc$descriptor *outname)           {r             int idx;             char user[64];)             struct dsc$descriptor tmpdsc;g  G             strncpy(user, inname->dsc$a_pointer, inname->dsc$w_length); .             user[inname->dsc$w_length] = '\0';  1             for(idx=0; idx < strlen(user); idx++) %               if (isupper(user[idx])) /                 user[idx] = tolower(user[idx]);r  6             for(idx=0; idx < (*ctx)->num_names; idx++)              {9               if (!strcmp((*ctx)->names[idx].user, user))r                {R                 init_static_descriptor(&tmpdsc, (char *) &(*ctx)->names[idx].ret);.                 str$copy_dx(outname, &tmpdsc);"                 return SS$_NORMAL;                }              } /* for */               return 0;              } /* get_alias */                   A                                                               A-5_ _  _    "           Address Rewriter Example                     /*
           !++            !n"           ! ROUTINE NAME:     INIT           ! #           ! FUNCTIONAL DESCRIPTION:_           !_V           !   Allocates and initializes context block for subsequent name conversions.           !uO           ! RETURNS:      cond_value, longword (unsigned), write only, by value            !N           ! PROTOTYPE:           !            !   INIT  ctxptr           ! H           ! ctxptr:   pointer, longword (unsigned), modify, by reference           !s#           ! IMPLICIT INPUTS:  None.s           ! #           ! IMPLICIT OUTPUTS: None.o           !u           ! COMPLETION CODES:,           !i?           !   SS$_NORMAL:         normal successful completion._           !_           ! SIDE EFFECTS:_           !            !   None. 
           !--t           */           unsigned int'           init (struct context **ctx) {   -               int ctxsize, status, idx, idx2;:8               $DESCRIPTOR(mx_node_name, "MX_NODE_NAME");>               $DESCRIPTOR(mx_site_generic, "MX_SITE_GENERIC");B               $DESCRIPTOR(mx_site_aliases, "MX_SITE_ALIASES_TMP");"               char alias_file[64];-               struct dsc$descriptor aliasdsc;                FILE *fd;   
           A-6          A                                          Address Rewriter Example           /               ctxsize = sizeof(struct context);a2               status = lib$get_vm (&ctxsize, ctx);6               if (status != SS$_NORMAL) return status;  :               init_dynamic_descriptor(&(*ctx)->localnode);8               init_dynamic_descriptor(&(*ctx)->generic);1               init_dynamic_descriptor(&aliasdsc);   M               status = lib$sys_trnlog (&mx_node_name, 0, &(*ctx)->localnode);e6               if (status != SS$_NORMAL) return status;  N               status = lib$sys_trnlog (&mx_site_generic, 0, &(*ctx)->generic);6               if (status != SS$_NORMAL) return status;  G               status = lib$sys_trnlog (&mx_site_aliases, 0, &aliasdsc);n6               if (status != SS$_NORMAL) return status;  Q               strncpy(alias_file, aliasdsc.dsc$a_pointer, aliasdsc.dsc$w_length);e7               alias_file[aliasdsc.dsc$w_length] = '\0';l  .               if (fd = fopen(alias_file, "r"))                {(                 (*ctx)->num_names = 100;;                 for (idx=0; idx < (*ctx)->num_names; idx++)                   {?                   fscanf(fd, "%s %s", &(*ctx)->names[idx].user, %             &(*ctx)->names[idx].ret);t  B            /* Convert the alias to lowercase for matching later */G            for(idx2=0; idx2 < strlen((*ctx)->names[idx].ret); idx2++) {t1                  (*ctx)->names[idx].alias[idx2] = 3              tolower((*ctx)->names[idx].ret[idx2]);>              }                     if (feof(fd))a                    {,                     (*ctx)->num_names = idx;                     break;                    }                  } /* for */                }               else                 return 0;   A                                                               A-7e d       "           Address Rewriter Example                          return SS$_NORMAL;           }  /* init */e             /*
           !++a           !t,           ! ROUTINE NAME:     REWRITE_HEADER           ! #           ! FUNCTIONAL DESCRIPTION:e           !cO           ! RETURNS:      cond_value, longword (unsigned), write only, by values           !a           ! PROTOTYPE:           ! 8           !   REWRITE_HEADER ctxptr, instr, outstr, code           ! H           ! ctxptr:   pointer, longword (unsigned), modify, by referenceU           ! instr:    char_string, character string, read only, by descriptor (fixed) N           ! outstr:   char_string, character string, write only, by descriptorT           ! code:     word_unsigned, word (unsigned), read only, by value/reference?           !_#           ! IMPLICIT INPUTS:  None.            ! #           ! IMPLICIT OUTPUTS: None._           !_           ! COMPLETION CODES:            !N?           !   SS$_NORMAL:         normal successful completion.r           !            ! SIDE EFFECTS:o           !            !   None.n
           !--h           */           unsigned int/           rewrite_header( struct context **ctx, 7                           struct dsc$descriptor *inadr,o8                           struct dsc$descriptor *outadr,/                           unsigned short code )            {e  
           A-8x         A                                          Address Rewriter Example                        int   rc,                    len,                   pos,                   start_pos,                   end_pos;@             struct dsc$descriptor localdsc, domdsc, newlocaldsc;  2             init_dynamic_descriptor(&newlocaldsc);/             init_dynamic_descriptor(&localdsc);e-             init_dynamic_descriptor(&domdsc);n  /             pos = str$position(inadr, &atsign);a             if (pos > 0)              {               start_pos = 1;                end_pos = pos - 1;C               str$len_extr(&localdsc, inadr, &start_pos, &end_pos);n"               start_pos = pos + 1;2               end_pos = inadr->dsc$w_length - pos;A               str$len_extr(&domdsc, inadr, &start_pos, &end_pos);u                }               switch (code)               {Z               /* Possible headers that could be rewritten (from [MX.ROUTER]PROCESS.B32) */8               case MX_K_HDR_FROM:             /* From */?               case MX_K_HDR_R_FROM:           /* Resent From */ O                       rc = str$case_blind_compare(&domdsc, &(*ctx)->localnode);_B                       if (get_alias(ctx, &localdsc, &newlocaldsc))                        {T                         str$concat(outadr, &newlocaldsc, &atsign, &(*ctx)->generic);*                         return SS$_NORMAL;                        }                       break;6               case MX_K_HDR_TO:               /* To */=               case MX_K_HDR_R_TO:             /* Resent To */u6               case MX_K_HDR_CC:               /* CC */=               case MX_K_HDR_R_CC:             /* Resent CC */ 7               case MX_K_HDR_BCC:              /* BCC */ >               case MX_K_HDR_R_BCC:            /* Resent BCC */<               case MX_K_HDR_REPLY_TO:         /* Reply To */  A                                                               A-9_ _  _    "           Address Rewriter Example          :               case MX_K_HDR_SENDER:           /* Sender */A               case MX_K_HDR_R_SENDER:         /* Resent Sender */ C               case MX_K_HDR_R_REPLY_TO:       /* Resent Reply To */                default:                       break;              }               return 0;n            } /* rewrite_header */             /*
           !++s           !y.           ! ROUTINE NAME:     REWRITE_ENVELOPE           ! #           ! FUNCTIONAL DESCRIPTION:            !eO           ! RETURNS:      cond_value, longword (unsigned), write only, by value            !a           ! PROTOTYPE:           !o4           !   REWRITE_ENVELOPE ctxptr, inadr, outadr           !eH           ! ctxptr:   pointer, longword (unsigned), modify, by referenceU           ! instr:    char_string, character string, read only, by descriptor (fixed) N           ! outstr:   char_string, character string, write only, by descriptor           !a#           ! IMPLICIT INPUTS:  None.o           ! #           ! IMPLICIT OUTPUTS: None.            !s           ! COMPLETION CODES:e           !t?           !   SS$_NORMAL:         normal successful completion.            !            ! SIDE EFFECTS:h           !c           !   None. 
           !--            */             A-10         A                                          Address Rewriter Example                      unsigned int1           rewrite_envelope( struct context **ctx,t9                             struct dsc$descriptor *inadr,c;                             struct dsc$descriptor *outadr )_           {_             int   rc,                    len,                   pos,                   start_pos,                   end_pos;@             struct dsc$descriptor localdsc, domdsc, newlocaldsc;  2             init_dynamic_descriptor(&newlocaldsc);/             init_dynamic_descriptor(&localdsc);E-             init_dynamic_descriptor(&domdsc);n  /             pos = str$position(inadr, &atsign);t             if (pos > 0)+              { /* Remove the "<" and ">" */ ;               start_pos = str$position(inadr, &lbrack) + 1;                 end_pos = pos - 2;C               str$len_extr(&localdsc, inadr, &start_pos, &end_pos); "               start_pos = pos + 1;5               end_pos = str$position(inadr, &rbrack);_               if (end_pos > 0).                 end_pos = end_pos - start_pos;               else4                 end_pos = inadr->dsc$w_length - pos;A               str$len_extr(&domdsc, inadr, &start_pos, &end_pos);T              }  C             rc = str$case_blind_compare(&domdsc, &(*ctx)->generic);              if (rc != 0)               return 0;   9             if (check_name(ctx, &localdsc, &newlocaldsc))               {T               str$concat(outadr, &lbrack, &newlocaldsc, &atsign, &(*ctx)->localnode,                       &rbrack);1                return SS$_NORMAL;              }  A                                                              A-11S P  N    "           Address Rewriter Example                       return 0;M  "           } /* rewrite_envelope */             /*           */           unsigned int*           cleanup (struct context **ctx) {  "               int ctxsize, status;  8               status = str$free1_dx(&(*ctx)->localnode);6               if (status != SS$_NORMAL) return status;  6               status = str$free1_dx(&(*ctx)->generic);6               if (status != SS$_NORMAL) return status;  /               ctxsize = sizeof(struct context);:3               status = lib$free_vm (&ctxsize, ctx); 6               if (status != SS$_NORMAL) return status;                 *ctx = NULL;                return SS$_NORMAL;           }  /* cleanup */             #ifdef MAINE           int main(void)           {Y8              $DESCRIPTOR(x, "goathunter@NUKE2.WKU.EDU");>              $DESCRIPTOR(y, "<Hunter.goatley@NUKE2.WKU.EDU>");3              struct dsc$descriptor outstr, outstr2;R!              struct context *ctx;               int status;  /              init_dynamic_descriptor (&outstr);l0              init_dynamic_descriptor (&outstr2);                init (&ctx); H              status = rewrite_header (&ctx, &x, &outstr, MX_K_HDR_FROM);&              lib$put_output (&outstr);;              status = rewrite_envelope (&ctx, &y, &outstr);T&              lib$put_output (&outstr);             A-12         A                                          Address Rewriter Example                          return(SS$_NORMAL);           }e           #endif                                                                          A                                                              A-13                      A           _______________________________________________________v  "    B      Domain Expansion Example      >           This is an example of a domain name expansion module8           for use with CMU-Tek TCP/IP, written in BLISS.  $           %TITLE 'DOM_EXPANSION_CMU'W           MODULE DOM_EXPANSION_CMU (IDENT='V1.0', ADDRESSING_MODE (EXTERNAL=GENERAL)) =t           BEGIN 
           !++ &           ! FACILITY:      MX Examples           !gM           ! ABSTRACT:      Example of a domain name expander for use with MX.n4           !             For use with CMU-Tek TCP/IP.           !            ! MODULE DESCRIPTION:            ! J           !   This module contains the routines necessary for implementingK           !   a domain name expander for use by the MX Router agent.  These I           !   routines can be used to eliminate SMTP mail loops when mailXI           !   is addressed using an abbreviated host name, without havingn9           !   to DEFINE PATH LOCAL for each abbreviation.            !AF           !   To use this module: modify it as needed, then compile it,           !   and link it with the commands:           !X(           !    $ BLISS DOM_EXPANSION_CMUR           !    $ LINK/SHARE=DOM_EXPANSION/NOTRACE DOM_EXPANSION_CMU,SYS$INPUT:/OPT0           !        UNIVERSAL=INIT,EXPAND,CLEANUP           !        <ctrl/Z>            !<M           !   Then copy it to MX_EXE and make it available to the Router with            !   the commands:            !</           !    $ COPY DOM_EXPANSION.EXE MX_EXE:sN           !    $ DEFINE/SYSTEM/EXEC MX_SITE_DOM_EXPANSION MX_EXE:DOM_EXPANSION  A                                                               B-1t $  C    "           Domain Expansion Example          !           !    $ MCP RESET ROUTERc           ! B           !   (You need a suitably privileged account to do this.)           ! %           ! AUTHOR:        M. MadisonsO           !      Copyright  1993,1994, MadGoat Software.  All Rights Reserved.            !e)           ! CREATION DATE:    07-DEC-1990_           !i!           ! MODIFICATION HISTORY:o           ! :           !   07-DEC-1990 V1.0 Madison     Initial coding.
           !-- ,               LIBRARY 'SYS$LIBRARY:STARLET';,               LIBRARY 'SYS$LIBRARY:NETWORK';                 EXTERNAL ROUTINE@                STR$CONCAT, STR$COPY_R, STR$FREE1_DX, LIB$GET_VM,                LIB$FREE_VM;C                 LITERAL                  CTX_S_CTXDEF = 2;                 FIELDi                CTX_FIELDS =                 SET,                    CTX_W_CHAN =   [0,0,16,0]                TES;t                 MACROcF                CTXDEF = BLOCK [CTX_S_CTXDEF,BYTE] FIELD (CTX_FIELDS)%;                  
           B-2Y T       A                                          Domain Expansion Example-                     %SBTTL 'INIT' )           GLOBAL ROUTINE INIT (CTX_A_A) =            BEGIN 
           !++c#           ! FUNCTIONAL DESCRIPTION:s           !*N           !   Called by the Router to initialize the module.  Could be used toL           !   allocate any storage that will be needed by the EXPAND routineN           !   (these routines must be reentrant, so OWN storage is right out).           ! L           ! RETURNS:   cond_value, longword (unsigned), write only, by value           !r           ! PROTOTYPE:           !e           !   INIT  ctxptr           ! H           ! ctxptr:   pointer, longword (unsigned), modify, by reference           !e#           ! IMPLICIT INPUTS:  None.            !m#           ! IMPLICIT OUTPUTS: None.            !            ! COMPLETION CODES:_           !m<           !   SS$_NORMAL:      normal successful completion.           !n           ! SIDE EFFECTS:            !$           !   None. 
           !--                BIND.                CTX  = .CTX_A_A   : REF CTXDEF;                 LOCALe                STATUS;          A                                                               B-3n e  l    "           Domain Expansion Example          =               STATUS = LIB$GET_VM (%REF (CTX_S_CTXDEF), CTX);)               IF .STATUS THEN                BEGINrL                STATUS = $ASSIGN (DEVNAM=%ASCID'IP0', CHAN=CTX [CTX_W_CHAN]);J                IF NOT .STATUS THEN LIB$FREE_VM (%REF (CTX_S_CTXDEF), CTX);               END;                 .STATUSd             END; ! INITi             %SBTTL 'EXPAND'p>           GLOBAL ROUTINE EXPAND (CTX_A_A, INSTR_A, OUTSTR_A) =           BEGIN 
           !++<#           ! FUNCTIONAL DESCRIPTION:            ! H           !   This routine is called to perform a domain name expansion.           !nM           !   INSTR can be assumed to be a DTYPE_T, CLASS_S string descriptor M           !   (or compatible).  You must use STR$ routines to copy the result            !   to OUTSTR!           ! L           ! RETURNS:   cond_value, longword (unsigned), write only, by value           !            ! PROTOTYPE:           ! +           !   EXPAND  ctxptr, instr, outstrr           ! H           ! ctxptr:   pointer, longword (unsigned), modify, by referenceM           ! instr:    char_string, character string, read only, by descriptor N           ! outstr:   char_string, character string, write only, by descriptor           ! #           ! IMPLICIT INPUTS:  None.v           !d#           ! IMPLICIT OUTPUTS: None.            !            ! COMPLETION CODES:            ! <           !   SS$_NORMAL:      normal successful completion.           !s  
           B-4y f  n    A                                          Domain Expansion Example                      ! SIDE EFFECTS:            !            !   None.C
           !--                BIND2                CTX  = .CTX_A_A       : REF CTXDEF,/                CHN  = CTX [CTX_W_CHAN]  : WORD, A                INSTR = .INSTR_A       : BLOCK [DSC$K_S_BLN,BYTE], B                OUTSTR = .OUTSTR_A      : BLOCK [DSC$K_S_BLN,BYTE];                 LOCAL,*                GHBLK : GTHST_NMLOOK_BLOCK,#                IOSB : NETWORK_IOSB, /                STR  : BLOCK [DSC$K_S_BLN,BYTE],"                STATUS;  "               $INIT_DYNDESC (STR);A               STR$CONCAT (STR, INSTR, %ASCID %STRING(%CHAR (0)));dM               STATUS = NET$GTHST (BUFADRS=GHBLK, BUFSIZE=%ALLOCATION (GHBLK), L                GTHFUNCT=GTH_NAMADR, GTHP1=.STR [DSC$A_POINTER], IOCHAN=.CHN,                IO$SB=IOSB);eN               IF .STATUS THEN STATUS = (IF .IOSB [VMS_CODE] EQL SS$_ABORT THENH                               .IOSB [NET_XERROR] ELSE .IOSB [VMS_CODE]);                IF NOT .STATUS ANDV                    CH$RCHAR (.INSTR [DSC$A_POINTER]+.INSTR [DSC$W_LENGTH]-1) NEQ %C'.'               THEN               BEGIN H                STR$CONCAT (STR, INSTR, %ASCID %STRING ('.', %CHAR (0)));N                STATUS = NET$GTHST (BUFADRS=GHBLK, BUFSIZE=%ALLOCATION (GHBLK),P                    GTHFUNCT=GTH_NAMADR, GTHP1=.STR [DSC$A_POINTER], IOCHAN=.CHN,                    IO$SB=IOSB); O                IF .STATUS THEN STATUS = (IF .IOSB [VMS_CODE] EQL SS$_ABORT THENfH                               .IOSB [NET_XERROR] ELSE .IOSB [VMS_CODE]);               END;!               STR$FREE1_DX (STR); P               IF .STATUS THEN STATUS = STR$COPY_R (OUTSTR, GHBLK [GH$NL_NAMLEN],8                                   GHBLK [GH$NL_NAMSTR]);               .STATUS              END; ! EXPAND   A                                                               B-5  s  f    "           Domain Expansion Example                     %SBTTL 'CLEANUP',           GLOBAL ROUTINE CLEANUP (CTX_A_A) =           BEGIN 
           !++ #           ! FUNCTIONAL DESCRIPTION:-           !eI           !   Called by the Router to clean up any context info set up by            !   INIT.n           ![L           ! RETURNS:   cond_value, longword (unsigned), write only, by value           !            ! PROTOTYPE:           !            !   CLEANUP  ctxptr            ! H           ! ctxptr:   pointer, longword (unsigned), modify, by reference           ! #           ! IMPLICIT INPUTS:  None.            ! #           ! IMPLICIT OUTPUTS: None.            !            ! COMPLETION CODES:            ! <           !   SS$_NORMAL:      normal successful completion.           !            ! SIDE EFFECTS:            !A           !   None.R
           !--                BIND.                CTX  = .CTX_A_A   : REF CTXDEF;  /               $DASSGN (CHAN=.CTX [CTX_W_CHAN]);l5               LIB$FREE_VM (%REF (CTX_S_CTXDEF), CTX);                CTX = 0;                 SS$_NORMAL             END; ! CLEANUP  
           ENDp           ELUDOM  
           B-6  i                   A           _______________________________________________________s  !    C      Name Conversion Example       @           This is an example of a simple name conversion module,           written in C.w             /*
           !++a           !?*           ! MODULE:        NAME_CONVERSION           ! &           ! FACILITY:      MX examples           ! K           ! ABSTRACT:      Example of site-installable nickname conversion.L           !m           ! MODULE DESCRIPTION:            ! P           !   This module contains routines for use by MX modules (specifically,S           !   the MX_MAILSHR interface to VMS Mail and the MX_ROUTER agent process) U           !   for translating between actual VMS usernames and site-specific aliases.c           !oO           !   This module contains a fairly primitive lookup table to implement            !   the translation.           ! T           !   To use this module: MODIFY IT AS NEEDED FOR YOUR SITE, then compile it,           !   and link it with the commands:           !r&           !       $ cc name_conversionB           !    $ link/share/notrace name_conversion,sys$input:/opt*           !        sys$share:vaxcrtl/share>           !        universal=init,convert,full_convert,cleanup           !        <ctrl/Z>m           ! M           !   Then copy it to MX_EXE and make it available with the commands:            ! A           !    $ copy name_conversion.exe mx_exe:/protection=w:re   A                                                               C-1     r    !           Name Conversion Example           H           !    $ install create mx_exe:name_conversion/share/open/headerR           !    $ define/system/exec mx_site_name_conversion mx_exe:name_conversionE           !    $ mcp reset router  ! to force Router to load the codeO           ! B           !   (You need a suitably privileged account to do this.)           !_%           ! AUTHOR:        M. Madison O           !      Copyright  1993,1994, MadGoat Software.  All Rights Reserved._           ! O           !   THIS SOFTWARE IS PROVIDE "AS IS".  NEITHER THE AUTHOR NOR MadGoat S           !   MAKE ANY GUARANTEES REGARDING THE SUITABILITY, RELIABILITY, SECURITY, S           !   USEFULNESS, OR PERFORMANCE OF THIS SOFTWARE.  >>USE AT YOUR OWN RISK.            ! )           ! CREATION DATE:    03-DEC-1990            ! !           ! MODIFICATION HISTORY:            !s:           !   03-DEC-1990 V1.0 Madison     Initial coding.>           !   11-MAR-1992 V1.1 Madison     Update for MX V3.1.T           !   15-MAY-1992 V1.2 Madison     Correct "restat" typo.  Add full_convert.
           !--            */             #include descrip           #include string            #include stdio           #include ssdef           #include str$routines_           #include lib$routinese  '           #define NICK_TO_ADDRESS     1e'           #define USERNAME_TO_NICK    2   $           #define NAME_COUNT       2  6           static char *user [] = {"SMYTHE", "SYSTEM"};@           static char *nick [] = {"J.Smythe", "System.Manager"};  $           #define FULL_COUNT       2  =           static char *full_user[] = {"MADISON", "SHANDY_P"}; 8           static char *full_nick[] = {"madison@tgv.com",Q                           "Peter_Shandy@portulaca-purple-passion.balaclava.edu"};   
           C-2          A                                           Name Conversion Example                      struct context {.               struct dsc$descriptor localnode;               };             /*
           !++p           !,"           ! ROUTINE NAME:     INIT           ! #           ! FUNCTIONAL DESCRIPTION:r           ! V           !   Allocates and initializes context block for subsequent name conversions.           !cL           ! RETURNS:   cond_value, longword (unsigned), write only, by value           !C           ! PROTOTYPE:           !            !   INIT  ctxptr           ! H           ! ctxptr:   pointer, longword (unsigned), modify, by reference           ! #           ! IMPLICIT INPUTS:  None.            !e#           ! IMPLICIT OUTPUTS: None.            !            ! COMPLETION CODES:            ! <           !   SS$_NORMAL:      normal successful completion.           !            ! SIDE EFFECTS:c           !t           !   None. 
           !--d           */           unsigned int'           init (struct context **ctx) {d                 int ctxsize;7               $DESCRIPTOR(mx_node_name,"MX_NODE_NAME");o  A                                                               C-3     u    !           Name Conversion Examplel          /               ctxsize = sizeof(struct context);c)               lib$get_vm (&ctxsize, ctx);c<               (*ctx)->localnode.dsc$b_dtype = DSC$K_DTYPE_T;<               (*ctx)->localnode.dsc$b_class = DSC$K_CLASS_D;1               (*ctx)->localnode.dsc$w_length = 0;"5               (*ctx)->localnode.dsc$a_pointer = NULL;rD               lib$sys_trnlog (&mx_node_name, 0, &(*ctx)->localnode);                return SS$_NORMAL;           }  /* init */              /*
           !++            ! %           ! ROUTINE NAME:     CONVERT            !_#           ! FUNCTIONAL DESCRIPTION:n           !_J           !   Converts username -> nickname or nickname -> RFC821-address.           ! O           !   NB: You MUST use STR$ routines to copy result to OUTSTR parameter 0           !       to ensure proper operation!!!!           ! R           !       You _may_ safely assume that INSTR is compatible with a DTYPE_T,A           !    CLASS_S (standard fixed-length) string descriptor.c           !bL           ! RETURNS:   cond_value, longword (unsigned), write only, by value           !            ! PROTOTYPE:           ! 2           !   CONVERT  ctxptr, code, instr, outstr           ! H           ! ctxptr:   pointer, longword (unsigned), modify, by referenceU           ! code:     longword_unsigned, longword (unsigned), read only, by reference U           ! instr:    char_string, character string, read only, by descriptor (fixed)sN           ! outstr:   char_string, character string, write only, by descriptor           !t#           ! IMPLICIT INPUTS:  None.            ! #           ! IMPLICIT OUTPUTS: None.c           !   
           C-4  _  M    A                                           Name Conversion Example                      ! COMPLETION CODES:i           ! <           !   SS$_NORMAL:      normal successful completion.           !=           ! SIDE EFFECTS:n           !            !   None.u
           !--            */           unsigned intQ           convert (struct context **ctx, int *code, struct dsc$descriptor *instr, 3                    struct dsc$descriptor *outstr) {;  .               struct dsc$descriptor tmp, tmp2;               size_t count;t                int i, j, retstat;'               $DESCRIPTOR(lbrack, "<");r'               $DESCRIPTOR(rbrack, ">");e'               $DESCRIPTOR(atsign, "@");   ,               count = instr -> dsc$w_length;.               tmp.dsc$b_dtype = DSC$K_DTYPE_T;.               tmp.dsc$b_class = DSC$K_CLASS_D;#               tmp.dsc$w_length = 0;)'               tmp.dsc$a_pointer = NULL;;  /               tmp2.dsc$b_dtype = DSC$K_DTYPE_T; /               tmp2.dsc$b_class = DSC$K_CLASS_S;                         A                                                               C-5          !           Name Conversion Example                          switch (*code) {           /*
           !++1#           !  Local alias -> address            !_R           !   This code should return a status of SS$_NORMAL if an alias is found,           !   0 otherwise.           !aR           !   If an alias is found, the resulting string MUST BE IN RFC821 format:           !T)           !                   <user@host>D           !CN           !   >>>>>> EVEN IF THE ADDRESS IS FOR THE LOCAL HOST (so you have toL           !   look up MX_NODE_NAME and tack it on after the translated name,@           !   if you're just doing a local-host user directory).
           !--            */$                case NICK_TO_ADDRESS:                    retstat = 0; ,                    str$copy_dx(&tmp, instr);5                    for (i = 0; i < NAME_COUNT; i++) { 8                     tmp2.dsc$w_length = strlen(nick[i]);1                     tmp2.dsc$a_pointer = nick[i];mD                     if (str$case_blind_compare(instr, &tmp2) == 0) {,                         j = strlen(user[i]);6                         str$copy_r(&tmp, &j, user[i]);B                         str$concat(outstr, &lbrack, &tmp, &atsign,6                          &(*ctx)->localnode, &rbrack);-                         retstat = SS$_NORMAL;E                         break;                     }/                    }                    break;               
           C-6A C  N    A                                           Name Conversion Example                      /*
           !++h           !   Username -> Alias            ! J           !   Return sucess status ONLY if you are actually converting theQ           !   username to an alias!  Otherwise, return a non-success status code.            ! K           !   For compatibility with the name_conversion interface prior toEL           !   MX V3.1, you should copy the input string to the output string3           !   when you return a non-success status.s           ! 
           !--            */%                case USERNAME_TO_NICK:t                    retstat = 0;AQ                    str$copy_dx(outstr, instr);   /* for pre-V3.1 compatibility */ 5                    for (i = 0; i < NAME_COUNT; i++) { 8                     tmp2.dsc$w_length = strlen(user[i]);1                     tmp2.dsc$a_pointer = user[i];BD                     if (str$case_blind_compare(instr, &tmp2) == 0) {,                         j = strlen(nick[i]);8                         str$copy_r(outstr, &j, nick[i]);-                         retstat = SS$_NORMAL;                          break;                     }                     }                    break;                }C                 return retstat;              }  /* convert */                A                                                               C-7          !           Name Conversion Example                      /*
           !++i           !x*           ! ROUTINE NAME:     FULL_CONVERT           !B#           ! FUNCTIONAL DESCRIPTION:            ! L           !   Converts username -> alias address (full address substitution)           ! J           !   Unlike the CONVERT routine, FULL_CONVERT converts a usernameN           !   to a complete RFC822-type address.  You must be running MX V3.1C+           !   or later to use this feature.t           ! O           !   NB: You MUST use STR$ routines to copy result to OUTSTR parameterl0           !       to ensure proper operation!!!!           ! R           !       You _may_ safely assume that INSTR is compatible with a DTYPE_T,A           !    CLASS_S (standard fixed-length) string descriptor.            ! L           ! RETURNS:   cond_value, longword (unsigned), write only, by value           !            ! PROTOTYPE:           !L7           !   FULL_CONVERT  ctxptr, code, instr, outstr            !FH           ! ctxptr:   pointer, longword (unsigned), modify, by referenceU           ! code:     longword_unsigned, longword (unsigned), read only, by reference U           ! instr:    char_string, character string, read only, by descriptor (fixed)-N           ! outstr:   char_string, character string, write only, by descriptor           !V#           ! IMPLICIT INPUTS:  None.            !T#           ! IMPLICIT OUTPUTS: None.            !A           ! COMPLETION CODES:0           !C<           !   SS$_NORMAL:      normal successful completion.           !T           ! SIDE EFFECTS:            !   
           C-8          A                                           Name Conversion ExampleE                     !   None.T
           !--            */           unsigned intV           full_convert (struct context **ctx, int *code, struct dsc$descriptor *instr,3                    struct dsc$descriptor *outstr) {   .               struct dsc$descriptor tmp, tmp2;               size_t count;                 int i, j, retstat;'               $DESCRIPTOR(lbrack, "<"); '               $DESCRIPTOR(rbrack, ">"); '               $DESCRIPTOR(atsign, "@");)  ,               count = instr -> dsc$w_length;.               tmp.dsc$b_dtype = DSC$K_DTYPE_T;.               tmp.dsc$b_class = DSC$K_CLASS_D;#               tmp.dsc$w_length = 0;g'               tmp.dsc$a_pointer = NULL;n  /               tmp2.dsc$b_dtype = DSC$K_DTYPE_T;y/               tmp2.dsc$b_class = DSC$K_CLASS_S;c  6               if (*code != USERNAME_TO_NICK) return 0;                                A                                                               C-9          !           Name Conversion Example                      /*
           !++ 9           !   Username -> alias (full address conversion)            ! J           !   Return sucess status ONLY if you are actually converting theQ           !   username to an alias!  Otherwise, return a non-success status code.            ! 
           !--X           */               retstat = 0;0               for (i = 0; i < FULL_COUNT; i++) {8                tmp2.dsc$w_length = strlen(full_user[i]);1                tmp2.dsc$a_pointer = full_user[i];S?                if (str$case_blind_compare(instr, &tmp2) == 0) {G,                    j = strlen(full_nick[i]);8                    str$copy_r(outstr, &j, full_nick[i]);(                    retstat = SS$_NORMAL;                    break;                     }                }                 return retstat;              }  /* full_convert */L                                           C-10 H  C    A                                           Name Conversion ExampleI                     /*
           !++T           !(%           ! ROUTINE NAME:     CLEANUP            ! #           ! FUNCTIONAL DESCRIPTION:S           !CB           !   Deallocates context block allocated by init routine.           !CL           ! RETURNS:   cond_value, longword (unsigned), write only, by value           !            ! PROTOTYPE:           !%           !   CLEANUP  ctxptr;           ! H           ! ctxptr:   pointer, longword (unsigned), modify, by reference           !G#           ! IMPLICIT INPUTS:  None._           !H#           ! IMPLICIT OUTPUTS: None.S           !            ! COMPLETION CODES:            !S<           !   SS$_NORMAL:      normal successful completion.           !]           ! SIDE EFFECTS:            !N           !   None.$
           !--            */           unsigned int*           cleanup (struct context **ctx) {                 int ctxsize;  /               str$free1_dx(&(*ctx)->localnode); /               ctxsize = sizeof(struct context); *               lib$free_vm (&ctxsize, ctx);               *ctx = NULL;                return SS$_NORMAL;           }  /* cleanup */  A                                                              C-11 