/* REXX: dummy SMTP client testing RCPT TO: or other */ /* addresses at a given domain. The script uses `nslookup -q=mx` */ /* to determine the favourite MX of the domain. */ /* Usage : RXHELO address */ /* Example: RXHELO abuse@domain.example */ /* Version 0.2 source */ /* Prerequisites: */ /* 1 - OS/2 RxSock.dll available from IBM (REXX IPv4 sockets API) */ /* 2 - OS/2 nslookup.exe with a working DNS server configured in */ /* %ETC%\resolv2 - on my system the file D:\MPTN\ETC\RESOLV2, */ /* for another tool modify procedure GETMX(), e.g. use "dig". */ /* 3 - Please modify variables HELO and FROM for your system: */ HELO = 'example.org.invalid' /** PLEASE modify THIS line **/ FROM = 'abuse@' || HELO /** please MODIFY this LINE **/ signal on novalue name TRAP ; signal on syntax name TRAP signal on failure name TRAP ; signal on halt name TRAP signal on notready name TRAP ; signal on error name TRAP CRLF = x2c( 0D0A ) ; EXPO = 'TSOCK CRLF' parse arg ADDR '@' PEER THIS if PEER = '' then parse arg PEER ':' PORT THIS ADDR else parse var PEER PEER ':' PORT if PORT = '' then PORT = 25 if ADDR = '' then ADDR = 'postmaster' else ADDR = ADDR || '@' || PEER if PEER = '' | THIS <> '' then do parse source . . THIS say 'usage:' THIS 'address' say 'the default test address for an FQDN is ' exit 1 end THIS = PEER PEER = GETMX( THIS ) ; say 'using' PEER 'for' THIS signal on novalue name TFAIL ; signal on syntax name TFAIL signal on failure name TFAIL ; signal on halt name TFAIL if TOPEN( PEER, PORT ) then exit 1 THIS = date( 'S' ) time() say THIS 'TCP connection with' PEER || ':' || PORT select when TRANS( /* intro */ ) then nop when TRANS( 'ehlo' HELO ) then nop when TRANS( 'mail from:<' || FROM || '>' ) then nop when TRANS( 'rcpt to:<' || ADDR || '>' ) then nop otherwise nop end call TRANS 'quit' exit SockClose( TSOCK ) <> 0 GETMX: procedure expose (EXPO) /* parse `nslookup -q=mx` output */ arg NAME ; PRI = copies( 9, digits()) address CMD '@nslookup -q=mx' NAME '| rxqueue' rxqueue( 'GET' ) do while sign( queued()) pull . ' = ' NEW ',' . ' = ' MX select when MX = '' | MX = '.' then nop when datatype( NEW, 'w' ) = 0 then nop when NEW > PRI then nop otherwise NAME = MX ; PRI = NEW end end return strip( NAME ) TRANS: procedure expose (EXPO) /* send stuff and get an answer: */ if arg() = 1 then do if TSEND( arg( 1 ) || CRLF ) then exit TFAIL() say substr( time( 'L' ), 4, 8 ) arg( 1 ) end DATA = TREAD() ; MORE = '' do while DATA \== '' MORE = MORE || DATA do while sign( pos( CRLF, MORE )) parse var MORE DATA (CRLF) MORE say substr( time( 'L' ), 4, 8 ) DATA end if MORE == '' then do DATA = word( DATA, 1 ) select when length( DATA ) <> 3 then nop when sign( verify( DATA, '0123456789' )) then nop otherwise return 1 - abbrev( DATA, '2' ) end /* 0: 2xx ok., 1: not 2xx is bad */ end DATA = TREAD() end exit TFAIL() /* -------------------------------------------------------------- */ /* RXsock.dll interface (TOPEN, TREAD, TSEND, TFAIL) + gen. RXMSG */ TOPEN: procedure expose (EXPO) /* TCP connect with HOST at PORT */ if RxFuncQuery( 'SockLoadFuncs' ) then do call RxFuncAdd 'SockLoadFuncs', 'RXSOCK', 'SockLoadFuncs' call SockLoadFuncs 'N' /* TRAP if RXSOCK.DLL not found */ end if datatype( value( 'TSOCK' ), 'w' ) then call SockClose TSOCK if sign( verify( arg( 1 ), '0.123456789' )) = 0 then do if SockGetHostByAddr( arg( 1 ), 'PEER.' ) = 0 then do PEER.ADDR = arg( 1 ) ; PEER.HOST = arg( 1 ) end /* support IP without host name: */ end /* SockConnect() handles bad IP */ else if SockGetHostByName( arg( 1 ), 'PEER.' ) = 0 then return RXMSG( 'unknown host' arg( 1 ) value( 'h_errno' )) PEER.PORT = arg( 2 ) ; PEER.FAMILY = 'AF_INET' TSOCK = SockSocket( PEER.FAMILY, 'SOCK_STREAM', 'IPPROTO_TCP' ) if 0 <= TSOCK then do if SockConnect( TSOCK, 'PEER.' ) = 0 then return 0 end /* 0: okay, connected with TSOCK */ return TFAIL() /* 1: error shown, socket closed */ TREAD: procedure expose (EXPO) /* TCP read line (or data block) */ READ = '' do until N < 2000 | sign( pos( x2c( 0A ), READ )) N = SockRecv( TSOCK, 'DATA', 2000 ) if N > 0 then READ = READ || left( DATA, N ) end return READ TSEND: procedure expose (EXPO) /* TCP send complete data block */ if arg( 1, 'e' ) /* 1: any error, 0: sent / close */ then return length( arg( 1 )) <> SockSend( TSOCK, arg( 1 )) else return SockShutDown( TSOCK, 1 ) <> 0 RXMSG: procedure expose (EXPO) /* show error message & return 1 */ parse source . . THIS ; signal on syntax name RXMSG.TRAP call RxMessageBox arg( 1 ), THIS, /**/, 'HAND' ; return 1 RXMSG.TRAP: call SockPSock_Errno arg( 1 ) ; return 1 TFAIL: /* close sockets and handle TRAP */ signal on novalue name TRAP ; signal on syntax name TRAP signal on failure name TRAP ; signal on halt name TRAP TRAP = RXMSG( 'socket error' SockSock_Errno() value( 'errno' )) if symbol( 'TSOCK' ) = 'VAR' then TRAP = SockClose( TSOCK ) if condition() = '' then return 1 /* drop into normal TRAP handler, 'sigl' + 'result' preserved: */ /* see , (c) F. Ellermann */ TRAP: /* select REXX exception handler */ call trace 'O' ; trace N /* don't trace interactive */ parse source TRAP /* source on separate line */ TRAP = x2c( 0D ) || right( '+++', 10 ) TRAP || x2c( 0D0A ) TRAP = TRAP || right( '+++', 10 ) /* = standard trace prefix */ TRAP = TRAP strip( condition( 'c' ) 'trap:' condition( 'd' )) select when wordpos( condition( 'c' ), 'ERROR FAILURE' ) > 0 then do if condition( 'd' ) > '' /* need an additional line */ then TRAP = TRAP || x2c( 0D0A ) || right( '+++', 10 ) TRAP = TRAP '(RC' rc || ')' /* any system error codes */ if condition( 'c' ) = 'FAILURE' then rc = -3 end when wordpos( condition( 'c' ), 'HALT SYNTAX' ) > 0 then do if condition( 'c' ) = 'HALT' then rc = 4 if condition( 'd' ) > '' & condition( 'd' ) <> rc then do if condition( 'd' ) <> errortext( rc ) then do TRAP = TRAP || x2c( 0D0A ) || right( '+++', 10 ) TRAP = TRAP errortext( rc ) end /* future condition( 'd' ) */ end /* may use errortext( rc ) */ else TRAP = TRAP errortext( rc ) rc = -rc /* rc < 0: REXX error code */ end when condition( 'c' ) = 'NOVALUE' then rc = -2 /* dubious */ when condition( 'c' ) = 'NOTREADY' then rc = -1 /* dubious */ otherwise /* force non-zero whole rc */ if datatype( value( 'RC' ), 'W' ) = 0 then rc = 1 if rc = 0 then rc = 1 if condition() = '' then TRAP = TRAP arg( 1 ) end /* direct: TRAP( message ) */ TRAP = TRAP || x2c( 0D0A ) || format( sigl, 6 ) signal on syntax name TRAP.SIGL /* throw syntax error 3... */ if 0 < sigl & sigl <= sourceline() /* if no handle for source */ then TRAP = TRAP '*-*' strip( sourceline( sigl )) else TRAP = TRAP '+++ (source line unavailable)' TRAP.SIGL: /* ...catch syntax error 3 */ if abbrev( right( TRAP, 2 + 6 ), x2c( 0D0A )) then do TRAP = TRAP '+++ (source line unreadable)' ; rc = -rc end select when 1 then do /* in pipes STDERR: output */ parse version TRAP.REXX . . /* REXX/Personal: \dev\con */ signal on syntax name TRAP.FAIL if TRAP.REXX = 'REXXSAA' /* fails if no more handle */ then call lineout 'STDERR' , TRAP else call lineout '\dev\con', TRAP end when 0 then do /* OS/2 PM: RxMessageBox() */ signal on syntax name TRAP.FAIL call RxMessageBox , /* fails if not in PMREXX */ translate( TRAP, ' ', x2c( 0D )), , 'CANCEL', 'WARNING' end /* replace any CR by blank */ otherwise say TRAP ; trace ?L /* interactive Label trace */ end if condition() = 'SIGNAL' then signal TRAP.EXIT TRAP.CALL: return rc /* continue after CALL ON */ TRAP.FAIL: say TRAP ; rc = 0 - rc /* force TRAP error output */ TRAP.EXIT: exit rc /* exit for any SIGNAL ON */