/*-------------------------------------------------------------------*/ /* */ /* TEST1413 EXEC: Test a VM Ident Daemon -- Melinda Varian */ /* */ /* This file provides a test case for a VM Ident Daemon (as defined */ /* by RFC 1413). It sets up a dummy server and a dummy client to */ /* run in the same virtual machine. When the dummy client begins */ /* a session with the dummy server, the server issues an Ident */ /* query to the system's Ident Daemon. (That is, the dummy server */ /* becomes a client to the Ident Daemon running on the host on */ /* which its own dummy client is running.) */ /* */ /* This EXEC can be invoked in two ways, as a straight EXEC that */ /* issues a PIPE command that invokes a REXX filter, and as that */ /* REXX filter. */ /* */ /*-------------------------------------------------------------------*/ Signal On Syntax Signal On Novalue Signal On Error Parse Source . . . . . . how . /* How were we called? */ If (how = '?') Then Signal Filter_1413 /* As a pipeline filter. */ /*-------------------------------------------------------------------*/ /* Here when invoked as EXEC, not pipeline filter */ /*-------------------------------------------------------------------*/ Parse Arg stack , /* Userid of stack w/daemon. */ my_port . /* Free port on default stack. */ If stack == '' Then stack = 'TCPIP' If my_port == '' Then my_port = '1999' Address Command Say '----------------------------------------------------------------' Say ' ' Say 'Running RFC 1413 TCP test (Ident -- port 113)' Say ' ' 'PIPE (name RFC1413_TCP_Client)', 'rexx (test1413 exec)' stack my_port /* Test the Ident daemon. */ Say ' ' Say '----------------------------------------------------------------' Exit /*-------------------------------------------------------------------*/ /* */ /* Here when invoked as a pipeline filter */ /* */ /* This pipeline filter issues an ADDPIPE to create a small server */ /* running on the local default TCP/IP stack (and listening on the */ /* port specified by the "my_port" arg). It then issues another */ /* ADDPIPE to create a small client running on the TCP/IP stack */ /* specified by the "stack" argument. (The two stacks can be the */ /* same or different.) */ /* */ /* Once the client has connected to the server via TCP/IP, the */ /* server sends an Ident query to port 113 of the stack on which */ /* the client is running, to determine the userid of the client. */ /* After the Ident Daemon has responded, the server sends a */ /* message to the client, and they both terminate. */ /* */ /* (All communication between the server and client is via TCP/IP, */ /* even though they are running in the same virtual machine.) */ /* */ /*-------------------------------------------------------------------*/ Filter_1413: /* Beginning of pipe filter. */ Parse Arg stack , /* ID of stack running Ident. */ my_port /* Local port we can use. */ /*-------------------------------------------------------------------*/ /* */ /* The server consists of the following ADDPIPE and the REXX code */ /* later in this filter (starting with the "READTO" and ending */ /* with the "OUTPUT"). The ADDPIPE is connected to the REXX code */ /* via the primary input and output streams. That is, the ADDPIPE */ /* is configured as an "affixed ADDPIPE": */ /* */ /* +------------------------------+ */ /* | | */ /* | Rexx Code | */ /* | | */ /* +-->| READTO OUTPUT |---+ */ /* | | | | */ /* | +------------------------------+ | */ /* | | */ /* *.input.0: *.output.0: */ /* | | */ /* | +------------------------------+ | */ /* | | | | */ /* +---| DummyServer ADDPIPE |<--+ */ /* | | */ /* +------------------------------+ */ /* */ /* */ /* Or, in more detail: */ /* */ /* +------------------------------+ */ /* | | */ /* | Rexx Code | */ /* | | */ /* +-*.input.0:-->| READTO OUTPUT |--*.output.0:--+ */ /* | | | | */ /* | +------------------------------+ | */ /* | | */ /* | +------+ +----+ +------+ +-----+ +-------+ +----+ | */ /* | | tcp- |--|take|--|fanout|-------|fanin|--|tcpdata|--|hole| | */ /* | |listen| +----+ | |-+ +->| | +-------+ +----+ | */ /* | +------+ +------+ | | +-----+ | */ /* | | | | */ /* +----------------------------+ +-----------------------------+ */ /* */ /* */ /* Or, folded inside-out: */ /* */ /* +------+ +-----+ */ /* +------+ +----+ | |----------| | +-------+ +----+ */ /* | tcp- |--|take|--|fanout| +----+ |fanin|--|tcpdata|--|hole| */ /* |listen| +----+ | |--|Rexx|--| | +-------+ +----+ */ /* +------+ +------+ |Code| +-----+ */ /* +----+ */ /* */ /*-------------------------------------------------------------------*/ 'ADDPIPE (endchar ? name DummyServer)', /* Make a dummy server: */ 't: tcplisten' my_port, /* Run it on default stack. */ 'reuseaddr |', /* (Let port be re-used.) */ 'take 1 |', /* One client is enough. */ 'o: fanout |', /* Divert copy of descriptor. */ 'f: fanin |', /* Bring in reply for client. */ 'T: tcpdata |', /* Communicate with client. */ 'hole', /* Keep connection alive. */ '?', 'o: |', /* Copy of client descriptor. */ '*.input.0:', /* Input to this filter. */ '?', '*.output.0: |', /* Output from this filter. */ 'f:', /* Response to go to client. */ '?', 't: |', 'nfind 5001|', /* Pipeline already stopped. */ 'insert /A: / | console | pipestop', /* Stop other tasks. */ '?', 'T: |', 'nfind 5001|', /* Pipeline already stopped. */ 'insert /B: / | console | pipestop' /* Stop other tasks. */ /*-------------------------------------------------------------------*/ /* After the server ADDPIPE has been issued, we wait for a short */ /* interval to allow it to connect to the TCP/IP stack and begin */ /* waiting for a client to connect. */ /*-------------------------------------------------------------------*/ Signal Off Error 'SUSPEND' /* Let server get started... */ 'CALLPIPE literal +00:00:01 | delay' /* ...before starting client. */ 'SUSPEND' Signal On Error /*-------------------------------------------------------------------*/ /* */ /* We then issue another ADDPIPE command to create a client for */ /* the server. This small client task runs asynchronously in the */ /* same virtual machine as the server, communicating with the */ /* server only via TCP/IP. It sends one record to the server and */ /* waits to receive a single response line in return. After it */ /* has displayed that line on the virtual machine console, it */ /* terminates. */ /* */ /* All of the "tcpxxxxx" stages in both the client and the server, */ /* as well as in the pipeline that connects to the Ident Daemon */ /* (below), feed a record to a "pipestop" stage if they terminate */ /* abnormally and the "ERRNO" is not 5001. This allows these */ /* asynchronous tasks to terminate one another in case of an */ /* unrecoverable TCP/IP failure. (An ERRNO 5001 indicates that */ /* a record has been sent to a "pipestop" stage in some other of */ /* the asynchronous pipelines -- no need to do it again.) */ /* */ /*-------------------------------------------------------------------*/ 'CALLPIPE hostid | var local_host' /* IP address of default stack.*/ 'ADDPIPE (endchar ? name DummyClient)', /* Make client for server: */ 'strliteral x0D25 |', /* EBCDIC "query" for server. */ 't: tcpclient' local_host, /* Send to the dummy server... */ my_port 'userid' stack, /* ...from specified stack. */ 'deblock CRLF', /* Deblock (EBCDIC) response. */ 'oneresponse |', /* Enforce 1 output per input. */ 'take 1 |', /* Stop when get one record. */ 'console', /* Display the response. */ '?', 't: |', 'nfind 5001|', /* Pipeline already stopped. */ 'insert /C: / | console | pipestop' /* Stop other tasks. */ /*-------------------------------------------------------------------*/ /* */ /* When the client connects to the server, "tcplisten" in the */ /* server ADDPIPE will write a client connection request record */ /* to its output. "fanout" will make two copies of that record, */ /* sending the first to "tcpdata" and the second to the input */ /* connector. "tcpdata" will read its copy of the record and */ /* begin communicating with the client. The copy of the record */ /* that is written to the input connector will be read by the */ /* "READTO" command below. */ /* */ /*-------------------------------------------------------------------*/ 'READTO record' /* Wait for client to connect. */ If RC <> 0 /* Did client connect? */ Then Exit RC*(RC<>12) /* No, terminate. */ /*-------------------------------------------------------------------*/ /* */ /* This client connection request record contains the information */ /* the server needs to formulate an Ident query to send to the */ /* Ident Daemon on the host where the client is running. */ /* */ /*-------------------------------------------------------------------*/ 'CALLPIPE (name ParseDescriptor)', 'var record |', /* Load client descriptor. */ 'spec 65.16 1 |', /* Get client sockaddr_in. */ 'socka2ip |', /* Convert to readable form. */ 'split |', /* Make 3 one-word records. */ 'drop 1 |', /* Discard "AF_INET" record. */ 'var remote_port |', /* Store client's port number. */ 'drop 1 |', /* Discard that record, too. */ 'var remote_ip' /* Store client's host IP add. */ query = remote_port ',' my_port /* Build IDENT query message. */ Say 'Client is on port' remote_port 'on' remote_ip'.' Say 'Server is on port' my_port 'on' local_host'.' Say ' ' /*-------------------------------------------------------------------*/ /* */ /* The Ident query is sent to port 113 of the "stack" stack, which */ /* is the one on which the dummy client is running. The Ident */ /* Daemon listening on that port should reply with the userid of */ /* the virtual machine in which the dummy client is running. */ /* */ /*-------------------------------------------------------------------*/ 'CALLPIPE (endchar ? name RFC1413_TCP_Client)', 'var query |', /* Load IDENT query message. */ 'insert x0D25 after |', /* Garnish with CR and LF. */ 'xlate from 1047 to 819 |', /* Make the message ASCII. */ 't: tcpclient' remote_ip '113', /* Send message to IDENT port. */ 'deblock string x0D0A', /* Deblock the response. */ 'oneresponse', /* Enforce 1 output per input. */ 'timeout 15 |', /* Time out after 15 seconds. */ 'xlate from 819 to 1047 |', /* Convert response to EBCDIC. */ 'insert /Ident response is / |',/* Garnish the response. */ 'console', /* Display the response. */ '?', 't: |', 'nfind 5001|', /* Pipeline already stopped. */ 'insert /D: / | console | pipestop' /* Stop other tasks. */ /*-------------------------------------------------------------------*/ /* */ /* Once the response is received from port 113, a record can be */ /* sent on the output stream to the server ADDPIPE and across the */ /* network to the client to satisfy the "take 1" in the client, */ /* allowing it to terminate. */ /* */ /*-------------------------------------------------------------------*/ 'OUTPUT' 'Goodbye' || '0D25'x /* Reply to client via server. */ Exit /*-------------------------------------------------------------------*/ /* Error routines */ /*-------------------------------------------------------------------*/ Error: Say ' ' Say '----------------------------------------------------------------' Exit RC Novalue: Parse Source . . myfn myft . Say 'Novalue condition for symbol' Condition('D') 'on line' Sigl 'of', myfn myft':' Say Sourceline(Sigl) Exit 20 /* Exit with error setting. */ Syntax: Parse Source . . myfn myft . Say 'Syntax error on line' Sigl 'of' myfn myft':' Say Sourceline(Sigl) Say 'Error was:' Errortext(RC) Condition('D') Exit 24 /* Exit with error setting. */