Using example TELNET exit programs
The basic example TELNET initialization exit program (DEVINIT1)
The advanced example TELNET initialization exit program (DEVINIT2)
The example TELNET termination exit program (DEVTERM)
The advanced and basic TELNET initialization exit program examples
Examples: Using the ALLOW and MAP access lists to screen TELNET clients
Example: ALLOW access list for the basic TELNET exit program (DEVINIT1)
Example: MAP access list for the advanced TELNET exit program (DEVINIT2)
Parameters for the advanced TELNET initialization exit program's MAP access list
Example: DISALLOW access list for TELNET exit programs
Installing TELNET exit programs
Registering TELNET exit programs
Protecting TELNET exit programs
Example: Create TELNET exit program CL utility code (TELCRT)
Example: Delete TELNET exit program CL utility code (TELDLT)
Example: basic TELNET initialization exit program code (DEVINIT1)
Example: Advanced TELNET initialization exit program code (DEVINIT2)
Example: TELNET termination exit program code (DEVTERM)
Programmer's Reference Example: API (CUE) header file for TELNET exit programs (ETGDEVEX)
OS/400 Version 4 Release 2 provides new exit points for TELNET server use
You must have OS/400 V4R2 or higher installed on your AS/400 to use TELNET exit points and programs.
A road map of information
This is a road map of the V4R2 TELNET exit point and exit program information. Programs are included that you can use on your system with minor modifications. Also included are extensive instructions about how to create, install, modify, use, maintain, and uninstall TELNET exit programs. The programs, utilities, and instructions are designed to make learning about and using these exit programs as easy as possible, given the fairly complex process of using AS/400 exit programs in general.
Audience (who is this information for?)
This information is designed for AS/400 Administrators and Information Systems Managers, in other words, advanced users. It does not assume that you are familiar with TCP/IP, the Internet, or TELNET in particular. It requires more than a user level of AS/400 skills to use effectively.
Skill Requirements
Using this road map
For simplicity's sake, and to prevent you from getting lost in a chain of links between related documents, all of the documents listed below link back only to this road map. Exceptions to this rule apply only to links to AS/400 books that are published on the Internet. Links to AS/400 books are added wherever they may provide helpful additional information.
A TCP/IP reference book on the web
Additional information about the use of exit points and programs as a way to customize your AS/400 is available in the TCP/IP Configuration and Reference which is available on the Internet:
Internet security references
Note: | TELNET exit programs can greatly enhance the security of your TELNET server. Each TCP/IP server application is responsible for its own security. If your system is connected to the Internet, there are larger security issues you must be aware of. Your system must be protected from unauthorized access through your TCP/IP connection to the Internet. |
More information about securing your AS/400 on the Internet is available here:
Links to the TELNET exit program documents
For information about what TELNET exit points and programs are and how they can benefit you, see:
For Information about what the three example TELNET exit programs can do and the basics of how to use them, see:
For more detailed information about how to screen TELNET clients by using the example TELNET exit programs, and use the many other functions available, see:
For information about how to use the CL utility programs to install and remove TELNET exit programs, see:
Links to the program code:
What are they?
Exit points are attachment points. They provide a place to attach an external program to an AS/400 application. Their purpose is to allow you to excercise more control over an AS/400 application than the application's original code is designed to provide. In other words, they allow you to customize your system's function to meet very specific requirements; your requirements.
OS/400 Version 4 Release 2 provides new exit points for TELNET server use
You must have OS/400 V4R2 or higher installed on your AS/400 to use TELNET exit points and programs.
What do they do?
Exit points give you a way to gain very specific control of some portion of an application's function. Control of an AS/400 application exits from an application itself and transfers through an exit point to an exit point program (or exit program). After the exit program performs its tasks, it transfers control back to the application (in this case, the TCP/IP TELNET server application).
Benefits of using TELNET exit points (with exit programs)
TELNET exit points, and the example TELNET exit programs, provide you with the means to very specifically control a number of TELNET server functions, including:
Security: You can grant or deny access to specific TELNET clients (users), or specific groups of TELNET users, such as those that reside on a specific TCP/IP network, or subnetwork.
Monitor TELNET use: You can specify that records be kept in log files that tell you who has accessed your TELNET server, who has been denied access, and how long each TELNET session lasts.
Terminal Device: You can assign specific terminal device interfaces to specific TELNET users, or groups of TELNET users.
Work Management: You can enhance the performance of the AS/400 by distributing (routing) TELNET sessions to specific subsystems within the AS/400.
How are the TELNET exit points identified on the AS/400?
Each exit point has a name and an exit point interface. The exit point interface contains a list of input and output parameters that the TELNET server exchanges with your TELNET exit program. V4R2 provides two new exit points for the exclusive use of the TELNET server:
One for TELNET session initialization (log on)
(format) Exit Point Name Exit Point Interface Application --------------------------------------------------------------------- QIBM_QTG_DEVINIT INIT0100 TELNET session initiation
One for TELNET session termination
(format) Exit Point Name Exit Point Interface Application ---------------------------------------------------------------------- QIBM_QTG_DEVTERM TERM0100 TELNET session termination
Return to the TELNET exit program road map:
What are the example TELNET exit programs?
The exit program examples provided are 'C' programs that attach to the TELNET server at the TELNET exit points (you can think of them as attachment points).
OS/400 Version 4 Release 2 provides new exit points for TELNET server use
You must have OS/400 V4R2 or higher installed on your AS/400 to use TELNET exit points and programs.
What do they do?
TELNET exit programs are designed to temporarily take control of the TELNET server. They allow you to exercise more control over TELNET sessions than the TELNET server code is designed to provide. TELNET exit points are new for V4R2.
Exit programs must be registered to exit points
In order to work, a TELNET exit program must be registered (assigned) to a specific TELNET exit point. A TELNET initialization exit program must be registered to the TELNET initialization exit point. A TELNET termination exit program must be registered to the TELNET termination exit point. The exit points and exit programs are symbiotic in the sense that one is useless without the other. There are specific AS/400 commands that register exit programs to exit points. The commands and instructions on how to use them will be explained later.
Note: | The TELCRT install utility registers your exit programs to the correct exit points automatically. |
Before an exit program can be registered to an exit point, it must be compiled, and its *PGM object created and installed. Working examples of exit program code written in C programming language are provided. A small amount of modification to these example exit programs is required on your part before they will be of use to you. Instructions on which parts of the programs to modify, and how to modify them, are included. A CL utility program, TELCRT, is also provided which automates the compilation, installation, and registration process.
Exit programs need a minimum level of authority on your AS/400 to function properly
Various program, library, and user authorities must be granted to both the exit program (*PGM object) and the library it resides in. The various authorities granted to both the program and library have a profound effect on the security of both the exit program itself, and the AS/400 system it was written to protect. You must provide protection for the guard that is protecting you. See: "Protecting TELNET exit programs" for more information.
Note: | The TELCRT install utility automatically assigns the proper authorities to all of the required libraries, files, members, and program objects. |
TELNET Exception Handling
Your TELNET exit programs may encounter errors while they are processing. These errors are called exceptions. There are many possible causes for exception errors. If you write your own exit program, it may have bugs. Or the exit program may have called a file that no longer exists, and so on. The example TELNET exit programs have exception handling built into them. It is impossible to anticipate, and prepare for, all of the possible exceptions that the exit program may encounter.
There are two basic types of exceptions that an exit program may encounter:
TELNET server safeguards
When the exit program encounters an exception that it can not handle, it may lock up, or it may pass the exception on to the TELNET server. In either case, the TELNET server has automatic safeguards designed to allow the server to continue functioning:
Check your TELNET server job log frequently
Check your TELNET server job log frequently for exception errors, and resolve them.
Important tips for programmers, if you write your own TELNET exit programs
Exceptions and Security Risks
Be aware that some unhandled exceptions can compromise the security functions of exit programs, and may allow unauthorized access to your TELNET server, and therefore your AS/400.
Uitility programs for installing and deleting TELNET exit programs
Two CL utility programs designed to simplify the process of creating, installing, and removing the example TELNET exit programs are included:
TELCRT QCLSRC - Compiles, creates, and installs TELNET exit programs on the AS/400.
TELDLT QCLSRC - Uninstalls and deletes TELNET exit programs in the event they are no longer needed, or are to be replaced by updated TELNET exit programs.
Working examples of both of these programs, written in CL programming language, are provided. Instructions on their use are included.
TELNET exit program benefits
Exit programs, in conjunction with their corresponding exit points, provide you with the means to very specifically control certain aspects of your TELNET server that the TELNET server application does not otherwise provide:
Security: You can grant or deny access to specific TELNET clients (users), or specific groups of TELNET users, such as those that reside on a specific TCP/IP network, or subnetwork.
Monitor TELNET use: You can specify that records be kept in log files that tell you who has accessed your TELNET server, who has been denied access, and how long each TELNET session lasts.
Terminal Device: You can assign specific terminal device interfaces to specific TELNET users, or groups of TELNET users.
Work Management: You can enhance the performance of the AS/400 by distributing (routing) TELNET sessions to specific subsystems within the AS/400.
Example TELNET exit programs are provided
Three working examples of TELNET exit programs have been developed by the AS/400 TELNET team and provided for your use:
Two for TELNET session initialization, in basic and advanced versions
And a companion session termination exit program that works together with either of the two initialization programs
Return to the TELNET exit program road map:
The basic TELNET initialization (log on) exit program just screens TELNET clients
The basic TELNET initialization exit program (DEVINIT1) lets you screen TELNET clients. You decide who is allowed to connect to your TELNET server and who is not. It is 'basic' in the sense that it is not designed to take advantage of the many other functions available to TELNET exit programs. The advanced TELNET exit program is designed to take advantage of those functions.
It uses a simple pair of 'access lists' to control access to your TELNET server
The basic TELNET initialization exit program (DEVINIT1) uses the access lists ALLOW and DISALLOW to control whether a TELNET client is allowed to connect to your TELNET server and get a sign-on panel. Either a TELNET client is allowed a sign-on panel or the client is refused, and a "connect" or "refused" message is logged. In addition, when a connected client terminates the TELNET session, a "disconnect" message is also logged. The message log allows you to monitor which users have been granted access, or have been denied access, to your AS/400. The access lists allow you to control which users are granted access, or are denied access, to your AS/400. More information about how these access lists work is in "Advanced and basic TELNET initialization exit program examples" which is linked to the road map.
Return to the TELNET exit program road map:
DEVINIT2's MAP access list allows you to do more than just screen TELNET clients
The advanced TELNET initialization (log in) exit program uses the access lists MAP and DISALLOW. By using the MAP list instead of the simpler ALLOW list, the advanced initialization program exploits more of the exit point interface than the basic version. It allows you to set or override TELNET session settings which is a function you normally see in Client Access environments. Here are some examples of the kinds of session settings you can set:
Start with the basic version first
It is recommended you start with the basic TELNET initialization exit program until you understand how it works, and then migrate to the advanced TELNET initialization exit program if you require Virtual Device mapping or other advanced functions.
Do I really need the advanced version?
A common reason for using the advanced version is to create a Virtual Terminal device name that is similar to the User Profile of a specific client, or maps to a particular NLS subsystem for a specific client. Specifying a particular device name will either automatically create that device if it does not exist, or use the existing device (if not already in use).
The advanced functions usually require TELNET clients that support RFC 1572
Normally, the advanced functions outlined here require that the TELNET client pass in the user profile as part of the connect request. While this is automatic with SNA-type environments, a user profile will only be received if a TELNET client supports RFC 1572 extensions to TELNET.
You can still use some of the advanced functions if a TELNET client does not support RFC 1572
If a TELNET client does not support RFC 1572 extensions, you can still perform device selection for it by mapping the client s IP address to a particular device. For example, if you know IP address 111.222.333.444 is client JSTEVENS, you can map that IP address to a Virtual Terminal device named JSTEVENS01 that you have defined. Because a Virtual Terminal device can only be in use by one TELNET session at a time, you should set a Virtual Terminal devices for a specific IP addresses only, and not for wildcard addresses. Even with a Virtual Device limited to a single IP address, only one TELNET session from this IP address will work (because the first TELNET session will have the Virtual Terminal device in use). THIS IS A LIMITATION OF THE PROTOTYPE CODE, NOT A LIMITATION OF THE EXIT POINT. The exit program could be changed to search for a free device name (using system API's) or build a Device name using any algorithm you desire.
Set parameters in the MAP access list to use advanced functions
Note: | To implement the advanced version's full range of functions, you need to set some or all of parameters 1, 2, 6, and 7. For explanations of these parameters, see: "Parameters for the advanced TELNET initialization exit program's MAP access list". For instructions on how to set the parameter values in the MAP access list, see the comments in: "Example: MAP access list for the advanced TELNET exit program (DEVINIT2)". Both documents are linked to the road map. |
Auto-signon Capability
TELNET exit programs can allow a client to bypass the sign-on panel by activating the auto-signon flag (parameter 7). This is called "Auto-signon", and allows a TELNET client to signon to your AS/400 without a password. It is critical that the exit program carefully screen which clients are allowed to auto-signon. The exit point provides an "encrypted password validated" flag as part of the connection description information in the exit point interface. This flag indicates if the client sent an encrypted password, and if TELNET determined it was valid on the AS/400. Your exit program is free to respect or ignore this flag. See the field marked 'E' in the MAP access list.
Auto-signon and encrypted passwords
Clients that support encrypted, or clear-text, password negotiations can be identified by the connection description information they pass to your exit program. You can specify that the sign-on panel be bypassed for these clients. Only Enhanced (that support RFC 1572 extensions) Clients or Client Access, which send encrypted and clear-text passwords, will be have the password validated. This allows the your exit program to provide a secure client sign-on process.
Some conditions for auto-signon and encrypted passwords
The example exit program code is preset to refuse auto-signon for clients that have the secure password flag set (meaning an encrypted password is required to auto-signon) but do not send a profile and an encrypted password. If the encrypted password flag is not set (meaning no encrypted password is required), then the exit program allows clients to auto-signon without passwords. The encrypted password flag is field 'E' on the MAP access list. In order for a client to auto-signon without a password, a profile must be supplied by either the client or the exit program.
A few notes on security
Note: | For more information about exit program security , see "Protecting TELNET exit programs" which is linked to the TELNET exit program road map |
Return to the TELNET exit program road map:
One example termination program is provided, filename:
DEVTERM works with both DEVINIT1 and DEVINIT2
This is a companion program to both DEVINIT1 QCSRC and DEVINIT2 QCSRC. The termination messages it logs can be matched up with the initialization messages to determine TELNET session duration.
Disconnect requests and time stamps
DEVTERM logs disconnect requests. When a TELNET client terminates its session, DEVTERM builds a timestamp record for logging. This record is written to the same log file as are the connect messages from the initialization exit program. By comparing the connect and disconnect logs for a particular client, you can calculate how long that client was connected.
DEVTERM receives only one parameter
The only parameter that is given to the DEVTERM exit program is the name of the virtual terminal device that was used. This may or may not be identical to that of the virtual terminal device requested at session startup time. Therefore, in order to match up session start and session end records, the program logs both the IP address and the port number of the TELNET client.
DEVTERM gets the client's IP address and port number
To get the IP address and port number of a client, DEVTERM calls the Retrieve Device Description (QDCRDEVD) system API using the DEVD0600 format for display devices. This API will return the IP address and network address information needed (the program then extracts the port number from the network address information).
DEVTERM logs disconnect messages to member LOG, and to the QTCP *MSGQ object
The exit program then formats a disconnect message and logs it to member LOG of the access list file. The disconnect message is also logged to the QTCP user profile *MSGQ object. This is simply a temporary message for quick inspection by the System Administrator.
Parameter for the TELNET termination exit program (DEVTERM)
The QIBM_QTG_DEVTERM exit point occurs when a TELNET client ends the TELNET session. This gives you the opportunity to log session termination information and optionally perform device reset or cleanup operations.
The following shows the parameter for the QIBM_QTG_DEVTERM exit point.
+---------------------------------------------------------------------+ | Required Parameter Group: | | | | 1 Device name Input Char(10) | | | | QSYSINC Member Name: NONE | | Exit Point Name: QIBM_QTG_DEVTERM | | Exit Point Format Name: TERM0100 | +---------------------------------------------------------------------+
The specific virtual device name that is associated with this TELNET session.
Return to the TELNET exit program road map:
Background
The first thing example TELNET initialization programs do is read the access lists to see if the remote TELNET client is allowed to connect to your AS/400 TELNET server. They try to match the client's IP address with entries in the exit program access lists. Whether or not there is a match, or which list the match occurs in, determines whether or not the TELNET client will be allowed to connect to your AS/400 TELNET server.
Some access list basics
Access lists are lists of IP addresses that you maintain using the Source Editing Utility (SEU). They exist in member files that the exit program calls during its operation, rather than as part of the exit program code. This allows you to update the files easily and often, without having to recompile or reinstall the exit program itself. The TELNET server can continue operation while you update the access list files. The TELNET server does not have to be restarted to use the updated files.
DEVINIT1 access lists ALLOW and DISALLOW
The ALLOW list supersedes the DISALLOW list. This means that if you disallow all IP addresses, then only those IP addresses specified in the ALLOW list will be given permission to connect to your TELNET server. Because the TELNET initialization exit program reads the ALLOW list first, you must carefully consider whether or not to put any entry in it that uses wildcards (*). An entry in the ALLOW list that contains wildcards will grant permission to a large number of TELNET clients, potentially granting access to users you do not want to access your AS/400 and posing a security risk.
Be carefull with wildcard entries!
The access lists are currently configured with wildcard entries to allow normal TELNET operation, which operates as if no TELNET exit programs are registered. Under normal TELNET operation, without the use of exit programs, all TELNET clients are allowed to sign on to your AS/400 TELNET server, none are disallowed. Once you have installed TELNET exit programs, you can modify the access lists so that only those users you designate will be allowed to connect.
Matching the client's IP address
The IP address of the TELNET client is passed by the TELNET server to the TELNET exit program. The address is then broken down into four individual segments for comparison purposes. An IP address entry (or template) from the ALLOW access list will be read and compared to the remote client's address. If the client's IP address matches an address template, the exit program will stop reading the ALLOW list and begin reading the DISALLOW list to see if any of its entries match the address. A match can be an exact individual IP address, or the address may conform to a wildcard entry that designates an entire subnetwork of addresses.
Examples of Matching the client's IP address
Here are some examples of IP addresses that match a wildcard (*) entry in an ALLOW list:
Entry in ALLOW IP address of requesting client ----------------------------------------------------- 1.2.*.* 1.2.3.4 1.2.5.8 1.2.119.26 All of the IP addresses in the right hand column are a match to the ALLOW list entry on the left.
Examples of addresses that do not match
Here are some examples of IP addresses that do NOT match the same ALLOW list entry:
Entry in ALLOW IP address of requesting client ------------------------------------------------------- 1.2.*.* 112.32.3.4 25.16.5.8 1.22.119.26 None of the IP addresses in the right hand column are a match to the ALLOW list entry on the left.
Allow is checked first, then DISALLOW
If the entire ALLOW access list is read without finding a match, the exit program proceeds to the DISALLOW access list. It will search the DISALLOW list in the same manner for a matching IP address template. If a match is found, then the TELNET client is specifically not allowed to connect. You must set the output flags pAllowConnect and pAllowAutoSignon both to '0' to deny access. These flags are set in the API header file (named ETGDEVEX) near the top of the DEVINIT1 program code.
Put specific addresses on top of the list
Since the access lists are read from top to bottom, put specific IP address entries ahead of any wildcard entries. In other words, put specific IP addresses at the top of the access list.
What happens when the client's IP address does not match
If all access lists are read and no matches are found, the remote client will be allowed to connect, just as if no exit program was running. Set the output flags pAllowConnect to '1' and pAllowAutoSignon to '0' in this case.
Using the MAP list instead of ALLOW in the advanced exit program (DEVINIT2)
You can set as many fields as you wish in your MAP table, however, adding or removing fields in the MAP table requires a corresponding change to the DEVINIT2 exit program. At a minimum, the required output flags pAllowConnect (parameter 6) and pAllowAutoSignon (parameter 7) must be reset to grant or deny TELNET access. If the pAllowConnect flag is enabled, then the I/O parameters will be active. Optionally, you can redefine the fields in either of the I/O parameters. To control what virtual terminal device is selected, you should set the fields in the Device description information parameter. If the pAllowAutoSignon flag is enabled, then you should set the fields in the User description information parameter. Note that the fields in the User description information parameter may have initial settings received from any TELNET client that supports RFC 1572 Environment Option extensions. You can inspect these settings and override them if desired. The raw data (in ASCII form) can be seen in the Environment options parameter.
Note: | For explanations of these parameters, see: "Parameters for the advanced TELNET initialization exit program's MAP access list" For instructions on how to set the parameter values in the MAP access list, see the comments in: "Example: MAP access list for the advanced TELNET exit program (DEVINIT2)" Both documents are linked to the road map. |
Some additional functions using MAP
You also have the option to perform other tasks if you so desire. For example, the same MAP access table could list a printer to be associated with a particular TELNET client. A special program can be written to read the MAP table and use it to modify the default output (printer) queue for the client with the CHGOUTQ command at sign-on time. This default printer setup could be done by setting the 'Current library' and 'Program to call' values in the User description information (parameter 1) with the name of a special program for this purpose (refer to the separate QRMTWTR PACKAGE for how such a program could function). See: "Example: MAP access list for the advanced TELNET exit program (DEVINIT2)" for instructions on setting these values in the MAP access list.
Log on attempts are posted to member LOG and to QTCP *MSGQ
With the example exit programs in use, any time a TELNET client attempts to connect to your TELNET server, a message indicating acceptance or refusal of the TELNET client is posted to the QTCP user profile *MSGQ, and also logged as a permanent record to member LOG in your access list *FILE object. A matching message is logged when the client disconnects. By comparing the timestamps in the connect and disconnect records, you can calculate how long the client was connected.
You can turn logging off
If you do not want any logging to be done, you need to modify the QUSRSYS/QTGEXITS data area. Change the value to the data by removing the '*MSGQ' or '*FILE' strings, or both strings. See the instructions in the comments of the access list examples for more information on how to turn on and off one or both of these logging mechanisms.
Bypassing the sign on panel
You must set the output flags pAllowConnect to '1' and pAllowAutoSignon to either '0' or '1', depending on whether you want to bypass the sign-on panel. If you are using the advanced initialization exit program (DEVINIT2), you can set these flags in the MAP access list. See "Example: MAP access list for the advanced TELNET exit program (DEVINIT2)". Otherwise these flags can be set in the API header file (ETGDEVEX) of DEVINT1.
Be careful when letting clients Bypass the sign on panel!
Letting a client bypass the sign-on panel means they get to sign-on to your AS/400 without having to use a password. This is the equivalent of an unsecured system! Before you let any client bypass the sign-on panel, you should check the connection information parameter to see if an encrypted password was received and validated by TELNET. This will be set if Enhanced Clients or Client Access send the encrypted password for validation. This allows the exit program to provide a secure client sign-on process.
Do not let your QTCP message queue or the LOG file get too big
If you have logging active and expect lots of TELNET traffic, you may need to prevent the QTCP message queue from becoming full by deleting the messages regularly. Likewise, you may need to copy, delete, or rename the file LOG member regularly if the file becomes large. A new member LOG will be created if none exists (provided user QTCP has been granted *OBJMGT authority to the file).
After the access lists have been processed
Once the exit program is finished comparing the TELNET client's IP address to both of the access lists, it will send a connection message to the connection log file if you have activated message logging. The message is logged to the QTCP user profile message queue object and the LOG member of the access list file.
Access list physical files
The access lists can be in either a physical file (PF) or source physical file (SRCPF). The example TELNET exit programs use a SRCPF so that the Source Editing Utility (SEU) can be used to update the access lists without recompiling the whole exit program.
QTCP needs authority
The TELNET server runs under user profile QTCP. If you grant QTCP access to all of the objects defined in your exit programs, you should not have any authority problems. If you add any objects to the exit program, be sure that QTCP has access to them.
To add records to database file members, such as logging connection records, you need to grant QTCP at least *CHANGE private authority. If you expect your exit program to add or create members to the file as needed, you need to give *OBJMGT private authority to user profile QTCP.
Return to the TELNET exit program road map:
If you are using the basic TELNET initialization exit program (DEVINIT1) which uses the ALLOW and DISALLOW access lists, see the examples below for the ALLOW list. If you are using the advanced TELNET initialization exit program (DEVINIT2) which uses the MAP and DISALLOW access lists, see the examples below for the MAP list.
If you are not sure which example exit program is installed on your system, see the following instructions:
Determining which exit programs (and access lists) are active
The access lists that are active depend on whether DEVINIT1 or DEVINIT2 is registered to exit point QIBM_QTG_DEVINIT, format INIT0100. To find out which program is registered, enter the following command at the AS/400 command line, then select option 8:
WRKREGINF EXITPNT(QIBM_QTG_DEVINIT) FORMAT(INIT0100)You should see either DEVINIT1 or DEVINIT2 under the Exit Program column. If you see:
DEVINIT1The basic TELNET initialization exit program is registered and access lists ALLOW and DISALLOW are active. If you see:
DEVINIT2The advanced TELNET initialization exit program is registered and access lists MAP and DISALLOW are active
The following examples demonstrate how to use the access lists of the basic example TELNET initialization exit program (DEVINIT1).
ALLOW supercedes DISALLOW
The ALLOW access list is your primary access list because it is examined first, and because it supersedes the DISALLOW list. It is assumed that you know the specific IP addresses of the TELNET clients you want to allow to log in to your AS/400, but not the addresses of the TELNET clients you DO NOT want to allow to log in.
Use specific adresses or subnets only in ALLOW
You should delete the *.*.*.* entry in the ALLOW list and replace it with specific IP addresses and subnetwork mask entries only. The *.*.*.* entry in the DISALLOW list will ensure that only those entries listed in the ALLOW list are allowed to log in. In other words, the *.*.*.* entry in the DISALLOW list prevents ALL users from logging in, except those subnet masks or TELNET clients you specifically define in your ALLOW access list.
Using subnet entries instead of specific addresses
For example, assume that you want to allow TELNET clients on the Rochester and Endicott subnetworks only to have access to your TELNET server. Here is an example of how to configure your access lists to achieve that.
Example 1: Allow Rochester and Endicott subnets ONLY
Because the ALLOW access list supersedes the DISALLOW list, decide who to ALLOW first, then who to DISALLOW. All IP addresses on the Rochester subnetwork (subnet) begin with 9.5. An example of a specific IP address on that subnetwork might be:
9.5.124.112 where 9.5. = The Rochester Subnetwork 124.112 = One host on the Rochester SubnetworkInstead of listing every known IP address on that subnetwork, use wildcards (*) to create one list entry for the entire subnetwork:
9.5.*.* where 9.5. = The Rochester Subnetwork *.* = Any host on the Rochester SubnetworkIn this case, the first two numbered segments of the IP address, the nine and the five, identify the Rochester subnetwork. The following two segments identify a specific host on that subnetwork. Using wildcards (*) in place of numbers in the last two address segments allows you to specify all of the individual addresses on the entire subnetwork.
Use wildcards to create one entry for an entire subnet
To allow specific subnets of users, such as 9.5.*.* for Rochester, and 9.130.*.* for Endicott, delete the default entry *.*.*.* from ALLOW, and add the subnets. The *.*.*.* entry in DISALLOW will stop anyone who is not from the Rochester or Endicott subnets from signing on. Your lists would look like this:
Example 2: Allow Rochester and Endicott subnets ONLY
ALLOW: 9.5.*.* # Anyone on the Rochester subnetwork 9.130.*.* # Anyone on the Endicott subnetwork DISALLOW: *.*.*.* # Anyone not from Rochester or Endicott
You can deny access to specific clients or subnets
Now that you know how to configure for the most common scenario, let us look at one of the quirks of the access lists which can be exploited. This quirk occurs when an IP address does NOT match an entry from either the ALLOW or the DISALLOW list. If an IP address does not match an entry in either list, then no access rule is applied, and the IP address (of the TELNET client) will be allowed to log in. You can use this quirk to deny specific IP addresses or subnets, while letting all others in. Here is an example of how that works:
Disallow a single IP address:
Suppose you want to deny access to a single Rochester IP address: 9.5.78.132, as well as all of the Endicott subnetwork. This is difficult because a Rochester IP address will match the Rochester subnet listed in the ALLOW access list in Example 1.
If we delete all entries from ALLOW, and only put those IP addresses or subnets that we want to deny TELNET access to in DISALLOW, then only the specific users we have specified are denied access while all others are allowed. Here is an example:
Example 3: Disallow a single IP address
ALLOW: # No entries DISALLOW: 9.5.78.132 # Deny access to this Specific user 9.130.*.* # Deny access to any user on the Endicott subnetWith this configuration, any TELNET client can connect to the TELNET server except for one client on the Rochester subnet (9.5.78.132), and all of the TELNET clients on the Endicott subnet. That is because the ALLOW list, which supersedes the DISALLOW list, allows all TELNET clients access. That is because there are no entries in the ALLOW list to match the client's address to. Only client addresses that match either of the two entries in the DISALLOW list will be prevented from accessing the TELNET server.
The following examples demonstrate how to use the MAP access list to screen TELNET clients. The MAP access list is used by the advanced TELNET exit program (DEVINIT2).
The MAP access list is more complex than the ALLOW list. In addition to screening TELNET clients, it gives you a way to set advanced functions such as bypassing the sign-on panel. The DISALLOW access is still used together with the MAP access list. The MAP list supercedes the DISALLOW list. The MAP list uses flags to control how various output parameters in the exit program interface are set. These flags are not actually part of the exit program parameters, but tell it how to set them. In other words, the MAP access list acts like a control panel that allows you to set values for the advanced TELNET exit program functions.
Note: | For explanations of these parameters, see: "Parameters for the advanced TELNET initialization exit program's MAP access list" For instructions on how to set the parameter values in the MAP access list, see the comments in: "Example: MAP access list for the advanced TELNET exit program (DEVINIT2)" Both documents are linked to the road map. |
Example 3: This MAP list contains two records (the companion DISALLOW list is also shown for convenience)
MAP: # M C S # L P e D o i E # i g n e n g n # IP Addr Profile B m u v KBD CP CS n n c # ------- ------- - - - - --- -- --- - - - 1.2.3.4 | STEVENS | # | # | # | # | USB | 37 | 697 | 1 | 1 | 0 | *.*.*.* | # | # | # | # | # | # | # | # | 1 | 1 | 1 | DISALLOW: *.*.*.* # Deny everyone not listed in the MAP file
The first record (1.2.3.4 ) allows a TELNET client at IP address 1.2.3.4 to auto-signon using user profile STEVENS if at least a clear-text password is received. And sets up a TELNET session that uses keyboard type USB, codepage 37 and character set 697. For the TELNET session to be set up, these values must be returned in the output parameters of the exit program interface. Any field with a '#' value means to leave that value in the output parameter unchanged. Thus, the current library, program, menu, and device name are left unchanged for a client connecting from IP address 1.2.3.4.
The second record (*.*.*.*) allows everyone else to connect, but they can only bypass the sign-on panel if an encrypted password is received (for example, from Client Access).
The DISALLOW record (*.*.*.*) prevents all TELNET clients from connecting to the TELNET server, except those listed in the MAP access list. In other words, the TELNET clients listed in MAP are the only ones allowed to connect. (Remember, the MAP list supercedes the DISALLOW list.)
Return to the TELNET exit program road map:
This is an example of an ALLOW access list used by the basic TELNET initialization exit program (DEVINIT1) It uses this list to screen TELNET users before allowing (granting) or disallowing (denying) them access to your AS/400 TELNET server.
The comments included in this example will help you to modify this ALLOW access list to suit your TELNET server requirements.
Return to the TELNET exit program road map:
The ALLOW access list used by DEVINIT1
# Client IP Description # ------------- --------------------------------------------------- #111.222.333.444 # Host employee.company.com #1.222.33.* # Opti-Connect group #2.333.44.* # Sub-network for Raised Floor administrators #5.666.77.* # Sub-network for TCP/IP developers *.*.*.* # Allow all clients to connect # # TELNET Client IP Address ALLOW Mapping Rules: # # This file lists IP addresses that WILL be allowed to connect # to the TELNET server. # # You must create a SRCPF (or PF) file object and download this # file as member ALLOW. The name of the file must be identified # inside character *DTAARA object QUSRSYS/QTGEXITS in the form # 'filelib/filename'. Do not put quotes in the *DTAARA. # Append '*MSGQ' and '*FILE' strings if you want messages logged # to QTCP message queue and file member LOG, respectively. # # User profile QTCP must have at least *READ private authority # to this file, *USE or *CHANGE is recommended for logging. # # 1) Any line with a '#' in column 1 is a comment. # 2) A '*' serves as a wildcard in IP address hops. # 3) A '#' delimits an IP address and description on a line. # 4) Host and domain names are not allowed. # 5) Place wildcard entries last since file is read top to # bottom. The first matching IP address applies. Put # comment lines after IP addresses to find matches quicker. # 6) The ALLOW file has priority over the DISALLOW file, use # care when putting wildcards into the ALLOW file. # # QUSRSYS/QTGEXITS Data Area Rules: # - Required to have library and name of map file as the first # string in the *DTAARA, separated by a slash. # - To log a message to QTCP user *MSGQ, append a blank # delimited '*MSGQ' string to the *DTAARA contents. # - To log a message to the file member LOG, append a blank # delimited '*FILE' string to the *DTAARA contents. # # QUSRSYS/QTGEXITS Data Area Example: # 'filelib/filename msgqflag fileflag' # 'TELEXITS/TELNET *MSGQ' # 'MYLIB/TELNET *MSGQ *FILE'
Return to the TELNET exit program road map:
This is an example of an access list MAP that the advanced TELNET initialization exit program uses to screen TELNET users, and set many of the parameters available to TELNET exit programs.
Note: | The comments included in this example explain how to set the values for each field. An explanation of these parameters is in: "Parameters for the advanced TELNET initialization exit program's MAP access list" which is linked to the TELNET exit program road map. The first MAP field: Client IP, relates to parameter 3 of the parameter table: Connection description information. The next four MAP fields: Profile, Library, Program, and Menu, relate to parameter 1 of the parameter table: User description information. The next four MAP fields: Device, KBD (keyboard type), CP (code page), and CS (character set), relate to parameter 2 of the parameter table: Device description information. The MAP field labeled C relates to parameter 6 of the parameter table: Allow connection (pAllowConnect in the ETGDEVEX API header file). The MAP field labeled S relates to parameter 7 of the parameter table: Allow auto-signon (pAllowAutoSignon in the ETGDEVEX API header file). The last MAP field, marked 'E', is the encrypted password flag. |
Return to the TELNET exit program road map:
The MAP access list
# Client IP Profile Library Program Menu Device KBD CP CS C S E # ---------------- ---------- --------- -------- --------- ---------- --- --- --- - - - # 5.110.70.63 | SMITHJ | # | # | # | # | USB | 37 | 697 | 1 | 1 | 1 | # 5.110.70.64 | TERRYT | ACCTING | PRONET | PROGRAM | # | USB | 37 | 697 | 1 | 1 | 1 | # 5.120.70.220 | TERRYT | ACCTING | PRONET | PROGRAM | TERRYT01 | USI | -1 | -1 | 1 | 1 | 1 | | 5.120.70.58 | TERRYT | ACCTING | PRONET | PROGRAM | 5250A | USI | -1 | -1 | 1 | 1 | 1 | | 5.120.70.44 | EPERKINS | # | # | # | # | # | # | # | 1 | 1 | 0 | | 5.120.70.48 | EPERKINS | # | # | # | # | # | # | # | 1 | 1 | 0 | | 5.120.70.213 | CARSON | # | # | # | # | # | # | # | 1 | 1 | 0 | | 5.120.70.60 | # | # | # | # | # | # | # | # | 1 | 1 | 0 | | 5.35.70.83 | # | # | # | # | # | # | # | # | 1 | 1 | 0 | | 5.35.62.* | # | # | # | # | # | # | # | # | 1 | 0 | 1 | | 5.110.70.* | # | # | # | # | # | # | # | # | 1 | 0 | 1 | | *.*.*.* | # | # | # | # | # | # | # | # | 1 | 0 | 1 | # # Telnet Client IP Address MAP Mapping Rules: # # 1) A '#' in column 1 indicates a comment, and a '|' is a field delimiter. # 2) A '*' can be used as a wildcard in IP address hops (sub-nets). # 3) Any field with '#' means to NOT set a return value, and the field contents are left unchanged. # 4) Device field values of '#' mean Telnet auto-selects the device (QPADEVnnnn). # Device field must be '#' for wildcard (sub-net) entries! # 5) A -1 for CP and/or CS mean Telnet should use system default values. # 6) Fields C (AllowConnect), S (Auto-signon) and E (Encrypted auto-signon) values must be '1' or '0', # meaning true or false, respectively. # 7) AllowConnect means to allow a connection (can get a sign-on panel). # 8) Auto-signon means to bypass sign-on panel. # 9) Encrypted auto-signon means that the user exit program checks the Connection Information for the # Client Password Validated flag to see if the client sent a valid encrypted password. If so, then # the 'S' Auto-signon flag is enabled. In general, the Encrypted field is used to validate clients # that send a profile (and password), as opposed to the map file supplying the profile. # # Legend: # # ClientIP - Client IP address must match this template for this record to apply. # Profile * - User profile used for auto-signon Telnet session. # Library - Current library for auto-signon Telnet session. # Program - Program to Call for auto-signon Telnet session. # Menu - Menu for auto-signon Telnet session. # Device * - This will be the Virtual Terminal device for this Telnet session. # KBD * - This will be the keyboard language type for the Virtual Terminal device. # CP * - This will be the code page for the Virtual Terminal device. # CS * - This will be the character set for the Virtual Terminal device. # C - Allow connection flag. Indicates if this client is allowed a sign-on panel. # S - Auto-signon flag. Indicates if this client can bypass the sign-on panel. # E - Encrypted password flag. Indicates if the client is required to send a # valid encrypted password before allowing the 'S' auto-signon flag function. # # * - this value can be negotiated by any Telnet client supporting RFC 1572 extensions.
Return to the TELNET exit program road map:
Both the Basic and advanced initialization exit programs use the ETGDEVEX API header file. This file contains parameters you can specify to control TELNET sessions. The basic initialization exit program, DEVINIT1, was not designed to configure these parameters, therefore they are preset to default values. However, the advanced initialization exit program, DEVINIT2, allows you configure parameters 1, 2, 6, and 7, by setting values in the MAP access list. Parameters 2 through 5 concern information sent by the TELNET client that the TELNET exit program needs to initiate the session. Comments in the MAP access list example explain how and where to set these parameter values.
Note: | Most of the values for parameters 2 through 5 can only be negotiated by TELNET clients that support TELNET extensions specified by TCP/IP RFC 1572. |
Background
The QIBM_QTG_DEVINIT exit point occurs when a TELNET client attempts to connect to your TELNET server. TCP/IP RFC 1572 extensions (TELNET Environment Option) allow TELNET clients to negotiate, among other things, the virtual device name, virtual device attributes, user profile, library, program to call, initial menu, etc. These values can then be used to control how the session is initiated.
Any client that does not support RFC 1572 extensions will use default values (which can also be set by your program). If your exit program does not set or override user or device description information, the client simply gets the sign-on panel (provided the Allow connection parameter permits it).
Your exit program can take this opportunity to log any of the information passed as parameters to a database (data area) file. This will create an access log of who tried to connect, which connections were accepted, etc. You can log a timestamp to mark the start of a session, then use the QIBM_QTG_DEVTERM exit point program to mark the end of a session, to calculate how long a client was connected.
Note: | Concerning the Connection description information. Due to TCP/IP limitations, the apparent originating IP address supplied by the TELNET client can be forged in the TCP/IP data packet. However, for internal networks and protected installations, you can expect this information to be accurate. |
The following table describes the parameters for the QIBM_QTG_DEVINIT exit point. (These parameters can be set in the MAP access list of the advanced TELNET initialization program, DEVINIT2.)
+---------------------------------------------------------------------+ | Required Parameter Group: | | | | 1 User description information I/O Char(*) | | 2 Device description information I/O Char(*) | | 3 Connection description Input Char(*) | | information | | 4 Environment options Input Char(*) | | 5 Length of environment options Input Bin(4) | | 6 Allow connection Output Char(1) | | 7 Allow auto-signon Output Char(1) | | | | QSYSINC Member Name: ETGDEVEX | | Exit Point Name: QIBM_QTG_DEVINIT | | Exit Point Format Name: INIT0100 | +---------------------------------------------------------------------+
Note: | The values for parameters 1, 2, 6, and 7 can be set in the MAP access list. The first MAP field: Client IP, relates to parameter 3 of the parameter table: Connection description information. The next four MAP fields: Profile, Library, Program, and Menu, relate to parameter 1 of the parameter table: User description information. The next four MAP fields: Device, KBD (keyboard type), CP (code page), and CS (character set), relate to parameter 2 of the parameter table: Device description information. The MAP field labeled 'C' relates to parameter 6 of the parameter table: Allow connection (pAllowConnect in the ETGDEVEX API header file). The MAP field labeled 'S' relates to parameter 7 of the parameter table: Allow auto-signon (pAllowAutoSignon in the ETGDEVEX API header file). See: "Example: MAP access list for the advanced TELNET exit program (DEVINIT2)". |
Information about the user that will be used as part of the auto-signon process. This parameter relates directly to four fields in the MAP access list: Profile, Library, Program, and Menu.
The following table shows the format of the user description information.
Offset Dec Hex Type Field Descriptions --- --- -------- -------------------------------------- 0 0 INT(4) Length of user description information 4 4 CHAR(10) User profile 14 E CHAR(10) Current library 24 18 CHAR(10) Program to call 34 22 CHAR(10) Initial Menu
Field Descriptions for the User Description table
Information that will be used to create or change the device used for this TELNET session. This parameter relates directly to four fields in the MAP access list: Device, KBD (keyboard type), CP (code page), and CS (character set).
The following table shows the format of the device description information, which describes the characteristics of the device to be associated with each TELNET session.
Offset Dec Hex Type Field Descriptions --- --- -------- ------------------------------------- 0 0 CHAR(10) Device name 10 A CHAR(8) Device format 18 12 CHAR(2) Reserved 20 14 BINARY(4) Offset to device attributes structure 24 18 BINARY(4) Length of device attributes structure 28 1C CHAR(*) Device attributes structure
Field Descriptions for the Device description information table:
Offset Dec Hex Type Field Descriptions --- --- -------- ------------------------------------- 0 0 CHAR(3) Keyboard identifier 3 3 CHAR(1) Reserved 4 4 BINARY(4) Code page 8 8 BINARY(4) Character set
Field Descriptions for the device attributes structure table:
Information about the client connection used by the TELNET initialization exit. This parameter relates to the first field in the MAP access list: Client IP.
Note: | These values are not set by you. It is information the TELNET client sends to your TELNET server about itself, and the type of TELNET session it is requesting. |
The following table shows the format of the connection description information, which describes client and connection information for this session.
Offset Dec Hex Type Field Descriptions --- --- -------- -------------------------------------- 0 0 INT(4) Length of connection description information 4 4 CHAR(20) Client internet address 24 18 CHAR(1) Client password validated 25 19 CHAR(14) Workstation type
Field Descriptions for the Connection description information table:
Name Size Description ---------- --------- -------------------------------------------- sin_len CHAR(1) Size of the sockaddr_in structure sin_family CHAR(1) Protocol family. IP is hex 02, IPX is hex 06 sin_port CHAR(2) 16-bit unsigned port number sin_addr CHAR(16) 4-byte unsigned network address
An array containing all the RFC 1572 environment options negotiated by the client. These will be in exactly the format as they were received from the client and specified by RFC 1572.
Specifies the length of the RFC 1572 environment options negotiated by the client. If no such options were negotiated, the length is set to 0.
Applies to all devices and indicates to the TELNET server whether the client should be allowed to connect. This parameter relates directly to the field labeled 'C' in the MAP access list , (and to pAllowConnect in the ETGDEVEX API header file). If the device type is DISPLAY and auto-signon is enabled, then the TELNET client may also bypass the sign-on panel on the AS/400. The possible values are:
Applies to DISPLAY device types, and indicates to the TELNET server whether the auto-signon operation should be allowed to proceed for this particular client. If auto-signon is allowed, then the TELNET client can bypass the sign-on panel on the AS/400. This parameter relates directly to the field labeled 'S' in the MAP access list , (and to pAllowAutoSignon in the ETGDEVEX API header file). The possible values are:
Return to the TELNET exit program road map:
This is an example of a DISALLOW access list that TELNET initialization exit programs use to screen TELNET users before allowing (granting) or disallowing (denying) them access to your AS/400 TELNET server.
The comments included in this example will help you to modify the DISALLOW access list to suit your requirements:
Return to the TELNET exit program road map:
The DISALLOW access list:
# Client IP Description # --------- --------------------------------------------------- *.*.*.* # All users not listed in ALLOW will be refused. # # TELNET Client IP Address DISALLOW Mapping Rules: # # This file lists IP addresses that will NOT be allowed to # connect to the TELNET server. # # You must create a SRCPF (or PF) file object and download this # file as member ALLOW. The name of the file must be identified # inside character *DTAARA object QUSRSYS/QTGEXITS in the form # 'filelib/filename'. Do not put quotes in the *DTAARA. # Append '*MSGQ' and '*FILE' strings if you want messages logged # to QTCP message queue and file member LOG, respectively. # # User profile QTCP must have at least *READ private authority # to this file, *USE or *CHANGE is recommended for logging. # # 1) Any line with a '#' in column 1 is a comment. # 2) A '*' serves as a wildcard in IP address hops. # 3) A '#' delimits an IP address and description on a line. # 4) Host and domain names are not allowed. # 5) Place wildcard entries last since file is read top to # bottom. The first matching IP address applies. Put # comment lines after IP addresses to find matches quicker. # 6) The ALLOW file has priority over the DISALLOW file, use # care when putting wildcards into the ALLOW file. # # QUSRSYS/QTGEXITS Data Area Rules: # - Required to have library and name of map file as the first # string in the *DTAARA, separated by a slash. # - To log a message to QTCP user *MSGQ, append a blank # delimited '*MSGQ' string to the *DTAARA contents. # - To log a message the file member LOG, append a blank # delimited '*FILE' string to the *DTAARA contents. # # QUSRSYS/QTGEXITS Data Area Example: # 'filelib/filename msgqflag fileflag' # 'USEREXIT/TELNET *MSGQ' # 'MYLIB/TELNET *MSGQ *FILE'
Return to the TELNET exit program road map:
Two utility programs have been developed to make creating, installing, and removing TELNET exit programs easier. These utility programs are provided to you in the form of CL program code.
Note: | You need *SECOFR authority on your AS/400 to run this install successfully. |
The TELCRT program will create all objects (library, files, data areas, programs) and set all object authorities required by the TELNET exit programs for you. It is strongly recommended that you use the TELCRT program to set up your TELNET exit programs. TELCRT QCLSRC is the CL source code for the installation tool.
The TELDLT program will remove all objects and authorities created as part of the TELCRT call. This can also be used for "clean-up" in case anything goes wrong with the TELCRT call. For example, if you attempt to install to an existing library to which you do not have authority, the TELCRT call will fail. TELDLT QCLSRC is the CL source code for the removal tool.
First, create the CL utility programs
Create a temporary source physical file (SRCPF) in your current or working library.
Note: | This file must have the CCSID(37) parameter to preserve code point integrity
of the source code for the ILE/C compiler. Save the library name and the name
of the temporary source physical file (SRCPF). (You will need this for a later
step.)
Temporary Library: _________________ (up to 10 characters) Temporary File: _________________ (up to 10 characters) |
Download the following files to your temporary SRCPF, substituting the filename for the member name.
Note: | To prevent problems, do not put the temporary SRCPF in the target library (for the completed exit programs). In fact, the target library does not need to exist now, TELCRT will create it. The TELCRT tool will also copy the TELNET exit program files into a permanent source physical file in the target library. When TELCRT completes successfully, you can delete the temporary SRCPF file. |
These files should be downloaded into the SRCPF you just created:
Filename Filetype Srctype Function -------- -------- ------- ---------------- ALLOW TELNET TXT access list (file) DISALLOW TELNET TXT access list (file) MAP TELNET TXT access list (file) DEVINIT1 QCSRC C exit program (code) DEVINIT2 QCSRC C exit program (code) DEVTERM QCSRC C exit program (code) TELCRT QCLSRC CL install program (code) TELDLT QCLSRC CL remove program (code)
CRTCLPGM PGM(*LIBL/TELCRT) SRCFILE(*LIBL/srcpf) SRCMBR(*PGM) TEXT('Install TELNET Exit Programs') Substitute the name of the temporary source physical file you put the exit program files into for the "srcpf" of SRCFILE(*LIBL/srcpf) in the above command.
CRTCLPGM PGM(*LIBL/TELDLT) SRCFILE(*LIBL/srcpf) SRCMBR(*PGM) TEXT('Delete TELNET Exit Programs') Substitute the name of the temporary source physical file you put the exit program files into for the "srcpf" of SRCFILE(*LIBL/srcpf) in the above command.
Both of the CL utility programs should be created and ready to use.
Just choose a name. The TELCRT tool will create the library and all necessary authorities for it later. It is recommended to install TELNET exit programs in a new library so that only objects related to the exit programs will exist there. Choose a name for the library and write it here for use in a later step:
Exit program Library: _________________ (up to 10 characters)
Just choose a name. The TELCRT tool will create the file and all necessary authorities for it later. Write it here for use in a later step:
Exit program File: _________________ (up to 10 characters)
Decide whether to use the basic TELNET initialization program (DEVINIT1) or the advanced (DEVINIT2). This determines which access lists will be used. Write the name of the exit program you want to use here, you will need it for a later step:
Name of Exit program: _________________ (DEVINIT1 or DEVINIT2)
Note: | Though not required, consider stopping the TELNET server if it is currently
running before calling TELCRT. This is to avoid exposing your AS/400 to
security risks while installing the TELNET exit programs. Enter the following
command at the AS/400 command line to stop the TELNET server:
ENDTCPSVR SERVER(*TELNET) |
Note: | It is recommended that the authority for this library be PUBLIC(*EXCLUDE). User profile QTCP requires *OBJMGT authority to this library. TELCRT will create this library if it does not exist, and these authorities will automatically be set for you. |
1 = ALLOW and DISALLOW access lists (Basic DEVINIT1) 2 = MAP and DISALLOW access lists (Advanced DEVINIT2)
Examples of TELCRT calls
Example 1:
CALL PGM(TELCRT) PARM('srclib/tmpsrcpf' 'tgtlib/tgtfile' '2')
Note: | All of the quotation marks are required. |
Example 2:
Your TELCRT call would look like this if:
CALL PGM(TELCRT) PARM('QGPL/TELEXTMP' 'TELEXPRO/TELNETEX' '1')
Note: | All of the quotation marks are required. |
The TELCRT install utility performs the following process:
Exit Point: QIBM_QTG_DEVINIT Format: INIT0100 Exit Point: QIBM_QTG_DEVTERM Format: TERM0100(TELCRT uses the ADDEXITPGM command.)
Your TELNET exit programs take effect as soon as TELCRT completes successfully!
Note: | If you stopped the TELNET server before running the TELCRT tool, you need to
restart it. Enter the following command at the AS/400 command line to start
the TELNET server:
STRTCPSVR SERVER(*TELNET) |
The following objects have been created
Substitute usrlib for the name of your exit program library, and usrfile for the name of your exit program file:
Obj Name Library Type Attr Description -------- -------- ------- ----- ---------------------------------- usrfile usrlib *FILE SRCPF File with access lists/log members DEVINIT1 usrlib *PGM CLE Basic access exit program DEVINIT2 usrlib *PGM CLE Advanced access exit program DEVTERM usrlib *PGM CLE Termination exit program QTGEXITS QUSRSYS *DTAARA N/A Exit program Data Area
If the install tool is successful, there will be an informational message in the joblog reminding you to delete the temporary SRCPF you created. You might also want to delete the TELCRT and TELDLT tools that you created.
Modify your access lists
Use the Source Editing Utility (SEU) to edit the access lists. The installation program posts a message to the job log indicating which access lists are active and should be modified. There will be an INFO message in your joblog to remind you of this.
Once you have modified your access lists, you need keep the list of IP addresses in your access lists up to date. To make changes to your access lists, edit the access list members with Source Editing Utility (SEU), or an equivalent source editor. The changes take effect as soon as they are saved. You could also make updates to the lists offline, then FTP the updates to your file.
Note: | Changes to your lists are effective as soon as they are SAVED, without need to re-IPL or restart the TELNET server. If you update copies of the lists offline and download the updated members with FTP, the changes are effective as soon as the FTP transfer is complete. |
Debugging
Testing TELNET exit programs is easier if you activate the debugging feature.
Append a *DEBUG string to the QTCP/QTGEXITS character data area. This will activate program operation records which are written to the DEBUG member in your access list file. The buffer() and record() functions are provided for this purpose. When you are satisfied with any changes and testing of your modified exit programs, you should remove this string.
Return to the TELNET exit program road map:
Your TELNET exit programs are registered to the correct exit points automatically when you use the TELCRT installation utility. If you have not used the TELCRT utility, you need to follow these procedures to register your exit programs "manually".
Once you have created your exit program, you must identify the name of the program and the library in which it is located. You do this by using the WRKREGINF or ADDEXITPGM commands (the parameters are similar). You page down to the exit point to which your program applies and enter option 8 (Work with Exit Programs). You then enter the program on the screen
+-----------------------------------------------------------------------+ | WARNING! Once this is complete, your exit program is active - it is | | NOT necessary to end and restart the TELNET server to pick | | up this exit program. | +-----------------------------------------------------------------------+
For example, for the TELNET server exit points, use the following CL command:
WRKREGINF EXITPNT(QIBM_QTG_DEV*) FORMAT(*ALL)
This should show a panel similar to the following:
+---------------------------------------------------------------------+ | Work with Registration Information | | Type options, press Enter. | | 5=Display exit point 8=Work with exit programs | | Exit | | Exit Point | | Opt Point Format Registered Text | | 8 QIBM_QTG_DEVINIT INIT0100 *YES | | _ QIBM_QTG_DEVTERM TERM0100 *YES | | Command | | ===> ______________________________________________________________ | | F3=Exit F4=Prompt F9=Retrieve F12=Cancel | +---------------------------------------------------------------------+
Use option 8 on the first exit point. You should see the following panel:
+---------------------------------------------------------------------+ | Work with Exit Programs | | Exit point: QIBM_QTG_DEVINIT Format: INIT0100 | | | | Type options, press Enter. | | 1=Add 4=Remove 5=Display 10=Replace | | | | Exit | | Program Exit | | Opt Number Program Library | | 1 __________ __________ | | | | Command | | ===> ______________________________________________________________ | | F3=Exit F4=Prompt F9=Retrieve F12=Cancel | +---------------------------------------------------------------------+
Using option 1 takes you to this panel, where you enter your program:
+---------------------------------------------------------------------+ | Add Exit Program (ADDEXITPGM) | | Type choices, press Enter. | | Exit point . . . . . . . . . . . > QIBM_QTG_DEVINIT | | Exit point format . . . . . . . > INIT0100 Name | | Program number . . . . . . . . . > 1 1-2147483647, ... | | Program . . . . . . . . . . . . yourpgm Name | | Library . . . . . . . . . . . yourlib Name, *CURLIB | | Text 'description' . . . . . . . *BLANK | | | | F3=Exit F4=Prompt F5=Refresh F10=Additional parameters | | F13=How to use this display F24=More keys | +---------------------------------------------------------------------+
Hit Enter and then F5. You should return to the following panel:
+---------------------------------------------------------------------+ | Work with Exit Programs | | Exit point: QIBM_QTG_DEVINIT Format: INIT0100 | | Type options, press Enter. | | 1=Add 4=Remove 5=Display 10=Replace | | Exit | | Program Exit | | Opt Number Program Library | | _ __________ __________ | | _ 1 yourpgm yourlib | | Command | | ===> ______________________________________________________________ | | F3=Exit F4=Prompt F9=Retrieve F12=Cancel | +---------------------------------------------------------------------+
Repeat the above steps for both the initialization and termination exit programs.
Return to the TELNET exit program road map:
To ensure your exit program does not expose your system to hackers, you should take steps to secure both the program and the source code for the program.
The TELNET server runs under user QTCP, but if QTCP is not authorized to your exit program, then the TELNET server adopts authority sufficient to call the program. If your exit program is not secured, it could be replaced by users to provide unauthorized access to your system.
How do you protect your TELNET exit program?
If your exit program requires other objects to run (log files, mapping tables, etc.) then your exit program must either adopt sufficient owner authority to the object or you must grant user profile QTCP private authority to the object. The specific private authority you grant will depend on the function you require, such as *ADD to insert a log file record, *READ to read a mapping table, etc.
Note: | You should be aware that anyone on your system with *SECOFR authority has the ability to modify the registration information for a exit program, such as changing the program that is registered. The TELNET exit programs can be modified to allow anyone to sign-on, even without knowing the password for a user profile! So, if you have lost control of *SECOFR authority on your system, you have lost control of TELNET security. |
Be aware that anyone on the Internet can intercept and read TCP/IP data packets and acquire user profile and password information from them. The only secure solution for this is to use some form of sockets layer encryption. This is a consideration if you want to put your system on the Internet.
Internet security references
Note: | TELNET exit programs can greatly enhance the security of your TELNET server. Each TCP/IP server application is responsible for its own security. If your system is connected to the Internet, there are larger security issues you must be aware of. Your system must be protected from unauthorized access through your TCP/IP connection to the Internet. |
More information about securing your AS/400 on the Internet is available here:
Return to the TELNET exit program road map:
Run the TELDLT utility program to remove TELNET exit programs
Call the TELDLT removal tool. This tool will know which library contains the TELNET exit programs. No parameters are required when calling this tool. Type and enter the following command on the AS/400 command line:
CALL PGM(TELDLT)
When the TELDLT removal tool is run, the following occurs:
Exit Point: QIBM_QTG_DEVINIT Format: INIT0100 Exit Point: QIBM_QTG_DEVTERM Format: TERM0100
Since you may have other objects in the library where your exit program files reside, this library is not automatically deleted as part of the call. Instead, the program posts an informational message to the joblog reminding you to delete the library if it is empty (it will tell you the name of the library to check).
Note: | TELNET will stop using the TELNET exit programs as soon as this program is complete. |
Return to the TELNET exit program road map:
This is working example code for the create program needed to install TELNET exit programs. This is a utility program written in AS/400's command language (CL).
Return to the TELNET exit program road map:
/*---------------------------------------------------------------------*/ /* Name: */ /* TELCRT - Install TELNET exit programs */ /* */ /* Function: */ /* Creates the program and objects necessary for the example */ /* TELNET exit programs. */ /* */ /* Parameters: */ /* 'srclib/srcname' */ /* This is the file and library to which the files have been */ /* loaded. This file must be a source physical file with */ /* CCSID 37. */ /* */ /* You should do a CRTSRCPF to make a temporary file and FTP all */ /* package members to this file. If you do not specify the */ /* 'library' portion, then '*LIBL' is used. */ /* */ /* Parameter must have quotes. */ /* */ /* 'tgtlib/tgtname' */ /* This is the file and library in which the access list and */ /* logs are to be created. If this library does not exist, it */ /* will be created. If this file does not exist, it is */ /* created. */ /* */ /* Parameter must have quotes. */ /* */ /* 'type' */ /* Specifies which TELNET exit programs should be registered to */ /* QIBM_QTG_DEVINIT and QIBM_QTG_DEVTERM exit points. */ /* */ /* '1' - register DEVINIT1 to format INIT0100 */ /* and DEVTERM to format TERM0100 */ /* Uses access lists ALLOW and DISALLOW */ /* */ /* '2' - register DEVINIT2 to format INIT0100 */ /* and DEVTERM to format TERM0100 */ /* Uses access lists MAP and DISALLOW */ /* */ /* Parameter must have quotes. */ /* */ /* Invocation: */ /* CALL PGM(TELCRT) PARM('srclib/srcname' 'tgtlib/tgtname' 'type') */ /* */ /* Example: */ /* */ /* CALL TELCRT PARM('JEFFSLIB/QCSRC' 'USEREXIT/TELNET' '2') */ /* */ /* Copies the package files from source physical file QCSRC in */ /* library JEFFSLIB, and creates all access lists in source physical */ /* file TELNET in library USEREXIT, compiles the 'C' source code */ /* into *PGM objects, registers the DEVINIT2 and DEVTERM programs to */ /* the QIBM_QTG_DEVINIT and QIBM_QTG_DEVTERM exit points, activating */ /* access lists MAP and DISALLOW. */ /* */ /* Authorities Required: */ /* You must have *USE authority to library QUSRSYS in order to */ /* create a data area object. A character *DTAARA object */ /* QUSRSYS/QTGEXITS will be created to point at the library */ /* containing the TELNET exit programs, access lists and logs, */ /* meaning you must have *ALL authority to the QUSRSYS/QTGEXITS data */ /* area as well (if one already exists). */ /* */ /* You must also have *USE authority to the ADDEXITPGM command in */ /* order to register exit programs to the exit points. */ /* */ /* You must also have *CHANGE and *OBJMGT authority to tgtlib */ /* (target library) in order to create and manage objects in the */ /* library and to grant authority there for user profile QTCP. The */ /* TELNET server runs under profile QTCP, which needs authority to */ /* read/update/create access lists and logs in the file object in */ /* the tgtlib. */ /* */ /* Objects created: */ /* */ /* Obj Name Lib Type Attr Description */ /* -------- --------- ------- ------ --------------------------------- */ /* tgtname tgtlib *FILE PF-SRC Source file for access lists/logs */ /* DEVINIT1 tgtlib *PGM CLE Basic access exit program */ /* DEVINIT2 tgtlib *PGM CLE Advanced access exit program */ /* DEVTERM tgtlib *PGM CLE Termination exit program */ /* QTGEXITS QUSRSYS *DTAARA N/A Exit program library pointer */ /*---------------------------------------------------------------------*/ PGM PARM(&FROM &TO &TYPE) /*-------------------------------------------------------------------*/ /* The parameter TOLIB should be a target library in which to */ /* create our objects, and should be PUBLIC(*EXCLUDE) to protect */ /* the access lists. This library should also be in the *LIBL of */ /* the caller running this program. */ /*-------------------------------------------------------------------*/ QSYS/DCL VAR(&FROM) TYPE(*CHAR) LEN(21) /* Source 'lib/name' */ QSYS/DCL VAR(&TO) TYPE(*CHAR) LEN(21) /* Target 'lib/name' */ QSYS/DCL VAR(&FROMLIB) TYPE(*CHAR) LEN(10) /* Src *FILE SRCPF lib */ QSYS/DCL VAR(&FROMFILE) TYPE(*CHAR) LEN(10) /* Src *FILE SRCPF name */ QSYS/DCL VAR(&TOLIB) TYPE(*CHAR) LEN(10) /* Tgt *FILE SRCPF lib */ QSYS/DCL VAR(&TOFILE) TYPE(*CHAR) LEN(10) /* Tgt *FILE SRCPF name */ QSYS/DCL VAR(&TYPE) TYPE(*CHAR) LEN(1) /* Exit prgm 1 or 2? */ /*-------------------------------------------------------------------*/ /* Variables for Convert Case API call */ /*-------------------------------------------------------------------*/ QSYS/DCL VAR(&CNVCSCB) TYPE(*CHAR) LEN(22) /*-------------------------------------------------------------------*/ /* Variables for parsing of 'lib/name' strings */ /*-------------------------------------------------------------------*/ QSYS/DCL VAR(&STRING) TYPE(*CHAR) LEN(21) QSYS/DCL VAR(&LIB) TYPE(*CHAR) LEN(10) QSYS/DCL VAR(&NAME) TYPE(*CHAR) LEN(10) QSYS/DCL VAR(&SLASHPOS) TYPE(*DEC) LEN(2 0) VALUE(0) QSYS/DCL VAR(&BLANKPOS) TYPE(*DEC) LEN(2 0) VALUE(0) QSYS/DCL VAR(&START) TYPE(*DEC) LEN(2 0) QSYS/DCL VAR(&LEN) TYPE(*DEC) LEN(2 0) /*-------------------------------------------------------------------*/ /* Error handling variables */ /*-------------------------------------------------------------------*/ QSYS/DCL VAR(&ERRORSW) TYPE(*LGL) QSYS/DCL VAR(&MSGID) TYPE(*CHAR) LEN(7) QSYS/DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(512) QSYS/DCL VAR(&MSGF) TYPE(*CHAR) LEN(10) QSYS/DCL VAR(&MSGFLIB) TYPE(*CHAR) LEN(10) /*-------------------------------------------------------------------*/ /* The following are MEMBERs in the access list file */ /*-------------------------------------------------------------------*/ QSYS/DCL VAR(&ALLOW) TYPE(*CHAR) LEN(10) VALUE(ALLOW) QSYS/DCL VAR(&DISALLOW) TYPE(*CHAR) LEN(10) VALUE(DISALLOW) QSYS/DCL VAR(&MAP) TYPE(*CHAR) LEN(10) VALUE(MAP) QSYS/DCL VAR(&LOG) TYPE(*CHAR) LEN(10) VALUE(LOG) QSYS/DCL VAR(&DEBUG) TYPE(*CHAR) LEN(10) VALUE(DEBUG) /*-------------------------------------------------------------------*/ /* The following is the data area (points exit prgms to access list) */ /*-------------------------------------------------------------------*/ QSYS/DCL VAR(&DTALIB) TYPE(*CHAR) LEN(10) VALUE(QUSRSYS) QSYS/DCL VAR(&DTANAME) TYPE(*CHAR) LEN(10) VALUE(QTGEXITS) /*-------------------------------------------------------------------*/ /* These are the names of the TELNET exit programs. There are two */ /* TELNET Exit points, and a separate program is needed for each */ /* point. These programs need to be registered with ADDEXITPGM */ /* before they start running. */ /*-------------------------------------------------------------------*/ QSYS/DCL VAR(&INITPGM1) TYPE(*CHAR) LEN(10) VALUE(DEVINIT1) QSYS/DCL VAR(&INITPGM2) TYPE(*CHAR) LEN(10) VALUE(DEVINIT2) QSYS/DCL VAR(&TERMPGM) TYPE(*CHAR) LEN(10) VALUE(DEVTERM) /*-------------------------------------------------------------------*/ /* Monitor for all escape messages. */ /*-------------------------------------------------------------------*/ QSYS/MONMSG MSGID(CPF0000) EXEC(QSYS/GOTO STDERR1) /*-------------------------------------------------------------------*/ /* Upper case the arguments with the QLGCNVCS API */ /*-------------------------------------------------------------------*/ QSYS/CHGVAR VAR(%BIN(&CNVCSCB 1 4)) VALUE(1) /* CCSID format */ QSYS/CHGVAR VAR(%BIN(&CNVCSCB 5 4)) VALUE(0) /* Job CCSID */ QSYS/CHGVAR VAR(%BIN(&CNVCSCB 9 4)) VALUE(0) /* Upper Case */ QSYS/CHGVAR VAR(%SST(&CNVCSCB 13 10)) VALUE(X'00000000000000000000') QSYS/CALL PGM(QSYS/QLGCNVCS) + PARM(&CNVCSCB &FROM &FROM X'00000015' X'00000000') QSYS/CALL PGM(QSYS/QLGCNVCS) + PARM(&CNVCSCB &TO &TO X'00000015' X'00000000') /*-------------------------------------------------------------------*/ /* Set the variable with our first argument and parse. We could */ /* also use the QCLSCAN API but would take a performance hit. */ /*-------------------------------------------------------------------*/ QSYS/CHGVAR VAR(&STRING) VALUE(&FROM) LOOP1: QSYS/CHGVAR &BLANKPOS (&BLANKPOS + 1) QSYS/IF (&BLANKPOS *EQ 22) QSYS/GOTO DONE1 QSYS/IF (%SST(&STRING &BLANKPOS 1) *EQ ' ' ) QSYS/GOTO DONE1 QSYS/IF ((&SLASHPOS *EQ 0) *AND (%SST(&STRING &BLANKPOS 1) *EQ '/')) + QSYS/CHGVAR VAR(&SLASHPOS) VALUE(&BLANKPOS) QSYS/GOTO LOOP1 DONE1: QSYS/IF (&SLASHPOS *EQ 0) THEN(QSYS/DO) QSYS/CHGVAR VAR(&LIB) VALUE(*LIBL) QSYS/CHGVAR VAR(&LEN) VALUE(&BLANKPOS - 1) QSYS/CHGVAR VAR(&NAME) VALUE(%SST(&STRING 1 &LEN)) QSYS/ENDDO QSYS/ELSE CMD(QSYS/DO) QSYS/CHGVAR VAR(&LEN) VALUE(&SLASHPOS - 1) QSYS/CHGVAR VAR(&LIB) VALUE(%SST(&STRING 1 &LEN)) QSYS/CHGVAR VAR(&START) VALUE(&SLASHPOS + 1) QSYS/CHGVAR VAR(&LEN) VALUE(&BLANKPOS - &START) QSYS/CHGVAR VAR(&NAME) VALUE(%SST(&STRING &START &LEN)) QSYS/ENDDO /*-------------------------------------------------------------------*/ /* Set the results into our working variables */ /*-------------------------------------------------------------------*/ QSYS/CHGVAR VAR(&FROMLIB) VALUE(&LIB) QSYS/CHGVAR VAR(&FROMFILE) VALUE(&NAME) /*-------------------------------------------------------------------*/ /* Set the variable with our second argument and parse */ /*-------------------------------------------------------------------*/ QSYS/CHGVAR VAR(&STRING) VALUE(&TO) QSYS/CHGVAR VAR(&SLASHPOS) VALUE(0) QSYS/CHGVAR VAR(&BLANKPOS) VALUE(0) LOOP2: QSYS/CHGVAR &BLANKPOS (&BLANKPOS + 1) QSYS/IF (&BLANKPOS *EQ 22) QSYS/GOTO DONE2 QSYS/IF (%SST(&STRING &BLANKPOS 1) *EQ ' ' ) QSYS/GOTO DONE2 QSYS/IF ((&SLASHPOS *EQ 0) *AND (%SST(&STRING &BLANKPOS 1) *EQ '/')) + QSYS/CHGVAR VAR(&SLASHPOS) VALUE(&BLANKPOS) QSYS/GOTO LOOP2 DONE2: QSYS/IF (&SLASHPOS *EQ 0) THEN(QSYS/DO) QSYS/SNDPGMMSG MSG('A library is required for the tgt-lib/tgt-name') QSYS/RETURN QSYS/ENDDO QSYS/ELSE CMD(QSYS/DO) QSYS/CHGVAR VAR(&LEN) VALUE(&SLASHPOS - 1) QSYS/CHGVAR VAR(&LIB) VALUE(%SST(&STRING 1 &LEN)) QSYS/CHGVAR VAR(&START) VALUE(&SLASHPOS + 1) QSYS/CHGVAR VAR(&LEN) VALUE(&BLANKPOS - &START) QSYS/CHGVAR VAR(&NAME) VALUE(%SST(&STRING &START &LEN)) QSYS/ENDDO /*-------------------------------------------------------------------*/ /* Set the results into our working variables */ /*-------------------------------------------------------------------*/ QSYS/CHGVAR VAR(&TOLIB) VALUE(&LIB) QSYS/CHGVAR VAR(&TOFILE) VALUE(&NAME) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*INFO) + MSGDTA('Installing from' *BCAT + &FROMLIB *TCAT '/' *CAT &FROMFILE *BCAT + 'to' *BCAT + &TOLIB *TCAT '/' *CAT &TOFILE *BCAT + 'using type' *BCAT &TYPE) /*-------------------------------------------------------------------*/ /* Check to ensure we can create the data area in library QUSRSYS */ /*-------------------------------------------------------------------*/ QSYS/CHKOBJ OBJ(QSYS/&DTALIB) OBJTYPE(*LIB) AUT(*CHANGE) QSYS/MONMSG MSGID(CPF9802) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('You must have *CHANGE authority to library' *CAT &DTALIB) QSYS/ENDDO /*-------------------------------------------------------------------*/ /* Check authority to any existing data area object */ /* CPF1022 - No authority to library QUSRSYS data area QTGEXITS. */ /* CPF9802 - Not authorized to object QTGEXITS in QUSRSYS. */ /* CPF9801 - Object QTGEXITS in library QUSRSYS not found. */ /*-------------------------------------------------------------------*/ QSYS/CHKOBJ OBJ(&DTALIB/&DTANAME) OBJTYPE(*DTAARA) AUT(*ALL) QSYS/MONMSG MSGID(CPF9802) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('You must have *ALL authority to data area ' *BCAT + &DTALIB *TCAT '/' *CAT &DTANAME) QSYS/ENDDO QSYS/MONMSG MSGID(CPF1022) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('You must have *C authority to library' *CAT &DTALIB) QSYS/ENDDO QSYS/MONMSG MSGID(CPF9801) /*-------------------------------------------------------------------*/ /* Check to ensure we can run ADDEXITPGM/RMVEXITPGM */ /*-------------------------------------------------------------------*/ QSYS/CHKOBJ OBJ(ADDEXITPGM) OBJTYPE(*CMD) AUT(*USE) QSYS/MONMSG MSGID(CPF0000) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('You must have *USE authority to command ADDEXITPGM') QSYS/ENDDO /*-------------------------------------------------------------------*/ /* Check to ensure tgt-lib exists and we have proper authority */ /* CPF9802 - Not authorized to object USEREXIT in QSYS. */ /*-------------------------------------------------------------------*/ QSYS/CHKOBJ OBJ(QSYS/&TOLIB) OBJTYPE(*LIB) AUT(*OBJMGT) QSYS/MONMSG MSGID(CPF9802) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('You must have *OBJMGT authority to library' *BCAT &TOLIB) QSYS/ENDDO QSYS/CHKOBJ OBJ(QSYS/&TOLIB) OBJTYPE(*LIB) AUT(*CHANGE) QSYS/MONMSG MSGID(CPF9802) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('You must have *CHANGE authority to library' *BCAT &TOLIB) QSYS/ENDDO /*-------------------------------------------------------------------*/ /* Create the data area pointing the exit prgms to the access list. */ /* Make data area big enough in case want to add *DEBUG later. */ /* CPF1023 - Data area QTGEXITS exists in QUSRSYS. */ /*-------------------------------------------------------------------*/ QSYS/CRTDTAARA DTAARA(&DTALIB/&DTANAME) TYPE(*CHAR) LEN(50) + VALUE(&TOLIB *TCAT '/' *CAT &TOFILE *TCAT ' *MSGQ *FILE') + TEXT('TELNET Access List Pointer') AUT(*EXCLUDE) QSYS/MONMSG MSGID(CPF1023) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('You must run TELDLT clean-up before you can run TELCRT!') QSYS/ENDDO /*-------------------------------------------------------------------*/ /* Allow TELNET server QTCP profile access to the data area. */ /*-------------------------------------------------------------------*/ QSYS/GRTOBJAUT OBJ(&DTALIB/&DTANAME) OBJTYPE(*DTAARA) USER(QTCP) + AUT(*USE) REPLACE(*YES) /*-------------------------------------------------------------------*/ /* Check to ensure tgt-lib exists and we have proper authority */ /*-------------------------------------------------------------------*/ QSYS/CHKOBJ OBJ(QSYS/&TOLIB) OBJTYPE(*LIB) AUT(*OBJMGT) QSYS/MONMSG MSGID(CPF9801) EXEC(QSYS/DO) QSYS/CRTLIB LIB(&TOLIB) TYPE(*PROD) AUT(*EXCLUDE) QSYS/ENDDO QSYS/MONMSG MSGID(CPF0000) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('You must have *OBJMGT authority to library' *BCAT &TOLIB) QSYS/ENDDO /*-------------------------------------------------------------------*/ /* Give TELNET user QTCP profile access to library */ /*-------------------------------------------------------------------*/ QSYS/GRTOBJAUT OBJ(QSYS/&TOLIB) OBJTYPE(*LIB) USER(QTCP) AUT(*CHANGE) QSYS/MONMSG MSGID(CPF0000) /*-------------------------------------------------------------------*/ /* Check that all source file member exists before we begin */ /*-------------------------------------------------------------------*/ QSYS/CHKOBJ OBJ(&FROMLIB/&FROMFILE) MBR(&ALLOW) OBJTYPE(*FILE) QSYS/MONMSG MSGID(CPF9815) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('Missing member' *BCAT &ALLOW *BCAT 'in' *BCAT &FROM) QSYS/ENDDO QSYS/CHKOBJ OBJ(&FROMLIB/&FROMFILE) MBR(&DISALLOW) OBJTYPE(*FILE) QSYS/MONMSG MSGID(CPF9815) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('Missing member' *BCAT &DISALLOW *BCAT 'in' *BCAT &FROM) QSYS/ENDDO QSYS/CHKOBJ OBJ(&FROMLIB/&FROMFILE) MBR(&MAP) OBJTYPE(*FILE) QSYS/MONMSG MSGID(CPF9815) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('Missing member' *BCAT &MAP *BCAT 'in' *BCAT &FROM) QSYS/ENDDO QSYS/CHKOBJ OBJ(&FROMLIB/&FROMFILE) MBR(&INITPGM1) OBJTYPE(*FILE) QSYS/MONMSG MSGID(CPF9815) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('Missing member' *BCAT &INITPGM1 *BCAT 'in' *BCAT &FROM) QSYS/ENDDO QSYS/CHKOBJ OBJ(&FROMLIB/&FROMFILE) MBR(&INITPGM2) OBJTYPE(*FILE) QSYS/MONMSG MSGID(CPF9815) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('Missing member' *BCAT &INITPGM2 *BCAT 'in' *BCAT &FROM) QSYS/ENDDO QSYS/CHKOBJ OBJ(&FROMLIB/&FROMFILE) MBR(&TERMPGM) OBJTYPE(*FILE) QSYS/MONMSG MSGID(CPF9815) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('Missing member' *BCAT &TERMPGM *BCAT 'in' *BCAT &FROM) QSYS/ENDDO /*-------------------------------------------------------------------*/ /* Create either a physical file (PF) or source physical file */ /* (SRCPF) to hold access lists. The library is your choice. We */ /* must grant user profile QTCP at least *CHANGE authority to */ /* this file, but if you desire logging of debug records, */ /* then QTCP will need *OBJMGT authority. */ /* Note: Need CCSID 37 to ensure code point integrity (square */ /* brackets, namely) are preserved in the 'C' source code. */ /*-------------------------------------------------------------------*/ QSYS/CRTSRCPF FILE(&TOLIB/&TOFILE) RCDLEN(240) MBR(*NONE) + MAXMBRS(*NOMAX) AUT(*EXCLUDE) CCSID(37) + TEXT('TELNET Access Lists & Logs') /*-------------------------------------------------------------------*/ /* Ignore any errors if the source physical file already exists */ /* CPF7302 - File &1 not created in library &2. */ /*-------------------------------------------------------------------*/ QSYS/MONMSG MSGID(CPF7302) /*-------------------------------------------------------------------*/ /* Access Lists and Logs (TXT type) */ /*-------------------------------------------------------------------*/ QSYS/ADDPFM FILE(&TOLIB/&TOFILE) MBR(&ALLOW) SRCTYPE(TXT) + TEXT('Allowed IP Addresses (Basic exit prgm)') QSYS/ADDPFM FILE(&TOLIB/&TOFILE) MBR(&DISALLOW) SRCTYPE(TXT) + TEXT('Dis-allowed IP Addresses') QSYS/ADDPFM FILE(&TOLIB/&TOFILE) MBR(&MAP) SRCTYPE(TXT) + TEXT('Mapped IP Addresses (Advanced exit prgm)') QSYS/ADDPFM FILE(&TOLIB/&TOFILE) MBR(&LOG) SRCTYPE(TXT) + TEXT('TELNET Connect/Disconnect Access Log') QSYS/ADDPFM FILE(&TOLIB/&TOFILE) MBR(&DEBUG) SRCTYPE(TXT) + TEXT('Debugging Flight Recorder') /*-------------------------------------------------------------------*/ /* 'C' Source Code (C type) */ /*-------------------------------------------------------------------*/ QSYS/ADDPFM FILE(&TOLIB/&TOFILE) MBR(&INITPGM1) SRCTYPE(C) + TEXT('Basic exit prgm ALLOW/DISALLOW (INIT0100)') QSYS/ADDPFM FILE(&TOLIB/&TOFILE) MBR(&INITPGM2) SRCTYPE(C) + TEXT(Advanced exit prgm MAP/DISALLOW (INIT0100)') QSYS/ADDPFM FILE(&TOLIB/&TOFILE) MBR(&TERMPGM) SRCTYPE(C) + TEXT('Common Exit (TERM0100)') /*-------------------------------------------------------------------*/ /* Give TELNET user QTCP profile access to file */ /* *CHANGE lets TELNET profile QTCP add records to the members */ /* *OBJMGT lets TELNET profile QTCP add members to the database file */ /*-------------------------------------------------------------------*/ QSYS/GRTOBJAUT OBJ(&TOLIB/&TOFILE) OBJTYPE(*FILE) USER(QTCP) + AUT(*CHANGE) QSYS/GRTOBJAUT OBJ(&TOLIB/&TOFILE) OBJTYPE(*FILE) USER(QTCP) + AUT(*OBJMGT) QSYS/MONMSG MSGID(CPF0000) /*-------------------------------------------------------------------*/ /* Copy the program source code */ /*-------------------------------------------------------------------*/ QSYS/CPYF FROMFILE(&FROMLIB/&FROMFILE) TOFILE(&TOLIB/&TOFILE) + FROMMBR(&INITPGM1) TOMBR(&INITPGM1) MBROPT(*REPLACE) CRTFILE(*YES) + FMTOPT(*NOCHK) QSYS/CPYF FROMFILE(&FROMLIB/&FROMFILE) TOFILE(&TOLIB/&TOFILE) + FROMMBR(&INITPGM2) TOMBR(&INITPGM2) MBROPT(*REPLACE) CRTFILE(*YES) + FMTOPT(*NOCHK) QSYS/CPYF FROMFILE(&FROMLIB/&FROMFILE) TOFILE(&TOLIB/&TOFILE) + FROMMBR(&TERMPGM) TOMBR(&TERMPGM) MBROPT(*REPLACE) CRTFILE(*YES) + FMTOPT(*NOCHK) /*-------------------------------------------------------------------*/ /* Copy the access lists */ /*-------------------------------------------------------------------*/ QSYS/CPYF FROMFILE(&FROMLIB/&FROMFILE) TOFILE(&TOLIB/&TOFILE) + FROMMBR(&ALLOW) TOMBR(&ALLOW) MBROPT(*REPLACE) CRTFILE(*YES) + FMTOPT(*NOCHK) QSYS/CPYF FROMFILE(&FROMLIB/&FROMFILE) TOFILE(&TOLIB/&TOFILE) + FROMMBR(&DISALLOW) TOMBR(&DISALLOW) MBROPT(*REPLACE) CRTFILE(*YES) + FMTOPT(*NOCHK) QSYS/CPYF FROMFILE(&FROMLIB/&FROMFILE) TOFILE(&TOLIB/&TOFILE) + FROMMBR(&MAP) TOMBR(&MAP) MBROPT(*REPLACE) CRTFILE(*YES) + FMTOPT(*NOCHK) /*-------------------------------------------------------------------*/ /* Compile source programs */ /*-------------------------------------------------------------------*/ QSYS/CRTBNDC PGM(&TOLIB/&INITPGM1) SRCFILE(&TOLIB/&TOFILE) + LANGLVL(*EXTENDED) SYSIFCOPT(*NOIFSIO) REPLACE(*YES) USRPRF(*USER) + AUT(*USE) SYSINC(*YES) TEXT('TELNET QIBM_QTG_DEVINIT User Exit') QSYS/CRTBNDC PGM(&TOLIB/&INITPGM2) SRCFILE(&TOLIB/&TOFILE) + LANGLVL(*EXTENDED) SYSIFCOPT(*NOIFSIO) REPLACE(*YES) USRPRF(*USER) + AUT(*USE) SYSINC(*YES) TEXT('TELNET QIBM_QTG_DEVINIT User Exit') QSYS/CRTBNDC PGM(&TOLIB/&TERMPGM) SRCFILE(&TOLIB/&TOFILE) + LANGLVL(*EXTENDED) SYSIFCOPT(*NOIFSIO) REPLACE(*YES) USRPRF(*USER) + AUT(*USE) SYSINC(*YES) TEXT('TELNET QIBM_QTG_DEVTERM User Exit') /*-------------------------------------------------------------------*/ /* De-register any existing exit programs to ensure success */ /*-------------------------------------------------------------------*/ QSYS/RMVEXITPGM EXITPNT(QIBM_QTG_DEVINIT) FORMAT(INIT0100) PGMNBR(1) QSYS/MONMSG MSGID(CPF0000) QSYS/RMVEXITPGM EXITPNT(QIBM_QTG_DEVTERM) FORMAT(TERM0100) PGMNBR(1) QSYS/MONMSG MSGID(CPF0000) /*-------------------------------------------------------------------*/ /* De-register any existing exit programs to ensure success */ /*-------------------------------------------------------------------*/ QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*INFO) + MSGDTA('You can delete file' *BCAT &FROM *BCAT + 'as it is no longer needed. Files installed successfully') /*-------------------------------------------------------------------*/ /* Register the compiled programs to the TELNET exit points */ /*-------------------------------------------------------------------*/ QSYS/IF (&TYPE = '2') THEN(QSYS/DO) /*-----------------------------------------------------------------*/ /* Register the Advanced TELNET exit program */ /*-----------------------------------------------------------------*/ QSYS/ADDEXITPGM EXITPNT(QIBM_QTG_DEVINIT) FORMAT(INIT0100) + PGMNBR(1) PGM(&TOLIB/&INITPGM2) REPLACE(*NO) CRTEXITPNT(*NO) + TEXT('TELNET Client Access Control') QSYS/ADDEXITPGM EXITPNT(QIBM_QTG_DEVTERM) FORMAT(TERM0100) + PGMNBR(1) PGM(&TOLIB/&TERMPGM) REPLACE(*NO) CRTEXITPNT(*NO) + TEXT('TELNET Client Access Control') QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*INFO) + MSGDTA('Use SEU to modify access lists' *BCAT + &MAP *BCAT 'and' *BCAT &DISALLOW *BCAT + 'in file' *BCAT &TOLIB *TCAT '/' *CAT &TOFILE) QSYS/ENDDO /* End of processing for type '2' */ QSYS/IF (&TYPE = '1') THEN(QSYS/DO) /*-----------------------------------------------------------------*/ /* Register the Basic exit program (make this the default) */ /*-----------------------------------------------------------------*/ QSYS/ADDEXITPGM EXITPNT(QIBM_QTG_DEVINIT) FORMAT(INIT0100) + PGMNBR(1) PGM(&TOLIB/&INITPGM1) REPLACE(*NO) CRTEXITPNT(*NO) + TEXT('TELNET Client Access Control') QSYS/ADDEXITPGM EXITPNT(QIBM_QTG_DEVTERM) FORMAT(TERM0100) + PGMNBR(1) PGM(&TOLIB/&TERMPGM) REPLACE(*NO) CRTEXITPNT(*NO) + TEXT('TELNET Client Access Control') QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*INFO) + MSGDTA('Use SEU to modify access lists' *BCAT + &ALLOW *BCAT 'and' *BCAT &DISALLOW *BCAT + 'in file' *BCAT &TOLIB *TCAT '/' *CAT &TOFILE) QSYS/ENDDO /* End of processing for type '1' */ QSYS/RETURN /* Normal end of CL program */ /*---------------------------------------------------------------------*/ /* Error handling */ /*---------------------------------------------------------------------*/ STDERR1: QSYS/IF &ERRORSW THEN(QSYS/DO) /* Function check requested? */ QSYS/SNDPGMMSG MSGID(CPF9999) MSGF(QCPFMSG) + MSGTYPE(*ESCAPE) /* Force a function check to abort */ QSYS/CHGVAR &ERRORSW '1' /* Set flag to do a function check */ QSYS/ENDDO /* End of processing for &ERRORSW */ STDERR2: QSYS/RCVMSG MSGTYPE(*DIAG) MSGDTA(&MSGDTA) MSGID(&MSGID) + MSGF(&MSGF) MSGFLIB(&MSGFLIB) QSYS/IF (&MSGID *EQ ' ') QSYS/GOTO STDERR3 QSYS/SNDPGMMSG MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) MSGDTA(&MSGDTA) + MSGTYPE(*DIAG) QSYS/GOTO STDERR2 /* Loop back for more diagnostics */ STDERR3: QSYS/RCVMSG MSGTYPE(*EXCP) MSGDTA(&MSGDTA) MSGID(&MSGID) MSGF(&MSGF) + MSGFLIB(&MSGFLIB) QSYS/SNDPGMMSG MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) MSGDTA(&MSGDTA) + MSGTYPE(*ESCAPE) ENDPGM
Return to the TELNET exit program road map:
This is a working example of code for the program that deletes TELNET exit programs from your AS/400.
Return to the TELNET exit program road map:
/*---------------------------------------------------------------------*/ /* Name: */ /* TELDLT - Delete the files and programs created by TELCRT */ /* */ /* Function: */ /* Deletes the program and objects that were created by the TELCRT */ /* installation program. */ /* */ /* Parameters: */ /* None. */ /* */ /* Invocation: */ /* CALL PGM(TELDLT) */ /* */ /* Authorities Required: */ /* You must have *CHANGE authority to library QUSRSYS, and *USE */ /* authority to the RMVEXITPGM command. You must also have *CHANGE */ /* and *OBJMGT authorities to tgtlib (target library) to delete any */ /* authorities granted user profile QTCP. */ /*---------------------------------------------------------------------*/ PGM /*-------------------------------------------------------------------*/ /* The parameter DLTLIB should be a target library in which to */ /* create our objects, and should be PUBLIC(*EXCLUDE) to protect */ /* the access lists. This library should also be in the *LIBL of */ /* the caller running this program. */ /*-------------------------------------------------------------------*/ QSYS/DCL VAR(&DLTFILE) TYPE(*CHAR) LEN(50) /* Size of DTAARA */ QSYS/DCL VAR(&DLTLIB) TYPE(*CHAR) LEN(10) QSYS/DCL VAR(&DLTNAME) TYPE(*CHAR) LEN(10) /*-------------------------------------------------------------------*/ /* Variables for Convert Case API call */ /*-------------------------------------------------------------------*/ QSYS/DCL VAR(&CNVCSCB) TYPE(*CHAR) LEN(22) /*-------------------------------------------------------------------*/ /* Variables for parsing of 'lib/name' strings */ /*-------------------------------------------------------------------*/ QSYS/DCL VAR(&STRING) TYPE(*CHAR) LEN(21) QSYS/DCL VAR(&LIB) TYPE(*CHAR) LEN(10) QSYS/DCL VAR(&NAME) TYPE(*CHAR) LEN(10) QSYS/DCL VAR(&SLASHPOS) TYPE(*DEC) LEN(2 0) VALUE(0) QSYS/DCL VAR(&BLANKPOS) TYPE(*DEC) LEN(2 0) VALUE(0) QSYS/DCL VAR(&START) TYPE(*DEC) LEN(2 0) QSYS/DCL VAR(&LEN) TYPE(*DEC) LEN(2 0) /*-------------------------------------------------------------------*/ /* The following are MEMBERs in the access list file */ /*-------------------------------------------------------------------*/ QSYS/DCL VAR(&ALLOW) TYPE(*CHAR) LEN(10) VALUE(ALLOW) QSYS/DCL VAR(&DISALLOW) TYPE(*CHAR) LEN(10) VALUE(DISALLOW) QSYS/DCL VAR(&MAP) TYPE(*CHAR) LEN(10) VALUE(MAP) QSYS/DCL VAR(&LOG) TYPE(*CHAR) LEN(10) VALUE(LOG) QSYS/DCL VAR(&DEBUG) TYPE(*CHAR) LEN(10) VALUE(DEBUG) /*-------------------------------------------------------------------*/ /* The following is the data area (points exit prgms to access list) */ /*-------------------------------------------------------------------*/ QSYS/DCL VAR(&DTALIB) TYPE(*CHAR) LEN(10) VALUE(QUSRSYS) QSYS/DCL VAR(&DTANAME) TYPE(*CHAR) LEN(10) VALUE(QTGEXITS) /*-------------------------------------------------------------------*/ /* These are the names of the TELNET exit programs. There are two */ /* TELNET Exit points, and a separate program is needed for each */ /* point. These programs need to be registered with ADDEXITPGM */ /* before they start running. */ /*-------------------------------------------------------------------*/ QSYS/DCL VAR(&INITPGM1) TYPE(*CHAR) LEN(10) VALUE(DEVINIT1) QSYS/DCL VAR(&INITPGM2) TYPE(*CHAR) LEN(10) VALUE(DEVINIT2) QSYS/DCL VAR(&TERMPGM) TYPE(*CHAR) LEN(10) VALUE(DEVTERM) /*-------------------------------------------------------------------*/ /* Monitor for all escape messages. */ /*-------------------------------------------------------------------*/ QSYS/MONMSG MSGID(CPF0000) /*-------------------------------------------------------------------*/ /* Check to ensure we can delete the data area in library QUSRSYS */ /*-------------------------------------------------------------------*/ QSYS/CHKOBJ OBJ(QSYS/&DTALIB) OBJTYPE(*LIB) AUT(*CHANGE) QSYS/MONMSG MSGID(CPF9802) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('You must have *CHANGE authority to library' *CAT &DTALIB) QSYS/ENDDO /*-------------------------------------------------------------------*/ /* Check authority to any existing data area object */ /* CPF9802 - Not authorized to object QTGEXITS in QUSRSYS. */ /* CPF9801 - Object QTGEXITS in library QUSRSYS not found. */ /*-------------------------------------------------------------------*/ QSYS/CHKOBJ OBJ(&DTALIB/&DTANAME) OBJTYPE(*DTAARA) AUT(*ALL) QSYS/MONMSG MSGID(CPF9802) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('You must have *ALL authority to data area' *CAT + &DTALIB *TCAT '/' *CAT &DTANAME) QSYS/ENDDO QSYS/MONMSG MSGID(CPF9801) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('Data area' *BCAT &DTALIB *TCAT '/' *CAT &DTANAME *BCAT + 'not found - perhaps TELNET exit programs are not installed?') QSYS/ENDDO /*-------------------------------------------------------------------*/ /* Check to ensure we can run ADDEXITPGM/RMVEXITPGM */ /*-------------------------------------------------------------------*/ QSYS/CHKOBJ OBJ(RMVEXITPGM) OBJTYPE(*CMD) AUT(*USE) QSYS/MONMSG MSGID(CPF0000) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('You must have *USE authority to command RMVEXITPGM') QSYS/ENDDO /*-------------------------------------------------------------------*/ /* Get the name of the mapping file out of the data area */ /*-------------------------------------------------------------------*/ QSYS/RTVDTAARA DTAARA(&DTALIB/&DTANAME) RTNVAR(&DLTFILE) QSYS/CHGVAR VAR(&DLTFILE) VALUE(%SST(&DLTFILE 1 22)) /*-------------------------------------------------------------------*/ /* In case the name is smaller than 21 characters, search for the */ /* first blank and parse out name at first blank */ /*-------------------------------------------------------------------*/ QSYS/CHGVAR VAR(&STRING) VALUE(&DLTFILE) QSYS/CHGVAR VAR(&BLANKPOS) VALUE(0) LOOP: QSYS/CHGVAR &BLANKPOS (&BLANKPOS + 1) QSYS/IF (&BLANKPOS *EQ 22) QSYS/GOTO DONE QSYS/IF (%SST(&STRING &BLANKPOS 1) *EQ ' ' ) QSYS/GOTO DONE QSYS/GOTO LOOP1 DONE: QSYS/CHGVAR VAR(&DLTFILE) VALUE(%SST(&DLTFILE 1 &BLANKPOS)) /*-------------------------------------------------------------------*/ /* Upper case the arguments with the QLGCNVCS API */ /*-------------------------------------------------------------------*/ QSYS/CHGVAR VAR(%BIN(&CNVCSCB 1 4)) VALUE(1) /* CCSID format */ QSYS/CHGVAR VAR(%BIN(&CNVCSCB 5 4)) VALUE(0) /* Job CCSID */ QSYS/CHGVAR VAR(%BIN(&CNVCSCB 9 4)) VALUE(0) /* Upper Case */ QSYS/CHGVAR VAR(%SST(&CNVCSCB 13 10)) VALUE(X'00000000000000000000') QSYS/CALL PGM(QSYS/QLGCNVCS) + PARM(&CNVCSCB &DLTFILE &DLTFILE X'00000015' X'00000000') /*-------------------------------------------------------------------*/ /* Set the variable with our first argument and parse. We could */ /* also use the QCLSCAN API but would take a performance hit. */ /*-------------------------------------------------------------------*/ QSYS/CHGVAR VAR(&BLANKPOS) VALUE(0) QSYS/CHGVAR VAR(&STRING) VALUE(&DLTFILE) LOOP1: QSYS/CHGVAR &BLANKPOS (&BLANKPOS + 1) QSYS/IF (&BLANKPOS *EQ 22) QSYS/GOTO DONE1 QSYS/IF (%SST(&STRING &BLANKPOS 1) *EQ ' ' ) QSYS/GOTO DONE1 QSYS/IF ((&SLASHPOS *EQ 0) *AND (%SST(&STRING &BLANKPOS 1) *EQ '/')) + QSYS/CHGVAR VAR(&SLASHPOS) VALUE(&BLANKPOS) QSYS/GOTO LOOP1 DONE1: QSYS/IF (&SLASHPOS *EQ 0) THEN(QSYS/DO) QSYS/SNDPGMMSG MSG('A library is required for the srclib/srcname.') QSYS/RETURN QSYS/ENDDO QSYS/ELSE CMD(QSYS/DO) QSYS/CHGVAR VAR(&LEN) VALUE(&SLASHPOS - 1) QSYS/CHGVAR VAR(&LIB) VALUE(%SST(&STRING 1 &LEN)) QSYS/CHGVAR VAR(&START) VALUE(&SLASHPOS + 1) QSYS/CHGVAR VAR(&LEN) VALUE(&BLANKPOS - &START) QSYS/CHGVAR VAR(&NAME) VALUE(%SST(&STRING &START &LEN)) QSYS/ENDDO /*-------------------------------------------------------------------*/ /* Set the results into our working variables */ /*-------------------------------------------------------------------*/ QSYS/CHGVAR VAR(&DLTLIB) VALUE(&LIB) QSYS/CHGVAR VAR(&DLTNAME) VALUE(&NAME) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*INFO) + MSGDTA('Deleting file' *BCAT &DLTLIB *TCAT '/' *CAT &DLTNAME) /*-------------------------------------------------------------------*/ /* Check to ensure dltlib exists and we have proper authority */ /*-------------------------------------------------------------------*/ QSYS/CHKOBJ OBJ(QSYS/&DLTLIB) OBJTYPE(*LIB) AUT(*OBJMGT) QSYS/MONMSG MSGID(CPF9801) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('Library' *BCAT &DLTLIB *BCAT 'does not exist') QSYS/ENDDO QSYS/MONMSG MSGID(CPF0000) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('You must have *OBJMGT authority to library' *BCAT &DLTLIB) QSYS/ENDDO QSYS/CHKOBJ OBJ(QSYS/&DLTLIB) OBJTYPE(*LIB) AUT(*CHANGE) QSYS/MONMSG MSGID(CPF9802) EXEC(QSYS/DO) QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) + MSGDTA('You must have *CHANGE authority to library' *BCAT &DLTLIB) QSYS/ENDDO /*-------------------------------------------------------------------*/ /* De-register the exit programs */ /*-------------------------------------------------------------------*/ QSYS/RMVEXITPGM EXITPNT(QIBM_QTG_DEVINIT) FORMAT(INIT0100) PGMNBR(1) QSYS/MONMSG MSGID(CPF0000) QSYS/RMVEXITPGM EXITPNT(QIBM_QTG_DEVTERM) FORMAT(TERM0100) PGMNBR(1) QSYS/MONMSG MSGID(CPF0000) /*-------------------------------------------------------------------*/ /* Delete the data area pointing the exit prgms to the access list */ /*-------------------------------------------------------------------*/ DLTDTAARA DTAARA(&DTALIB/&DTANAME) QSYS/MONMSG MSGID(CPF0000) /*-------------------------------------------------------------------*/ /* Delete the TELNET exit programs */ /*-------------------------------------------------------------------*/ QSYS/DLTPGM PGM(&DLTLIB/&INITPGM1) QSYS/MONMSG MSGID(CPF0000) QSYS/DLTPGM PGM(&DLTLIB/&INITPGM2) QSYS/MONMSG MSGID(CPF0000) QSYS/DLTPGM PGM(&DLTLIB/&TERMPGM) QSYS/MONMSG MSGID(CPF0000) /*-------------------------------------------------------------------*/ /* Delete the 'C' source code */ /*-------------------------------------------------------------------*/ QSYS/RMVM FILE(&DLTLIB/&DLTNAME) MBR(&INITPGM1) QSYS/MONMSG MSGID(CPF0000) QSYS/RMVM FILE(&DLTLIB/&DLTNAME) MBR(&INITPGM2) QSYS/MONMSG MSGID(CPF0000) QSYS/RMVM FILE(&DLTLIB/&DLTNAME) MBR(&TERMPGM) QSYS/MONMSG MSGID(CPF0000) /*-------------------------------------------------------------------*/ /* Delete the access lists and logs */ /*-------------------------------------------------------------------*/ QSYS/RMVM FILE(&DLTLIB/&DLTNAME) MBR(&ALLOW) QSYS/MONMSG MSGID(CPF0000) QSYS/RMVM FILE(&DLTLIB/&DLTNAME) MBR(&DISALLOW) QSYS/MONMSG MSGID(CPF0000) QSYS/RMVM FILE(&DLTLIB/&DLTNAME) MBR(&MAP) QSYS/MONMSG MSGID(CPF0000) QSYS/RMVM FILE(&DLTLIB/&DLTNAME) MBR(&LOG) QSYS/MONMSG MSGID(CPF0000) QSYS/RMVM FILE(&DLTLIB/&DLTNAME) MBR(&DEBUG) QSYS/MONMSG MSGID(CPF0000) /*-------------------------------------------------------------------*/ /* If file is empty, delete it */ /*-------------------------------------------------------------------*/ QSYS/CHKOBJ OBJ(&DLTLIB/&DLTNAME) MBR(*FIRST) OBJTYPE(*FILE) QSYS/MONMSG MSGID(CPF9801) EXEC(QSYS/GOTO NOFILE) QSYS/MONMSG MSGID(CPF9815) EXEC(QSYS/DLTF FILE(&DLTLIB/&DLTNAME)) /*-------------------------------------------------------------------*/ /* Remove any authorities that we granted */ /*-------------------------------------------------------------------*/ QSYS/RVKOBJAUT OBJ(QSYS/&DLTLIB) OBJTYPE(*LIB) USER(QTCP) AUT(*ALL) NOFILE: QSYS/SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*INFO) + MSGDTA('Delete library' *BCAT &DLTLIB *BCAT + 'if empty. Use command' *BCAT + 'WRKOBJ OBJ(' *TCAT &DLTLIB *TCAT '/*ALL)' *BCAT + 'to see check if empty, and ' *BCAT + 'DLTLIB LIB(' *CAT &DLTLIB *TCAT ') to delete it') QSYS/RETURN /* Normal end of CL program */ ENDPGM
Return to the TELNET exit program road map:
This is a working example of a TELNET initialization (log in) exit program. It provides basic TELNET user screening and log functions.
Return to the TELNET exit program road map:
/* Module Description *************************************************/ /* */ /**********************************************************************/ /* */ /* DISCLAIMER: This material contains programming source code for */ /* your consideration. These examples have not been thoroughly */ /* tested under all conditions. IBM, therefore, cannot guarantee or */ /* imply reliability, serviceability, performance or function of */ /* these programs. All programs contained herein are provided to you */ /* "AS IS". THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */ /* FOR A PARTICULAR PURPOSE ARE EXPRESSLY DISCLAIMED. */ /* */ /* LIMITATION OF LIABILITY: Neither IBM nor the author shall be */ /* liable for any claims or damages whatsoever, including property */ /* damage, personal injury, intellectual property infringement, loss */ /* of profits, or interruption of business, or for any special, */ /* consequential or incidental damages, however caused, whether */ /* arising out of breach of warranty, contract, tort (including */ /* negligence), strict liability, or otherwise. */ /* */ /**********************************************************************/ /* */ /* Source File Name: devinit1.c */ /* */ /* Module Name: TELNET Device Initialization exit program. */ /* */ /* Source File Description: */ /* */ /* Client access controls are defined via access lists, where */ /* the client IP address determines if a connection is allowed or */ /* dis-allowed. Customers indicate the library/name of the access */ /* list/log *FILE object via character data area QUSRSYS/QTGEXITS. */ /* */ /* Care must be taken to protect the TELNET exit *PGMs and the */ /* access list *FILE from unauthorized users! Failure to protect */ /* these objects using PUBLIC(*EXCLUDE) authorities can compromise */ /* your system! Only user profile QTCP and trusted users should be */ /* be granted access to these objects and the library in which they */ /* reside! */ /* */ /* Data area layout expected: */ /* - required to have library and name of IP address mapping file */ /* as the first string in the *DTAARA, separated by a slash. */ /* - if a connect msg to QTCP *MSGQ is desired, append a *MSGQ */ /* string to the *DTAARA contents. */ /* - if a connect msg to SRCPF or PF member LOG is desired, append */ /* a *FILE string to the *DTAARA contents. */ /* - if debug msgs to SRCPF or PF member DEBUG are desired, append */ /* a *DEBUG string to the *DTAARA contents. */ /* */ /* Example: */ /* 'filelib/filename msgqflag fileflag debugflag' */ /* 'USEREXIT/TELNET *MSGQ *DEBUG' */ /* 'USEREXIT/TELNET *FILE *DEBUG' */ /* 'USEREXIT/TELNET *MSGQ *FILE' */ /* 'USEREXIT/TELNET *MSGQ *FILE *DEBUG' */ /* */ /* For initialization purposes, this program first searches member */ /* ALLOW of the *FILE listed in QUSRSYS/QTGEXITS data area for a */ /* match, and allows the connection if a match is found. If no */ /* match is found, this program then reads member DISALLOW to see */ /* if this particular user should be dis-allowed. If still no */ /* match is found, then this user is allowed to connect as if no */ /* screening occurred. */ /* */ /* End Module Description *********************************************/ #pragma comment (copyright, \ "(C) Copyright IBM Corp. 1997. \ All rights reserved. \ US Government Users Restricted Rights - \ Use, duplication or disclosure restricted \ by GSA ADP Schedule Contract with IBM Corp. \ Licensed Materials - Property of IBM.") #define _DEVINIT1_C /**********************************************************************/ /* All file scoped includes go here */ /**********************************************************************/ /**********************************************************************/ /* Base Operating System Option 13 (QSYSINC) CUE's must be installed */ /**********************************************************************/ #ifndef ETGDEVEX_h #define ETGDEVEX_h /**********************************************************************/ /* Type Definition for the User Description Information */ /**********************************************************************/ typedef _Packed struct Qtg_User_Description { int Length_user_description; char User_profile[10]; char Current_library[10]; char Program_to_call[10]; char Initial_menu[10]; } Qtg_User_Description_t; /**********************************************************************/ /* Structures used by Device Description Information */ /**********************************************************************/ #define QTG_DSPD0100 "DSPD0100" typedef _Packed struct Qtg_DSPD0100 { char Keyboard_identifier[3]; char Reserved[1]; int Code_page; int Char_set; } Qtg_DSPD0100_t; /**********************************************************************/ /* Type Definition for the Device Description Information */ /**********************************************************************/ typedef _Packed struct Qtg_Device_Description { char Device_name[10]; char Device_format[8]; char Reserved[2]; int Offset_to_device_attributes; int Length_device_attributes; Qtg_DSPD0100_t Display_device; } Qtg_Device_Description_t; /**********************************************************************/ /* Type Definition for the Connection Description Information */ /**********************************************************************/ typedef _Packed struct Qtg_Connection_Description { int Length_connection_description; char Internet_address[20]; char Secure_password_valid[1]; char Workstation_type[14]; } Qtg_Connection_Description_t; #endif #include <recio.h> /* _Ropen, _Rwrite, _Rreadf */ #include <xxfdbk.h> /* _Ropnfbk */ #include <stdio.h> /* sprintf, printf, fopen, fread, */ #include <time.h> /* time(), ctime() */ #include <stdlib.h> /* system, atoi, free, malloc, exit, */ #include <string.h> /* strxxx, memxxx */ #include <ctype.h> /* isspace, toupper */ #include <except.h> /* _CNL_Hndlr_Parms_T structure */ #include <signal.h> /* _GetExcData(), signal() */ #include <stdarg.h> /* va_start(), va_arg(), va_end() */ #include <sys/types.h> /* Include for inet_ntoa() */ #include <sys/socket.h> /* Include for inet_ntoa() */ #include <netinet/in.h> /* Include for struct sockaddr_in */ #include <arpa/inet.h> /* Include for inet_ntoa() */ #include <unistd.h> /* File system API's (POSIX) */ #include <etgdevex.h> /* TELNET Device Exit Point programs */ #include <qdcrdevd.h> /* QDCRDEVD - Retrieve Device Desc */ #include <qcapcmd.h> /* QCAPCMD - Process CL commands */ #include <qmhrtvm.h> /* QMHRTVM - retrieve program message*/ #include <qmhsndm.h> /* QMHSNDM - send non-program msg */ #include <qmhchgem.h> /* QMHCHGEM - change exception msg */ #include <qwcrdtaa.h> /* QWCRDTAA() - retrieve data area */ #include <qmhsndpm.h> /* QMHSNDPM - send program message */ /**********************************************************************/ /* Make all API calls library qualified (valid only at V3R7 and up) */ /**********************************************************************/ #pragma map (QDCRDEVD, "QSYS/QDCRDEVD") #pragma map (QCAPCMD, "QSYS/QCAPCMD") #pragma map (QMHRTVM, "QSYS/QMHRTVM") #pragma map (QMHSNDM, "QSYS/QMHSNDM") #pragma map (QMHCHGEM, "QSYS/QMHCHGEM") #pragma map (QWCRDTAA, "QSYS/QWCRDTAA") #pragma map (QMHSNDPM, "QSYS/QMHSNDPM") /**********************************************************************/ /* All file scoped Constants go here */ /**********************************************************************/ const int fTrue = 1; const int fFalse = 0; /**********************************************************************/ /* All file scoped type declarations go here */ /**********************************************************************/ typedef struct _ERRSTRUCT { int Bytes_Provided; int Bytes_Available; char Exception_Id[7]; char Reserved; char Exception_Data[256]; } ERRSTRUCT; typedef ERRSTRUCT *PERRSTRUCT; /**********************************************************************/ /* All file scoped Macro invocations go here */ /**********************************************************************/ #define MAX(a,b) ( ((a) > (b)) ? (a) : (b) ) #define MIN(a,b) ( ((a) < (b)) ? (a) : (b) ) /**********************************************************************/ /* The following describe the IP address mapping file */ /**********************************************************************/ #define MAX_ADDRESSES 1000 #define RECORD_WIDTH 240 /**********************************************************************/ /* Some message API definitions */ /**********************************************************************/ #define MSG_DIAG "*DIAG " #define MSG_ESCAPE "*ESCAPE " #define MSG_INFO "*INFO " #define MSG_INQ "*INQ " #define MSG_COMP "*COMP " #define MSG_NOTIFY "*NOTIFY " #define MSG_RQS "*RQS " #define MSG_STATUS "*STATUS " #define MSQ_Q_CUR_PROG "* " #define QTCPMSG "QTCPMSG *LIBL " /* Stack MSGF */ #define QTCPMSGF "QTCPMSGF *LIBL " /* Apps MSGF */ #define QCPFMSG "QCPFMSG *LIBL " /* Most CPFxxxx */ #define QCEEMSG "QCEEMSG *LIBL " /* CEE9901 */ #define QC2MSGF "QC2MSGF *LIBL " /* ILE Signals */ /**********************************************************************/ /* All internal function prototypes go here */ /**********************************************************************/ /**********************************************************************/ /* If the fDebug flag is not true, then the buffer() and record() */ /* functions will not write anything to standard out. */ /**********************************************************************/ static void buffer(char *Buffer, int Length); static void record(char *Format, ...); void handler(int iSignal); static void cl_command(char *Command); static int Pad(char *pszString, int iLength); static int Trim(char *pszString, int iLength); /**********************************************************************/ /* All file scoped variable declarations go here */ /**********************************************************************/ static int fRemoveEscapeMsg; /* True if you want to hide exceptions */ static int fException; /* Set true only inside excp handler */ static int fDebug; /* True means to log records to DEBUG */ static int fLogMsg; /* True means send msg to QTCP msgq */ static int fLogFile; /* True means log msg to SRCPF or PF */ static char acMsg[256]; /* for QMHSNDM API call */ static char acMsgKey[4]; /* for QMHSNDM API call */ static char acMsgReply[20]; /* for QMHSNDM API call */ /* Function Specification *********************************************/ /* */ /* Function Name: Main */ /* */ /* Descriptive Name: TELNET Device Initiailization sample program. */ /* */ /* This exit program provides control over device selection and */ /* automatic sign-on for TELNET clients. It makes use of mapping */ /* table ALLOW to determine whether this client should be allowed */ /* to connect. */ /* */ /* If no match is found, then table DISALLOW is searched for a */ /* match. If a match is found, then this client is denied. If */ /* no match is found, then this client is allowed to connect as if */ /* no screening occurred. */ /* */ /* A message is logged to the QTCP user profile message queue, */ /* provided the QUSRSYS/QTGEXITS shows a "*MSGQ" string. */ /* */ /* A message is logged to member LOG in the *FILE object, */ /* provided the QUSRSYS/QTGEXITS shows a "*FILE" string. */ /* */ /* Notes: The "argv[]" parameters are "char *" by definition. */ /* Reference integers as "*(int(argv[1]))", for example. */ /* Consider the method for passing them back to the caller. */ /* */ /* Dependencies: */ /* */ /* TELNET Server exit point QIBM_QTG_DEVINIT format */ /* INIT0100 was registered during TCP/IP LPP installation. */ /* */ /* You must grant user profile QTCP authorities to all objects */ /* and libraries you create or require here! */ /* */ /* Input: */ /* */ /* char * argv[1] - User description information (I/O) */ /* char * argv[2] - Device description information (I/O) */ /* char * argv[3] - Connection description information (Input) */ /* char * argv[4] - Environment options string (Input) */ /* int * argv[5] - Length of environment options (Input) */ /* char * argv[6] - Allow connect '0'=No,'1'=Yes (Output) */ /* char * argv[7] - Allow autosignon '0'=No,'1'=Yes (Output) */ /* */ /* Exit Normal: Return AllowSignon and/or AllowAutoSignon value to */ /* server application, optionally override Device, KBD, */ /* CP, CS, User Profile, Program, Current Library and */ /* Menu values. */ /* Adds an timestamp access record to member LOG. */ /* Sends a message to the QTCP profile *MSGQ. */ /* */ /* Exit Error: None */ /* */ /* End Function Specification *****************************************/ void main(int argc, char *argv[]) { /********************************************************************/ /* Local Variables */ /********************************************************************/ ERRSTRUCT esErrCode; struct sockaddr_in *pSockAddr; /* Network address template */ char *pClientIP; /* Output of inet_ntoa() */ int iClientPort; /* Up to 5 digits possible */ int iParms; /* # required parameters */ int iMin; /* Working variable */ int i; /* Working variable */ int iMatch; /* Found match in file */ struct { /* IP address compare struct */ char C1[4]; /* Client IP address */ char C2[4]; char C3[4]; char C4[4]; char M1[4]; /* Mapping table IP address */ char M2[4]; char M3[4]; char M4[4]; } IP; struct { /* Mapping table layout */ char ClientIP[15 + 1]; /* Client IP address to match*/ } Map[MAX_ADDRESSES]; /* Up to 1000 entries */ Qtg_User_Description_t *pUser; /* From etgdevex.h */ Qtg_DSPD0100_t *pDisplay; /* From etgdevex.h */ Qtg_Device_Description_t *pDevice; /* From etgdevex.h */ Qtg_Connection_Description_t *pConnection; /* From etgdevex.h */ int iEnv; /* Length RFC 1572 variables */ char *pEnv; /* Actual RFC 1572 variables */ char *pAllowConnect; /* Let this client connect? */ char *pAllowAutoSignon; /* Bypass sign-on panel? */ /********************************************************************/ /* Data area layout expected: */ /* - required to have library and name of IP address mapping file */ /* - if a connect msg is desired to QTCP msgq, add *MSG string */ /* - if debug msgs are desired to DEBUG member, add *DEBUG string */ /* */ /* Example: */ /* 'filelib/filename msgqflag debugflag' */ /* 'USEREXIT/TELNET *MSGQ *DEBUG' */ /********************************************************************/ char *pszDtaara = "QTGEXITS QUSRSYS "; char *pszDataArea = "QUSRSYS/QTGEXITS"; /* Where are pgms/logs */ char *pszAllow = "ALLOW"; /* Allowed IP addresses */ char *pszDisallow = "DISALLOW"; /* Disallowed IP addresses */ char *pszDebug = "DEBUG"; /* Debug log file member */ char *pszLog = "LOG"; /* Access log file member */ char pcRecord[RECORD_WIDTH]; /* Buffer for reads */ char pcOpenFile[RECORD_WIDTH]; /* _Ropen() file name */ char pcOpenParms[RECORD_WIDTH]; /* _Ropen() attributes */ _XXOPFB_T *pOFdBk; /* Obtain Open feedback info */ _RIOFB_T *pFdBk; /* I/O feedback area */ _RFILE *pFile; /* Mapping file pointer */ FILE *pDebug; /* Debug file pointer */ FILE *pLog; /* Log file pointer */ int iOffset; /* Varies for SRCPF vs. PF */ time_t ltime; /* time() call */ char Time[24+1]; /* For msg sent to QTCP */ typedef struct _Dtaara { /******************************************************************/ /* typedef _Packed struct Qwc_Rdtaa_Data_Returned { */ /* int Bytes_Available; */ /* int Bytes_Returned; */ /* char Type_Value_Returned[10]; */ /* char Library_Name[10]; */ /* int Length_Value_Returned; */ /* int Number_Decimal_Positions; */ /* char Value[]; commented out */ /* } Qwc_Rdtaa_Data_Returned_t; */ /******************************************************************/ Qwc_Rdtaa_Data_Returned_t returned; char contents[257]; } Dtaara_t; Dtaara_t dtaara; /********************************************************************/ /* Initialize flags */ /********************************************************************/ fException = fFalse; /* Exception was trapped by signal() */ fRemoveEscapeMsg = fFalse; /* Remove exception msg from joblog */ fDebug = fFalse; /* Log debug msgs to DEBUG member */ fLogMsg = fFalse; /* Log connect msg to QTCP msg queue */ fLogFile = fFalse; /* Log connect msg to SRCPF or PF */ signal(SIGALL, &handler); /* Trap all signals with our handler */ /********************************************************************/ /* Read fixed data area to see if debug logging is active. We */ /* force exceptions to be signalled so we know if the data area */ /* exists or not by checking the fException flag. */ /********************************************************************/ memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = 0L; /* Force exception to be signalled */ memset(&dtaara, 0x00, sizeof(dtaara)); dtaara.returned.Bytes_Available = sizeof(dtaara); fException = fFalse; /* Initialize - reset in handler */ fRemoveEscapeMsg = fTrue; /* Initialize - want to hide errors */ QWCRDTAA(&dtaara, sizeof(dtaara), pszDtaara, -1, sizeof(dtaara.contents) - 1, &esErrCode); fRemoveEscapeMsg = fFalse; /* Reset - want to see errors */ /********************************************************************/ /* If data area found and contents exist, process contents */ /********************************************************************/ if (!fException && dtaara.returned.Bytes_Returned) { if (NULL != strstr(dtaara.contents, "*MSGQ")) { /****************************************************************/ /* If find *MSGQ, means log connect msg to QTCP msg queue */ /****************************************************************/ fLogMsg = fTrue; } /* endif */ if (NULL != strstr(dtaara.contents, "*FILE")) { /****************************************************************/ /* If find *FILE, means log connect msg to SRCPF or PF */ /****************************************************************/ fLogFile = fTrue; } /* endif */ if (NULL != strstr(dtaara.contents, "*DEBUG")) { /****************************************************************/ /* If find *DEBUG, log flight records to map file member DEBUG */ /****************************************************************/ fDebug = fTrue; } /* endif */ /******************************************************************/ /* Map file is supposed to be the first string in the data area */ /******************************************************************/ pszDataArea = strtok(dtaara.contents, " "); } /* endif */ if (fDebug) { /******************************************************************/ /* record() output goes to standard out */ /******************************************************************/ sprintf(pcOpenFile, "%s(%s)", pszDataArea, pszDebug); sprintf(pcOpenParms, "a+, lrecl=%d, recfm=v", RECORD_WIDTH); pDebug = freopen(pcOpenFile, pcOpenParms, stdout); } /* endif */ record("\n"); record("devinit1: >>>>> entry\n"); record("devinit1: Signals -> handler\n"); iParms = 7; /* Total of 7 parms on the interface */ record("devinit1: argc: %d iParms: %d\n", argc, iParms); if ((argc-1) != iParms) { record("devinit1: Invalid number of parameters!\n"); /******************************************************************/ /* TCP7117 - Program &1 in library &2 received an invalid number */ /* parameters. */ /******************************************************************/ sprintf(acMsg, "DEVINIT1 *LIBL "); iMin = strlen(acMsg); memcpy(&acMsg[iMin], &iParms, sizeof(int)); iMin += sizeof(int); QMHSNDPM("TCP7117", QTCPMSGF, acMsg, iMin, MSG_INFO, MSQ_Q_CUR_PROG, 0, acMsgKey, &esErrCode); record("devinit1: <<<<< exit\n\n"); exit(0); } /* endif */ /********************************************************************/ /* Set input structure pointers */ /********************************************************************/ record("devinit1: Setting pointers...\n"); pUser = (Qtg_User_Description_t *)argv[1]; pDevice = (Qtg_Device_Description_t *)argv[2]; pDisplay = (Qtg_DSPD0100_t *) ((char *)pDevice + pDevice->Offset_to_device_attributes); pConnection = (Qtg_Connection_Description_t *)argv[3]; pEnv = argv[4]; iEnv = *((int *)(argv[5])); pAllowConnect = argv[6]; pAllowAutoSignon = argv[7]; /********************************************************************/ /* Dump the parameter values for inspection (debug) */ /********************************************************************/ record("devinit1: argv[1]: pUser:\n"); iMin = MIN(50, pUser->Length_user_description); buffer((char *)pUser, iMin); record("devinit1: argv[2]: pDevice:\n"); iMin = MIN(100, (sizeof(Qtg_Device_Description_t) + pDevice->Length_device_attributes)); buffer((char *)pDevice, iMin); record("devinit1: argv[3]: pConnection:\n"); iMin = MIN(100, pConnection->Length_connection_description); buffer((char *)pConnection, iMin); record("devinit2: argv[3]: pConnection->Secure_password_valid: '%c'\n", pConnection->Secure_password_valid); record("devinit2: argv[3]: pConnection->Workstation_type: >%.14s<\n", pConnection->Workstation_type); record("devinit1: argv[5]: iEnv: %d\n", iEnv); if (0 < iEnv) { record("devinit1: argv[4]: pEnv[%d]:\n", iEnv); iMin = MIN(1024, iEnv); buffer(pEnv, iMin); } else { record("devinit1: argv[4]: pEnv[NULL]:\n"); } /* endelse */ record("devinit1: argv[6]: pAllowConnect = '%c'\n", *pAllowConnect); record("devinit1: argv[7]: pAllowAutoSignon = '%c'\n", *pAllowAutoSignon); /********************************************************************/ /* Initialize these two flags to allow any clients that are not */ /* found in either mapping table. */ /********************************************************************/ *pAllowConnect = '1'; *pAllowAutoSignon = '0'; /********************************************************************/ /* Read fixed data area to get IP Address Mapping table. If this */ /* data area is not found, then screening should NOT be active! */ /********************************************************************/ if (dtaara.returned.Bytes_Returned) { record("devinit1: Data area IP Map file is: >%s<\n", pszDataArea); } else { record("devinit1: Screening function not active\n"); *pAllowConnect = '1'; *pAllowAutoSignon = '0'; /******************************************************************/ /* Exit */ /******************************************************************/ record("devinit1: <<<<< exit\n\n"); if (fDebug) { fclose(pDebug); } /* endif */ exit(0); } /* endif */ /********************************************************************/ /* Calculate the IP address for analysis */ /********************************************************************/ pSockAddr = (struct sockaddr_in *)pConnection->Internet_address; pClientIP = inet_ntoa(pSockAddr->sin_addr); iClientPort = pSockAddr->sin_port; record("devinit1: Client's IP: %s\n", pClientIP); record("devinit1: Client's Port: %d\n", iClientPort); /********************************************************************/ /* Set Client's IP address into compare structure */ /********************************************************************/ memset(&IP, 0x00, sizeof(IP)); sscanf(pClientIP, "%[^.].%[^.].%[^.].%s", IP.C1, IP.C2, IP.C3, IP.C4); /********************************************************************/ /* The following is a sample ALLOW access list */ /********************************************************************/ /********************************************************************/ /* # Client IP Description */ /* # --------- --------------------------------------------------- */ /* 1.222.33.47 # Host wormhole.endicott.ibm.com */ /* 1.222.33.62 # Host pgvs5.endicott.ibm.com */ /* 1.222.33.63 # Host 8isit.endicott.ibm.com */ /* 1.222.33.57 # Host heimer.endicott.ibm.com */ /* 1.222.44.* # Sub-network for Raised Floor administrators */ /* 1.222.33.* # Sub-network for TCP/IP developers */ /* *.*.*.* # Allow all clients to connect */ /* # */ /* # TELNET Client IP Address ALLOW Mapping Rules: */ /* # */ /* # This file lists IP addresses that WILL be allowed to connect */ /* # to the TELNET server. */ /* # */ /* # You must create a SRCPF (or PF) file object and download this */ /* # file as member ALLOW. The name of the file must be identified */ /* # inside character *DTAARA object QUSRSYS/QTGEXITS in the form */ /* # 'filelib/filename'. Do not put quotes in the *DTAARA. */ /* # Append '*MSGQ' and '*FILE' strings if you want messages logged */ /* # to QTCP message queue and file member LOG, respectively. */ /* # */ /* # User profile QTCP must have at least *READ private authority */ /* # to this file, *USE or *CHANGE is recommended for logging. */ /* # */ /* # 1) Any line with a '#' in column 1 is a comment. */ /* # 2) A '*' serves as a wildcard in IP address hops. */ /* # 3) A '#' delimits an IP address and description on a line. */ /* # 4) Host and domain names are not allowed. */ /* # 5) Place wildcard entries last since file is read top to */ /* # bottom. The first matching IP address applies. Put */ /* # comment lines after IP addresses to find matches quicker. */ /* # 6) The ALLOW file has priority over the DISALLOW file, use */ /* # care when putting wildcards into the ALLOW file. */ /* # */ /* # QUSRSYS/QTGEXITS Data Area Rules: */ /* # - Required to have library and name of map file as the first */ /* # string in the *DTAARA, separated by a slash. */ /* # - To log a message to QTCP user *MSGQ, append a blank */ /* # delimited '*MSGQ' string to the *DTAARA contents. */ /* # - To log a message the file member LOG, append a blank */ /* # delimited '*FILE' string to the *DTAARA contents. */ /* # */ /* # QUSRSYS/QTGEXITS Data Area Example: */ /* # 'filelib/filename msgqflag fileflag' */ /* # 'USEREXIT/TELNET *MSGQ' */ /* # 'MYLIB/TELNET *MSGQ *FILE' */ /* # 'MYLIB/TELNET *FILE *DEBUG' */ /********************************************************************/ /********************************************************************/ /* Read *LIBL/TELNET(ALLOW) file first, supersedes DISALLOW */ /********************************************************************/ iMatch = 0; memset(pcRecord, 0x00, sizeof(pcRecord)); /********************************************************************/ /* Read mapping table into local variable - up to 1000 entries. */ /********************************************************************/ sprintf(pcOpenFile, "%s(%s)", pszDataArea, pszAllow); if (NULL != (pFile = _Ropen(pcOpenFile, "rr")) ) { record("devinit1: Map file >%s< opened\n", pcOpenFile); /******************************************************************/ /* Determine if source physical file or just physical file. */ /* Will be a 'Y' if source physical file, else 'N'. */ /******************************************************************/ if (NULL != (pOFdBk = _Ropnfbk(pFile))) { if ('Y' == pOFdBk->src_file_indic) { /**************************************************************/ /* Step over first 12 bytes */ /**************************************************************/ iOffset = 12; } else { /**************************************************************/ /* Start at first byte */ /**************************************************************/ iOffset = 0; } /* endelse */ record("devinit1: SRCPF: '%c' iOffset: %d\n", pOFdBk->src_file_indic, iOffset); } /* endif */ record("devinit1: Attempting match for: %s\n", pClientIP); i = 0; pFdBk = _Rreadf(pFile, pcRecord, sizeof(pcRecord), __NO_LOCK); while (pFdBk->num_bytes > 0) { /****************************************************************/ /* If '#' character in first column, then this is a comment. */ /* Must offset by 12 bytes into a source physical file to step */ /* over record information. */ /****************************************************************/ if (pcRecord[iOffset] == '#') { record("devinit1: Line %d is a comment line\n", i+1); /**************************************************************/ /* Step mapping table index variable */ /**************************************************************/ i++; /**************************************************************/ /* Read next mapping table record */ /**************************************************************/ pFdBk = _Rreadn(pFile, pcRecord, sizeof(pcRecord), __NO_LOCK); continue; } /* endif */ record("devinit1: Reading line %d\n", i+1); Trim(pcRecord, sizeof(pcRecord)-1); record("devinit1: pcRecord:\n"); buffer(pcRecord, strlen(pcRecord)); /****************************************************************/ /* Read and parse mapping table. Must offset by 12 bytes into */ /* a source physical file to step over record information. */ /* Comments ('#' delimiter) may exist on the line. */ /****************************************************************/ sscanf(&pcRecord[iOffset], " %s #", Map[i].ClientIP); /****************************************************************/ /* Read IP address from table into compare structure */ /****************************************************************/ memset(&(IP.M1[0]), 0x00, (sizeof(IP)/2)); sscanf(Map[i].ClientIP, "%[^.].%[^.].%[^.].%s", IP.M1,IP.M2,IP.M3,IP.M4); record("devinit1: IP Address compare structure:\n"); buffer((char *)&IP, sizeof(IP)); /****************************************************************/ /* Compare IP address. Hops must match or be "*" wildcarded. */ /****************************************************************/ if ((!strcmp(IP.C1, IP.M1) || !strcmp("*", IP.M1)) && (!strcmp(IP.C2, IP.M2) || !strcmp("*", IP.M2)) && (!strcmp(IP.C3, IP.M3) || !strcmp("*", IP.M3)) && (!strcmp(IP.C4, IP.M4) || !strcmp("*", IP.M4))) { record("devinit1: Match found! Client is allowed\n"); /**************************************************************/ /* Since a match was found, this client is allowed in */ /**************************************************************/ *pAllowConnect = '1'; *pAllowAutoSignon = '0'; /**************************************************************/ /* Since a match was found, set flag and break out of loop */ /**************************************************************/ iMatch = 1; break; } /* endif */ record("devinit1: No match found\n"); /****************************************************************/ /* Step mapping table index variable */ /****************************************************************/ i++; /****************************************************************/ /* Read next mapping table record */ /****************************************************************/ pFdBk = _Rreadn(pFile, pcRecord, sizeof(pcRecord), __NO_LOCK); } /* endwhile */ record("devinit1: End of file reached\n"); /******************************************************************/ /* Close mapping table file */ /******************************************************************/ if (NULL != pFile) { record("devinit1: File >%s< closed\n", pcOpenFile); _Rclose(pFile); } /* endif */ } else { /******************************************************************/ /* Tell me about errors in case we are debugging */ /******************************************************************/ record("devinit1: Error opening file >%s<!\n", pcOpenFile); } /* endif */ /********************************************************************/ /* # Client IP Description */ /* # --------- --------------------------------------------------- */ /* *.*.*.* # All users not listed in ALLOW will be refused. */ /* # */ /* # TELNET Client IP Address DISALLOW Mapping Rules: */ /* # */ /* # This file lists IP addresses that will NOT be allowed to */ /* # connect to the TELNET server. */ /* # */ /* # You must create a SRCPF (or PF) file object and download this */ /* # file as member ALLOW. The name of the file must be identified */ /* # inside character *DTAARA object QUSRSYS/QTGEXITS in the form */ /* # 'filelib/filename'. Do not put quotes in the *DTAARA. */ /* # Append '*MSGQ' and '*FILE' strings if you want messages logged */ /* # to QTCP message queue and file member LOG, respectively. */ /* # */ /* # User profile QTCP must have at least *READ private authority */ /* # to this file, *USE or *CHANGE is recommended for logging. */ /* # */ /* # 1) Any line with a '#' in column 1 is a comment. */ /* # 2) A '*' serves as a wildcard in IP address hops. */ /* # 3) A '#' delimits an IP address and description on a line. */ /* # 4) Host and domain names are not allowed. */ /* # 5) Place wildcard entries last since file is read top to */ /* # bottom. The first matching IP address applies. Put */ /* # comment lines after IP addresses to find matches quicker. */ /* # 6) The ALLOW file has priority over the DISALLOW file, use */ /* # care when putting wildcards into the ALLOW file. */ /* # */ /* # QUSRSYS/QTGEXITS Data Area Rules: */ /* # - Required to have library and name of map file as the first */ /* # string in the *DTAARA, separated by a slash. */ /* # - To log a message to QTCP user *MSGQ, append a blank */ /* # delimited '*MSGQ' string to the *DTAARA contents. */ /* # - To log a message the file member LOG, append a blank */ /* # delimited '*FILE' string to the *DTAARA contents. */ /* # */ /* # QUSRSYS/QTGEXITS Data Area Example: */ /* # 'filelib/filename msgqflag fileflag' */ /* # 'USEREXIT/TELNET *MSGQ' */ /* # 'MYLIB/TELNET *MSGQ *FILE' */ /* # 'MYLIB/TELNET *FILE *DEBUG' */ /********************************************************************/ /********************************************************************/ /* Read mapping table into local variable - up to 500 entries. */ /********************************************************************/ if (!iMatch) { /******************************************************************/ /* Read *LIBL/TELNET(DISALLOW) file next, if no match from ALLOW */ /******************************************************************/ iMatch = 0; memset(pcRecord, 0x00, sizeof(pcRecord)); /******************************************************************/ /* Read mapping table into local variable - up to 1000 entries. */ /******************************************************************/ sprintf(pcOpenFile, "%s(%s)", pszDataArea, pszDisallow); if (NULL != (pFile = _Ropen(pcOpenFile, "rr")) ) { record("devinit1: Map file >%s< opened\n", pcOpenFile); /****************************************************************/ /* Determine if source physical file or just physical file. */ /* Will be a 'Y' if source physical file, else 'N'. */ /****************************************************************/ if (NULL != (pOFdBk = _Ropnfbk(pFile))) { record("devinit1: pOFdBk->src_file_indic: %c\n", pOFdBk->src_file_indic); if ('Y' == pOFdBk->src_file_indic) { /************************************************************/ /* Step over first 12 bytes */ /************************************************************/ iOffset = 12; } else { /************************************************************/ /* Start at first byte */ /************************************************************/ iOffset = 0; } /* endelse */ } /* endif */ record("devinit1: Attempting match for: %s\n", pClientIP); i = 0; pFdBk = _Rreadf(pFile, pcRecord, sizeof(pcRecord), __NO_LOCK); while (pFdBk->num_bytes > 0) { /**************************************************************/ /* If '#' character in first column, then this is a comment. */ /* Must offset by 12 bytes into a source physical file to step*/ /* over record information. */ /**************************************************************/ if (pcRecord[iOffset] == '#') { record("devinit1: Line %d is a comment line\n", i+1); /************************************************************/ /* Step mapping table index variable */ /************************************************************/ i++; /************************************************************/ /* Read next mapping table record */ /************************************************************/ pFdBk = _Rreadn(pFile, pcRecord, sizeof(pcRecord), __NO_LOCK); continue; } /* endif */ Trim(pcRecord, sizeof(pcRecord)-1); record("devinit1: Reading line %d\n", i+1); record("devinit1: pcRecord:\n"); buffer(pcRecord, strlen(pcRecord)); /**************************************************************/ /* Read and parse mapping table. Must offset by 12 bytes into*/ /* a source physical file to step over record information. */ /* Comments ('#' delimiter) may exist on the line. */ /**************************************************************/ sscanf(&pcRecord[iOffset], " %s #", Map[i].ClientIP); /**************************************************************/ /* Read IP address from table into compare structure */ /**************************************************************/ memset(&(IP.M1[0]), 0x00, (sizeof(IP)/2)); sscanf(Map[i].ClientIP, "%[^.].%[^.].%[^.].%s", IP.M1,IP.M2,IP.M3,IP.M4); record("devinit1: IP Address compare structure:\n"); buffer((char *)&IP, sizeof(IP)); /**************************************************************/ /* Compare IP address. Hops must match or be "*" wildcarded. */ /**************************************************************/ if ((!strcmp(IP.C1, IP.M1) || !strcmp("*", IP.M1)) && (!strcmp(IP.C2, IP.M2) || !strcmp("*", IP.M2)) && (!strcmp(IP.C3, IP.M3) || !strcmp("*", IP.M3)) && (!strcmp(IP.C4, IP.M4) || !strcmp("*", IP.M4))) { record("devinit1: Match found! Client is NOT allowed\n"); /************************************************************/ /* Since a match was found, this client is NOT allowed in */ /************************************************************/ *pAllowConnect = '0'; *pAllowAutoSignon = '0'; /************************************************************/ /* Since a match was found, set flag and break out of loop */ /************************************************************/ iMatch = 1; break; } /* endif */ record("devinit1: No match found\n"); /**************************************************************/ /* Step mapping table index variable */ /**************************************************************/ i++; /**************************************************************/ /* Read next mapping table record */ /**************************************************************/ pFdBk = _Rreadn(pFile, pcRecord, sizeof(pcRecord), __NO_LOCK); } /* endwhile */ record("devinit1: End of file reached\n"); /****************************************************************/ /* Close mapping table file */ /****************************************************************/ if (NULL != pFile) { record("devinit1: File >%s< closed\n", pcOpenFile); _Rclose(pFile); } /* endif */ } else { /****************************************************************/ /* Tell me about errors in case we are debugging */ /****************************************************************/ record("devinit1: Error opening file >%s<!\n", pcOpenFile); } /* endif */ } /* if (!iMatch) */ /********************************************************************/ /* Post a success or failure message to user QTCP message queue */ /********************************************************************/ record("devinit1: Posting connect message to QTCP msg queue\n"); time(<ime); sprintf(Time, "%.24s", ctime(<ime)); if ('1' == *pAllowConnect) { sprintf(acMsg, "TELNET client %.15s port %d connect at %.24s.", pClientIP, iClientPort, Time); /******************************************************************/ /* Change the message if it was allowed to auto-sigon */ /******************************************************************/ if ('1' == *pAllowAutoSignon) { sprintf(acMsg, "TELNET client %.15s port %d auto-signon at %.24s.", pClientIP, iClientPort, Time); } /* endelse */ } else { sprintf(acMsg, "TELNET client %.15s port %d refused at %.24s.", pClientIP, iClientPort, Time); } /* endelse */ /********************************************************************/ /* If data area found and special string found, send connect msg */ /********************************************************************/ if (fLogMsg) { memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = sizeof(esErrCode);/* Ignore exceptions */ QMHSNDM("CPF9897", QCPFMSG, acMsg, strlen(acMsg), MSG_INFO, "QTCP *USER ", 1, acMsgReply, acMsgKey,&esErrCode); } /* endif */ /********************************************************************/ /* If data area found and special string found, log connect msg */ /********************************************************************/ if (fLogFile) { /******************************************************************/ /* Open database file for permanent log of test results. */ /******************************************************************/ sprintf(pcOpenFile, "%s(%s)", pszDataArea, pszLog); sprintf(pcOpenParms, "a, lrecl=%d, recfm=v", RECORD_WIDTH); pLog=fopen(pcOpenFile, pcOpenParms); fwrite(acMsg, 1, strlen(acMsg), pLog); fclose(pLog); } /* endif */ /********************************************************************/ /* Exit */ /********************************************************************/ record("devinit1: <<<<< exit\n\n"); if (fDebug) { fclose(pDebug); } /* endif */ exit(0); } /* End main */ /**********************************************************************/ /*@function buffer() */ /* */ /* Parameters: */ /* */ /* char *buffer - points at buffer to dump */ /* int length - length of buffer to dump */ /* */ /* Description: */ /* */ /* Dumps out a buffer in both hex and readable form. */ /* */ /* 1934D8E3 D4E3E2D7 C3F0F0F2 40404040 |..QTMTSPC002 | */ /* 40404040 40404040 40404040 40404040 | | */ /* 00000000 00000000 00000000 00000000 |................| */ /**********************************************************************/ void buffer(char *Buffer, int Length) { int iRow = 0; int iCol = 0; int iLast = 0; int iTotalRows = 0; int iLen; int iBytes; unsigned char c; char acLine[20]; char Buff[512]; char *pBuff = Buff; /********************************************************************/ /* If debug is not active, nothing to do here... */ /********************************************************************/ if (!fDebug || (Buffer == (char *)NULL)) { record("buffer: Buffer is NULL\n"); return; } /* endif */ /********************************************************************/ /* Calculate total rows to fit all bytes */ /********************************************************************/ iTotalRows = (Length + 15) / 16; /********************************************************************/ /* Primary loop for rows */ /********************************************************************/ for (iRow = 0; iRow < iTotalRows; iRow++) { /******************************************************************/ /* Offset into line by 4 blank characters */ /******************************************************************/ *pBuff = ' '; pBuff++; *pBuff = ' '; pBuff++; *pBuff = ' '; pBuff++; *pBuff = ' '; pBuff++; /******************************************************************/ /* Print 16 bytes of the buffer as the hexadecimal dump section */ /* Primary loop for columns */ /******************************************************************/ for (iCol = 0; iCol < 16; iCol++) { /****************************************************************/ /* One printable character displays two hexadecimal characters */ /****************************************************************/ if (iCol + (iRow * 16) >= Length) { /**************************************************************/ /* No more data - just fill remaining positions with blanks */ /**************************************************************/ *pBuff = ' '; pBuff++; *pBuff = ' '; pBuff++; } else { /**************************************************************/ /* Print actual data */ /**************************************************************/ sprintf(pBuff, "%02X", Buffer[iCol + (iRow * 16)]); pBuff += 2; } /* endelse */ /****************************************************************/ /* Add a space separator every 4 hex bytes */ /****************************************************************/ if (iCol % 4 == 3) { *pBuff = ' '; pBuff++; /* Pad character */ } /* endif */ } /* endfor */ /******************************************************************/ /* Print the same 16 bytes as the "readable" text section */ /******************************************************************/ *pBuff = ' '; pBuff++; *pBuff = '|'; pBuff++; /* Left text bar */ /******************************************************************/ /* Print 16 bytes of the buffer as readable text section */ /* Secondary loop for columns */ /******************************************************************/ iLast = 0; /* Bytes last line */ for (iCol = 0; iCol < 16; iCol++) { if (iCol + (iRow * 16) >= Length) { /**************************************************************/ /* No more data - just fill remaining positions with blanks */ /**************************************************************/ *pBuff = ' '; pBuff++; } else { /**************************************************************/ /* Print actual data */ /**************************************************************/ iLast++; /* Actual data */ c = Buffer[iCol + (iRow * 16)]; if (isprint(c)) { *pBuff = c; pBuff++; /* Actual char */ } else if (c == 0x40) { *pBuff = ' '; pBuff++; /* Blank char */ } else { *pBuff = '.'; pBuff++; /* Unprintable */ } /* endelse */ } /* endelse */ } /* endfor */ *pBuff = '|'; pBuff++; /* Right text bar */ /******************************************************************/ /* Build the finished output */ /******************************************************************/ memset(acLine, 0x00, sizeof(acLine)); iBytes = (iRow * 16) + iLast; sprintf(acLine," Byte %d\n", iBytes); /* Byte count */ strcpy(pBuff, acLine); pBuff = &Buff[0]; /* Reset pointer */ printf("%s", Buff); } /* endfor */ return; } /**********************************************************************/ /*@function record() */ /* */ /* Parameters: */ /* */ /* variable arguments */ /* */ /* Log test result entry to standard out (normally console). This */ /* will only occur if DEBUG is active. If this occurs in a batch */ /* job, a spooled file is usually created holding output. */ /**********************************************************************/ void record(char *Format, ...) { va_list arg_ptr; int iLen; char record[512]; /********************************************************************/ /* If debug is not active, nothing to do here... */ /********************************************************************/ if (!fDebug) { return; } /* endif */ va_start(arg_ptr, Format); iLen = vsprintf(record, Format, arg_ptr); va_end(arg_ptr); record[iLen] = 0x00; printf("%s", record); return; } /**********************************************************************/ /*@function handler() */ /* */ /* Parameters: */ /* */ /* int iSignal - value of the signal which caused the handler to be */ /* invoked. SIGABRT, SIGTERM, etc. */ /**********************************************************************/ void handler(int iSignal) { ERRSTRUCT esErrCode; _INTRPT_Hndlr_Parms_T Signal; _INTRPT_Hndlr_Parms_T *pSignal = &Signal; char *pszMsgFile = NULL; struct { int Bytes_Return; int Bytes_Available; int Length_Message_Returned; int Length_Message_Available; int Length_Help_Returned; int Length_Help_Available; char Message[256]; char Message_Help[256]; } rtvm0100; char *Signals[] = { "SIGABRT", /* 1 Abnormal termination */ "SIGFPE", /* 2 Erroneous arithmetic operation */ "SIGILL", /* 3 Invalid hardware instruction */ "SIGINT", /* 4 Interactive attention signal */ "SIGSEGV", /* 5 Invalid memory reference */ "SIGTERM", /* 6 Termination signal */ "SIGUSR1", /* 7 Application defined signal 1 */ "SIGUSR2", /* 8 Application defined signal 2 */ "SIGIO", /* 9 I/O possible, or completed */ "SIGALL", /* 10 All signals */ "SIGOTHER", /* 11 ILE C/400 signal */ "SIGKILL", /* 12 Termination signal(cannot be caught,ignored)*/ "SIGPIPE", /* 13 Write on a pipe with no readers */ "SIGALRM", /* 14 Timeout signal */ "SIGHUP", /* 15 Hangup detected on controlling terminal */ "SIGQUIT", /* 16 Interactive termination signal */ "SIGSTOP", /* 17 Stop signal (cannot be caught or ignored) */ "SIGTSTP", /* 18 Interactive stop signal */ "SIGCONT", /* 19 Continue if stopped */ "SIGCHLD", /* 20 Child process terminated or stopped */ "SIGTTIN", /* 21 Background read from controlling terminal */ "SIGTTOU", /* 22 Background write to controlling terminal */ "SIGURG", /* 23 High bandwidth data is available at socket */ "SIGPOLL", /* 24 Pollable event */ "SIG25", /* 25 Not defined */ "SIG26", /* 26 Not defined */ "SIG27", /* 27 Not defined */ "SIG28", /* 28 Not defined */ "SIG29", /* 29 Not defined */ "SIG30", /* 30 Not defined */ "SIG31", /* 31 Not defined */ "SIGBUS", /* 32 Bus error (specification exception) */ "SIGDANGER", /* 33 system crash imminent */ "SIGPRE", /* 34 programming exception */ "SIGSYS", /* 35 Bad system call */ "SIGTRAP", /* 36 Trace/Breakpoint trap */ "SIGPROF", /* 37 Profiling timer expired */ "SIGVTALRM", /* 38 Virtual timer expired */ "SIGXCPU", /* 39 CPU time limit exceeded */ "SIGXFSZ" /* 40 File size limit exceeded */ }; record("handler: Caught signal %s\n", Signals[iSignal-1]); /********************************************************************/ /* Set file scoped flag so caller knows exception occurred */ /********************************************************************/ fException = fTrue; /********************************************************************/ /* Try and pull out the message text */ /********************************************************************/ _GetExcData(&Signal); if (!memcmp(Signal.Msg_Id, "TCP", 3) ) { /******************************************************************/ /* TCP Apps (and Stack with recursive call) message file */ /******************************************************************/ pszMsgFile = QTCPMSGF; } else if (!memcmp(Signal.Msg_Id, "C2M16", 5) ) { /******************************************************************/ /* ILE-C message file (primarily for signals, if use raise/abort) */ /******************************************************************/ pszMsgFile = QC2MSGF; } else if (!memcmp(Signal.Msg_Id, "CEE99", 5) ) { /******************************************************************/ /* ILE-C message file */ /******************************************************************/ pszMsgFile = QCEEMSG; } else { /******************************************************************/ /* Most CPFxxxx messages */ /******************************************************************/ pszMsgFile = QCPFMSG; } /* endif */ memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = sizeof(esErrCode); /* Ignore exceptions */ QMHRTVM(&rtvm0100, /* Message information */ sizeof(rtvm0100), /* Length of message information */ "RTVM0100", /* Format name */ Signal.Msg_Id, /* Message identifier */ pszMsgFile, /* Qualified message file name */ Signal.Ex_Data, /* Message data */ sizeof(Signal.Ex_Data), /* Length of message data */ "*YES ", /* Replace substitution values */ "*NO ", /* Return format control */ &esErrCode, /* Error Code */ "*MSGID ", /* Retrieve option */ 0, /* Convert to CCSID */ 0); /* Message data CCSID */ if (esErrCode.Bytes_Available || !rtvm0100.Length_Message_Returned) { record("handler: Error - message not found\n"); } else { record("handler: Escape message:\n"); buffer(rtvm0100.Message, rtvm0100.Length_Message_Returned); } /* endelse */ /********************************************************************/ /* Delete msg from job log if caller so desires (flag indicator) */ /********************************************************************/ if (fRemoveEscapeMsg) { record("handler: Remove escape message from job log\n"); memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = sizeof(esErrCode); QMHCHGEM(&Signal.Target, 0, &Signal.Msg_Ref_Key, "*REMOVE ", "", 0, &esErrCode); } else { record("handler: Escape message not removed from job log\n"); } /* endif */ record("handler: Reset signals -> handler\n"); signal(SIGALL, &handler); return; } /* Function Specification *********************************************/ /* */ /* Function Name: cl_command */ /* */ /* Descriptive Name: run any CL command as if on a command line. */ /* */ /* char * Command - null terminated string */ /* */ /* End Function Specification *****************************************/ void cl_command(char *Command) /* Entry point */ { /********************************************************************/ /* Local Variables */ /********************************************************************/ ERRSTRUCT esErrCode; Qca_PCMD_CPOP0100_t cpop0100; char cpop0100_out[512]; int cpop0100_len; /********************************************************************/ /* typedef _Packed struct Qca_PCMD_CPOP0100 { */ /* int Command_Process_Type; */ /* char DBCS_Data_Handling; */ /* char Prompter_Action; */ /* char Command_String_Syntax; */ /* char Message_Key[4]; */ /* char Reserved[9]; */ /* } Qca_PCMD_CPOP0100_t; */ /********************************************************************/ record("cl_command: Command:\n"); buffer(Command, strlen(Command)); memset(cpop0100_out, 0x00, sizeof(cpop0100_out)); memset(&cpop0100, 0x00, sizeof(Qca_PCMD_CPOP0100_t)); cpop0100.Command_Process_Type = 0; cpop0100.DBCS_Data_Handling = '0'; cpop0100.Prompter_Action = '0'; cpop0100.Command_String_Syntax = '0'; memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = 0L; /* Force exceptions to be signalled */ QCAPCMD(Command, strlen(Command), &cpop0100, sizeof(Qca_PCMD_CPOP0100_t), "CPOP0100", cpop0100_out, sizeof(cpop0100_out) - 1, &cpop0100_len, &esErrCode); return; } /* Function Specification *********************************************/ /* */ /* Function Name: Pad */ /* */ /* char *pszString - null terminated string, may or may not have */ /* blanks */ /* */ /* int iLength - maximum length to pad the string (includes NULL */ /* terminator). A NULL terminator WILL be added! */ /* */ /* iLength should be where you want the NULL term. */ /* */ /* Thus, iLength=10 means array position 10, not 9. */ /* */ /* Descriptive Name: pads a string with blanks */ /* */ /* End Function Specification *****************************************/ int Pad(char *pszString, int iLength) { int iLen = strlen(pszString); if (iLength <= iLen) { return(iLen); } /* endif */ /********************************************************************/ /* Start at the end of the string, and add blanks. */ /********************************************************************/ while (iLen < iLength) { pszString[iLen] = ' '; iLen++; } /* endwhile */ /********************************************************************/ /* Add the null terminator to make it a 'C' string. */ /********************************************************************/ pszString[iLen] = 0x00; iLen = strlen(pszString); return(iLen); } /* Function Specification *********************************************/ /* */ /* Function Name: Trim */ /* */ /* char *pszString - null terminated string, may have trailing */ /* blanks */ /* */ /* int iLength - start position of the string to be trimmed. This */ /* need not be the length of the string. You should */ /* specify the position of any NULL terminator if you */ /* are trimming a 'C' string. */ /* */ /* iLength should be where you want the NULL term. */ /* */ /* Thus, iLength=10 means array position 10, not 9. */ /* */ /* Descriptive Name: pads a string with blanks */ /* */ /* End Function Specification *****************************************/ int Trim(char *pszString, int iLength ) { int iLen = iLength - 1; /********************************************************************/ /* While we are not at the 1st char AND (a char is whitespace OR */ /* 0x00) keep backing up from end of string */ /********************************************************************/ pszString[iLength] = 0x00; while ( (iLen >= 0) && ((isspace(pszString[iLen]) ) || (0x00 == pszString[iLen])) ) { iLen--; } /* endwhile */ /********************************************************************/ /* if iLen unchanged, it means no trimming was done (no whitespace */ /* found). */ /********************************************************************/ if (iLen == (iLength-1)) { return(iLen); } /* endif */ /********************************************************************/ /* if iLen >= 0, we found 1 or more chars, so mark end with a */ /* NULL terminator. */ /********************************************************************/ else if (iLen >= 0) { pszString[iLen + 1] = 0x00; } /* endif */ /********************************************************************/ /* else iLen < 0, which means we didn't find anything but whitespace*/ /********************************************************************/ else { pszString[0] = 0x00; } /* endelse */ iLen = strlen(pszString); return(iLen); } #undef _DEVINIT1_C /**********************************************************************/ /* END OF DEVINIT1.C */ /**********************************************************************/
Return to the TELNET exit program road map:
This is a working example of an Advanced TELNET initialization (log in) exit program. It provides more options than the basic TELNET initialization exit program.
Return to the TELNET exit program road map:
/* Module Description *************************************************/ /* */ /**********************************************************************/ /* */ /* DISCLAIMER: This material contains programming source code for */ /* your consideration. These examples have not been thoroughly */ /* tested under all conditions. IBM, therefore, cannot guarantee or */ /* imply reliability, serviceability, performance or function of */ /* these programs. All programs contained herein are provided to you */ /* "AS IS". THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */ /* FOR A PARTICULAR PURPOSE ARE EXPRESSLY DISCLAIMED. */ /* */ /* LIMITATION OF LIABILITY: Neither IBM nor the author shall be */ /* liable for any claims or damages whatsoever, including property */ /* damage, personal injury, intellectual property infringement, loss */ /* of profits, or interruption of business, or for any special, */ /* consequential or incidental damages, however caused, whether */ /* arising out of breach of warranty, contract, tort (including */ /* negligence), strict liability, or otherwise. */ /* */ /**********************************************************************/ /* */ /* Source File Name: devinit2.c */ /* */ /* Module Name: TELNET Device Initialization exit program. */ /* */ /* Source File Description: */ /* */ /* This module contains functions to allow client TELNET to */ /* bypass an AS/400 sign-on panel or request a specific virtual */ /* terminal device. RFC 1572 extensions (TELNET Environment */ /* Options) is necessary in the client code to activate device */ /* selection. */ /* */ /* Client access controls are defined via access lists, where */ /* the client IP address determines if a connection is allowed or */ /* dis-allowed. Customers indicate the library/name of the access */ /* list/log *FILE object via character data area QTCP/QTGEXITS. */ /* */ /* Care must be taken to protect the TELNET exit *PGMs and the */ /* access list *FILE from unauthorized users! Failure to protect */ /* these objects using PUBLIC(*EXCLUDE) authorities can compromise */ /* your system! Only user profile QTCP and trusted users should be */ /* be granted access to these objects and the library in which they */ /* reside! */ /* */ /* Data area layout expected: */ /* - required to have library and name of IP address mapping file */ /* as the first string in the *DTAARA, separated by a slash. */ /* - if a connect msg to QTCP *MSGQ is desired, append a *MSGQ */ /* string to the *DTAARA contents. */ /* - if a connect msg to SRCPF or PF member LOG is desired, append */ /* a *FILE string to the *DTAARA contents. */ /* - if debug msgs to SRCPF or PF member DEBUG are desired, append */ /* a *DEBUG string to the *DTAARA contents. */ /* */ /* Example: */ /* 'filelib/filename msgqflag fileflag debugflag' */ /* 'USEREXIT/TELNET *MSGQ *DEBUG' */ /* 'USEREXIT/TELNET *FILE *DEBUG' */ /* 'USEREXIT/TELNET *MSGQ *FILE' */ /* 'USEREXIT/TELNET *MSGQ *FILE *DEBUG' */ /* */ /* For initialization purposes, this program first searches member */ /* MAP of the *FILE listed in the QTCP/QTGEXITS data area for a */ /* match, and applies the session values if a match is found. If */ /* no match is found, this program then reads member DISALLOW to */ /* see if this particular user should be dis-allowed. If still no */ /* match is found, then this user is allowed to connect as if no */ /* screening occurred. */ /* */ /* End Module Description *********************************************/ #pragma comment (copyright, \ "(C) Copyright IBM Corp. 1997. \ All rights reserved. \ US Government Users Restricted Rights - \ Use, duplication or disclosure restricted \ by GSA ADP Schedule Contract with IBM Corp. \ Licensed Materials - Property of IBM.") #define _DEVINIT2_C /**********************************************************************/ /* All file scoped includes go here */ /**********************************************************************/ /**********************************************************************/ /* Include CUE direct, remove dependency that QSYSINC be installed */ /**********************************************************************/ #ifndef ETGDEVEX_h #define ETGDEVEX_h /**********************************************************************/ /* Type Definition for the User Description Information */ /**********************************************************************/ typedef _Packed struct Qtg_User_Description { int Length_user_description; char User_profile[10]; char Current_library[10]; char Program_to_call[10]; char Initial_menu[10]; } Qtg_User_Description_t; /**********************************************************************/ /* Structures used by Device Description Information */ /**********************************************************************/ #define QTG_DSPD0100 "DSPD0100" typedef _Packed struct Qtg_DSPD0100 { char Keyboard_identifier[3]; char Reserved[1]; int Code_page; int Char_set; } Qtg_DSPD0100_t; /**********************************************************************/ /* Type Definition for the Device Description Information */ /**********************************************************************/ typedef _Packed struct Qtg_Device_Description { char Device_name[10]; char Device_format[8]; char Reserved[2]; int Offset_to_device_attributes; int Length_device_attributes; Qtg_DSPD0100_t Display_device; } Qtg_Device_Description_t; /**********************************************************************/ /* Type Definition for the Connection Description Information */ /**********************************************************************/ typedef _Packed struct Qtg_Connection_Description { int Length_connection_description; char Internet_address[20]; char Secure_password_valid[1]; char Workstation_type[14]; } Qtg_Connection_Description_t; #endif #include <recio.h> /* _Ropen, _Rwrite, _Rreadf */ #include <xxfdbk.h> /* _Ropnfbk */ #include <stdio.h> /* sprintf, printf, fopen, fread, */ #include <time.h> /* time(), ctime() */ #include <stdlib.h> /* system, atoi, free, malloc, exit, */ #include <string.h> /* strxxx, memxxx */ #include <ctype.h> /* isspace, toupper */ #include <except.h> /* _CNL_Hndlr_Parms_T structure */ #include <signal.h> /* _GetExcData(), signal() */ #include <stdarg.h> /* va_start(), va_arg(), va_end() */ #include <sys/types.h> /* Include for inet_ntoa() */ #include <sys/socket.h> /* Include for inet_ntoa() */ #include <netinet/in.h> /* Include for struct sockaddr_in */ #include <arpa/inet.h> /* Include for inet_ntoa() */ #include <qusec.h> /* Include for API error code struct */ #include <unistd.h> /* File system API's (POSIX) */ #include <etgdevex.h> /* TELNET Device Exit Point programs */ #include <qwcrsval.h> /* QWCRSVAL - Retrieve System Value */ #include <qdcrdevd.h> /* QDCRDEVD - Retrieve Device Desc */ #include <qcapcmd.h> /* QCAPCMD - Process CL commands */ #include <qmhrtvm.h> /* QMHRTVM - retrieve program message*/ #include <qmhsndm.h> /* QMHSNDM - send non-program msg */ #include <qmhchgem.h> /* QMHCHGEM - change exception msg */ #include <qwcrdtaa.h> /* QWCRDTAA() - retrieve data area */ #include <qmhsndpm.h> /* QMHSNDPM - send program message */ /**********************************************************************/ /* Make all API calls library qualified (valid only at V3R7 and up) */ /**********************************************************************/ #pragma map (QWCRSVAL, "QSYS/QWCRSVAL") #pragma map (QDCRDEVD, "QSYS/QDCRDEVD") #pragma map (QCAPCMD, "QSYS/QCAPCMD") #pragma map (QMHRTVM, "QSYS/QMHRTVM") #pragma map (QMHSNDM, "QSYS/QMHSNDM") #pragma map (QMHCHGEM, "QSYS/QMHCHGEM") #pragma map (QWCRDTAA, "QSYS/QWCRDTAA") #pragma map (QMHSNDPM, "QSYS/QMHSNDPM") /**********************************************************************/ /* All file scoped Constants go here */ /**********************************************************************/ const int fTrue = 1; const int fFalse = 0; /**********************************************************************/ /* All file scoped type declarations go here */ /**********************************************************************/ typedef struct _ERRSTRUCT { int Bytes_Provided; int Bytes_Available; char Exception_Id[7]; char Reserved; char Exception_Data[256]; } ERRSTRUCT; typedef ERRSTRUCT *PERRSTRUCT; /**********************************************************************/ /* All file scoped Macro invocations go here */ /**********************************************************************/ #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) /**********************************************************************/ /* The following describe the IP address mapping file */ /**********************************************************************/ #define MAX_ADDRESSES 1000 #define RECORD_WIDTH 240 /**********************************************************************/ /* Some message API definitions */ /**********************************************************************/ #define MSG_DIAG "*DIAG " #define MSG_ESCAPE "*ESCAPE " #define MSG_INFO "*INFO " #define MSG_INQ "*INQ " #define MSG_COMP "*COMP " #define MSG_NOTIFY "*NOTIFY " #define MSG_RQS "*RQS " #define MSG_STATUS "*STATUS " #define MSQ_Q_CUR_PROG "* " #define QTCPMSG "QTCPMSG *LIBL " /* Stack MSGF */ #define QTCPMSGF "QTCPMSGF *LIBL " /* Apps MSGF */ #define QCPFMSG "QCPFMSG *LIBL " /* Most CPFxxxx */ #define QCEEMSG "QCEEMSG *LIBL " /* CEE9901 */ #define QC2MSGF "QC2MSGF *LIBL " /* ILE Signals */ /**********************************************************************/ /* All internal function prototypes go here */ /**********************************************************************/ static void buffer(char *Buffer, int Length); static void record(char *Format, ...); void handler(int iSignal); static void cl_command(char *Command); static int Pad(char *pszString, int iLength); static int Trim(char *pszString, int iLength); void GetSysVal(char *pszSysVal,char **ppcData,unsigned int *puiDataLen); /**********************************************************************/ /* All file scoped variable declarations go here */ /**********************************************************************/ static int fRemoveEscapeMsg; static int fException; static int fDebug; /* True means to log records to DEBUG */ static int fLogMsg; /* True means send msg to QTCP msgq */ static int fLogFile; /* True means log msg to SRCPF or PF */ static char acMsg[256]; static char acMsgKey[4]; static char acMsgReply[20]; /* Function Specification *********************************************/ /* */ /* Function Name: Main */ /* */ /* Descriptive Name: TELNET Device Initiailization sample program. */ /* */ /* This exit program provides control over device selection and */ /* automatic sign-on for TELNET clients. It makes use of mapping */ /* table MAP to determine what settings to use for a particular */ /* client (using the client IP address to index the table). */ /* */ /* If an IP address match is found in the mapping table, then the */ /* values scanned in are used to control the TELNET session. */ /* */ /* If no match is found, then table DISALLOW is searched for a */ /* match. If a match is found, then this client is denied. If */ /* no match is found, then this client is allowed to connect as if */ /* no screening occurred. */ /* */ /* In all cases, a message is logged to the QTCP user profile *MSGQ */ /* queue in addition to a record being added to an access log file. */ /* */ /* If desired, printers may be customized for a particular client, */ /* but this is not actually done here - we only read in printer */ /* information along with the session information. */ /* */ /* Notes: The "argv[]" parameters are "char *" by definition. */ /* Reference integers as "*(int(argv[1]))", for example. */ /* Consider the method for passing them back to the caller. */ /* */ /* Dependencies: */ /* */ /* TELNET Server exit point QIBM_QTG_DEVINIT format */ /* INIT0100 was registered during TCP/IP LPP installation. */ /* */ /* You must grant user profile QTCP authorities to all objects */ /* and libraries you create or require here! */ /* */ /* Input: */ /* */ /* char * argv[1] - User description information (I/O) */ /* char * argv[2] - Device description information (I/O) */ /* char * argv[3] - Connection description information (Input) */ /* char * argv[4] - Environment options string (Input) */ /* int * argv[5] - Length of environment options (Input) */ /* char * argv[6] - Allow connect '0'=No,'1'=Yes (Output) */ /* char * argv[7] - Allow auto-signon '0'=No,'1'=Yes (Output) */ /* */ /* Exit Normal: Return AllowSignon and/or AllowAutoSignon value to */ /* server application, optionally override Device, KBD, */ /* CP, CS, User Profile, Program, Current Library and */ /* Menu values. */ /* Adds an timestamp access record to member LOG. */ /* Sends a message to the QTCP profile *MSGQ. */ /* */ /* Exit Error: None */ /* */ /* End Function Specification *****************************************/ void main(int argc, char *argv[]) { /********************************************************************/ /* Local Variables */ /********************************************************************/ ERRSTRUCT esErrCode; struct sockaddr_in *pSockAddr; /* Network address template */ char *pClientIP; /* Output of inet_ntoa() */ int iClientPort; /* Up to 5 digits possible */ int iParms; /* # required parameters */ int iMin; /* Working variable */ int i; /* Working variable */ int iMatch; /* Found match in file */ char *pEndPtr; /* For strtol() call */ struct { /* IP address compare struct */ char C1[4]; /* Client IP address */ char C2[4]; char C3[4]; char C4[4]; char M1[4]; /* Mapping table IP address */ char M2[4]; char M3[4]; char M4[4]; } IP; struct { /* Mapping table layout */ char ClientIP[15 + 1]; /* Client IP address to match*/ char Profile[10 + 1]; /* User profile */ char Library[10 + 1]; /* Library */ char Program[10 + 1]; /* Program to call */ char Menu[10 + 1]; /* Initial menu */ char Device[10 + 1]; /* Virtual device name */ char KBD[3 + 1]; /* Keyboard identifier */ char cCP[3 + 1]; /* Code page (character) */ char cCS[3 + 1]; /* Character set (character) */ int CP; /* Code page (numeric) */ int CS; /* Character set (numeric) */ char AllowConnect; /* Grant sign-on to client */ char AutoSignon; /* Bypass sign-on for client */ char SecureAutoSignon; /* Only secure PW bypasses */ } Map[MAX_ADDRESSES]; /* Up to 1000 entries */ Qtg_User_Description_t *pUser; /* From etgdevex.h */ Qtg_DSPD0100_t *pDisplay; /* From etgdevex.h */ Qtg_Device_Description_t *pDevice; /* From etgdevex.h */ Qtg_Connection_Description_t *pConnection; /* From etgdevex.h */ int iEnv; /* Length RFC 1572 variables */ char *pEnv; /* Actual RFC 1572 variables */ char *pAllowConnect; /* Let this client connect? */ char *pAllowAutoSignon; /* Bypass sign-on panel? */ struct _rtvm0100 { int Bytes_Return; int Bytes_Available; int Length_Message_Returned; int Length_Message_Available; int Length_Help_Returned; int Length_Help_Available; char Message[240]; /* commented out in qmhrtvm.h */ char Message_Help[240]; /* commented out in qmhrtvm.h */ } rtvm0100; time_t ltime; /* for time() call */ char Dev[10+1]; /* Log file device name */ char IPAddr[15+1]; /* Log file IP address */ char Port[5+1]; /* Log file port number */ char Time[24+1]; /* Log file timestamp */ char Msg[80+1]; /* Log file description */ char acLog[RECORD_WIDTH]; /* Log file buffer */ /********************************************************************/ /* Data area layout expected: */ /* - required to have library and name of IP address mapping file */ /* - if a connect msg is desired to QTCP msgq, add *MSG string */ /* - if debug msgs are desired to DEBUG member, add *DEBUG string */ /* */ /* Example: */ /* 'filelib/filename msgqflag debugflag' */ /* 'USEREXIT/TELNET *MSGQ *DEBUG' */ /********************************************************************/ char *pszDtaara = "QTGEXITS QTCP "; char *pszDataArea = "QTCP/QTGEXITS"; /* Where are pgms/logs */ char *pszAllow = "MAP"; /* Allowed IP addresses */ char *pszDisallow = "DISALLOW"; /* Disallowed IP addresses */ char *pszDebug = "DEBUG"; /* Debug log file member */ char *pszLog = "LOG"; /* Access log file member */ char pcRecord[RECORD_WIDTH]; /* Buffer for reads */ char pcOpenFile[RECORD_WIDTH]; /* _Ropen() file name */ char pcOpenParms[RECORD_WIDTH]; /* _Ropen() attributes */ _XXOPFB_T *pOFdBk; /* Obtain Open feedback info */ _RIOFB_T *pFdBk; /* I/O feedback area */ _RFILE *pFile; /* Mapping file pointer */ FILE *pDebug; /* Debug file pointer */ FILE *pLog; /* Log file pointer */ int iOffset; /* Varies for SRCPF vs. PF */ typedef struct _Dtaara { /******************************************************************/ /* typedef _Packed struct Qwc_Rdtaa_Data_Returned { */ /* int Bytes_Available; */ /* int Bytes_Returned; */ /* char Type_Value_Returned[10]; */ /* char Library_Name[10]; */ /* int Length_Value_Returned; */ /* int Number_Decimal_Positions; */ /* char Value[]; commented out */ /* } Qwc_Rdtaa_Data_Returned_t; */ /******************************************************************/ Qwc_Rdtaa_Data_Returned_t returned; char contents[257]; } Dtaara_t; Dtaara_t dtaara; char acRmtSign[256]; /* QRMTSIGN system value */ char *pcRmtSign = &acRmtSign[0]; unsigned int uiRmtSignLen; /********************************************************************/ /* Code */ /********************************************************************/ fException = fFalse; /* Exception was trapped by signal() */ fRemoveEscapeMsg = fFalse; /* Remove exception msg from joblog */ fDebug = fFalse; /* Log debug msgs to DEBUG member */ fLogMsg = fFalse; /* Log connect msg to QTCP msg queue */ fLogFile = fFalse; /* Log connect msg to SRCPF or PF */ signal(SIGALL, &handler); /* Trap all signals with our handler */ /********************************************************************/ /* Read fixed data area to see if debug logging is active. We */ /* force exceptions to be signalled so we know if the data area */ /* exists or not by checking the fException flag. */ /********************************************************************/ memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = 0L; /* Force exception to be signalled */ memset(&dtaara, 0x00, sizeof(dtaara)); dtaara.returned.Bytes_Available = sizeof(dtaara); fException = fFalse; /* Initialize - reset in handler */ fRemoveEscapeMsg = fTrue; /* Initialize - want to hide errors */ QWCRDTAA(&dtaara, sizeof(dtaara), pszDtaara, -1, sizeof(dtaara.contents) - 1, &esErrCode); fRemoveEscapeMsg = fFalse; /* Reset - want to see errors */ /********************************************************************/ /* If data area found and contents exist, process contents */ /********************************************************************/ if (!fException && dtaara.returned.Bytes_Returned) { if (NULL != strstr(dtaara.contents, "*MSGQ")) { /****************************************************************/ /* If find *MSGQ, means log connect msg to QTCP msg queue */ /****************************************************************/ fLogMsg = fTrue; } /* endif */ if (NULL != strstr(dtaara.contents, "*FILE")) { /****************************************************************/ /* If find *FILE, means log connect msg to SRCPF or PF */ /****************************************************************/ fLogFile = fTrue; } /* endif */ if (NULL != strstr(dtaara.contents, "*DEBUG")) { /****************************************************************/ /* If find *DEBUG, log flight records to map file member DEBUG */ /****************************************************************/ fDebug = fTrue; } /* endif */ /******************************************************************/ /* Map file is supposed to be the first string in the data area */ /******************************************************************/ pszDataArea = strtok(dtaara.contents, " "); } /* endif */ if (fDebug) { /******************************************************************/ /* record() output goes to standard out */ /******************************************************************/ sprintf(pcOpenFile, "%s(%s)", pszDataArea, pszDebug); sprintf(pcOpenParms, "a+, lrecl=%d, recfm=v", RECORD_WIDTH); pDebug = freopen(pcOpenFile, pcOpenParms, stdout); } /* endif */ record("\n"); record("devinit2: >>>>> entry\n"); record("devinit2: Signals -> handler\n"); iParms = 7; /* Total of 7 parms on the interface */ record("devinit2: argc: %d iParms: %d\n", argc, iParms); if ((argc-1) != iParms) { record("devinit2: Invalid number of parameters!\n"); /******************************************************************/ /* TCP7117 - Program &1 in library &2 received an invalid number */ /* parameters. */ /******************************************************************/ sprintf(acMsg, "DEVINIT2 *LIBL "); iMin = strlen(acMsg); memcpy(&acMsg[iMin], &iParms, sizeof(int)); iMin += sizeof(int); QMHSNDPM("TCP7117", QTCPMSGF, acMsg, iMin, MSG_INFO, MSQ_Q_CUR_PROG, 0, acMsgKey, &esErrCode); record("devinit2: <<<<< exit\n\n"); exit(0); } /* endif */ /********************************************************************/ /* Read fixed data area to get IP Address Mapping table. If this */ /* data area is not found, then screening should NOT be active! */ /********************************************************************/ if (dtaara.returned.Bytes_Returned) { record("devinit2: Data area IP Map file is: >%s<\n", pszDataArea); } else { record("devinit2: Screening function not active\n"); *pAllowConnect = '1'; *pAllowAutoSignon = '0'; /* Safe setting */ /******************************************************************/ /* Exit */ /******************************************************************/ record("devinit2: <<<<< exit\n\n"); if (fDebug) { fclose(pDebug); } /* endif */ exit(0); } /* endif */ /********************************************************************/ /* Initialize for QMHSNDM API calls later */ /********************************************************************/ memset(acMsg, 0x00, sizeof(acMsg)); memset(acMsgKey, 0x00, sizeof(acMsgKey)); /********************************************************************/ /* Set structure pointers */ /********************************************************************/ record("devinit2: Setting pointers...\n"); pUser = (Qtg_User_Description_t *)argv[1]; pDevice = (Qtg_Device_Description_t *)argv[2]; pDisplay = (Qtg_DSPD0100_t *) ((char *)pDevice + pDevice->Offset_to_device_attributes); pConnection = (Qtg_Connection_Description_t *)argv[3]; pEnv = argv[4]; iEnv = *((int *)(argv[5])); pAllowConnect = argv[6]; pAllowAutoSignon = argv[7]; /********************************************************************/ /* Dump the parameter values for inspection (debug) */ /********************************************************************/ record("devinit2: argv[1]: pUser:\n"); iMin = MIN(50, pUser->Length_user_description); buffer((char *)pUser, iMin); record("devinit2: argv[2]: pDevice:\n"); iMin = MIN(100, (sizeof(Qtg_Device_Description_t) + pDevice->Length_device_attributes)); buffer((char *)pDevice, iMin); record("devinit2: argv[3]: pConnection:\n"); iMin = MIN(100, pConnection->Length_connection_description); buffer((char *)pConnection, iMin); record("devinit2: argv[3]: pConnection->Secure_password_valid[0]: '%c'\n", pConnection->Secure_password_valid[0]); record("devinit2: argv[3]: pConnection->Workstation_type: >%.14s<\n", pConnection->Workstation_type); record("devinit2: argv[5]: iEnv: %d\n", iEnv); if (0 < iEnv) { record("devinit2: argv[4]: pEnv[%d]:\n", iEnv); iMin = MIN(1024, iEnv); buffer(pEnv, iMin); } else { record("devinit2: argv[4]: pEnv[NULL]:\n"); } record("devinit2: argv[6]: pAllowConnect = '%c'\n", *pAllowConnect); record("devinit2: argv[7]: pAllowAutoSignon = '%c'\n", *pAllowAutoSignon); /********************************************************************/ /* Initialize these two flags to allow any clients that are not */ /* found in either mapping table. */ /********************************************************************/ *pAllowConnect = '1'; *pAllowAutoSignon = '0'; /********************************************************************/ /* Read fixed data area to get IP Address Mapping table. If this */ /* data area is not found, then screening should NOT be active! */ /********************************************************************/ if (dtaara.returned.Bytes_Returned) { record("devinit2: Data area IP Map file is: >%s<\n", pszDataArea); } else { record("devinit2: Screening function not active\n"); *pAllowConnect = '1'; *pAllowAutoSignon = '0'; /******************************************************************/ /* Exit */ /******************************************************************/ record("devinit2: <<<<< exit\n\n"); if (fDebug) { fclose(pDebug); } /* endif */ exit(0); } /* endif */ /********************************************************************/ /* Calculate the IP address for analysis */ /********************************************************************/ pSockAddr = (struct sockaddr_in *)pConnection->Internet_address; pClientIP = inet_ntoa(pSockAddr->sin_addr); iClientPort = pSockAddr->sin_port; record("devinit2: Client's IP: %s\n", pClientIP); record("devinit2: Client's Port: %d\n", iClientPort); /********************************************************************/ /* Set Client's IP address into compare structure */ /********************************************************************/ memset(&IP, 0x00, sizeof(IP)); sscanf(pClientIP, "%[^.].%[^.].%[^.].%s", IP.C1, IP.C2, IP.C3, IP.C4); /********************************************************************/ /* The following is a sample mapping table */ /********************************************************************/ /* # Client IP Profile Library Program Menu Device KBD CP CS C S E */ /* # ---------------- ---------- --------- -------- --------- ---------- --- --- --- - - - */ /* #| 111.222.333.444 | JSTEVENS | *USRPRF | *USRPRF | *USRPRF | JSTEVENS01 | USB | 37 | 697 | 1 | 1 | 0 | */ /* | *.*.*.* | # | # | # | # | # | # | # | # | 1 | 1 | 1 | */ /* # */ /* # TELNET Client IP Address MAP Mapping Rules: */ /* # */ /* # 1) A '#' in column 1 indicates a comment, and a '|' is a field delimiter. */ /* # 2) A '*' can be used as a wildcard in IP address hops (sub-nets). */ /* # 3) Any field with '#' means to NOT set a return value, and the field contents are left unchanged. */ /* # 4) Device field values of '#' mean TELNET auto-selects the device (QPADEVnnnn). */ /* # Device field must be '#' for wildcard (sub-net) entries! */ /* # 5) A -1 for CP and/or CS mean TELNET should use system default values. */ /* # 6) Fields C (AllowConnect), S (Auto-signon) and E (Encrypted auto-signon) values must be '1' or '0', */ /* # meaning true or false, respectively. */ /* # 7) AllowConnect means to allow a connection (can get a sign-on panel). */ /* # 8) Auto-signon means to bypass sign-on panel. */ /* # 9) Encrypted auto-signon means that the exit program checks the Connection Information for the */ /* # Client Password Validated flag to see if the client sent a valid encrypted password. If so, then */ /* # the 'S' Auto-signon flag is enabled. In general, the Encrypted field is used to validate clients */ /* # that send a profile (and password), as opposed to the map file supplying the profile. */ /* # */ /* # Legend: */ /* # */ /* # ClientIP - Client IP address must match this template for this record to apply. */ /* # Profile * - User profile used for auto-signon TELNET session. */ /* # Library - Current library for auto-signon TELNET session. */ /* # Program - Program to Call for auto-signon TELNET session. */ /* # Menu - Menu for auto-signon TELNET session. */ /* # Device * - This will be the Virtual Terminal device for this TELNET session. */ /* # KBD * - This will be the keyboard language type for the Virtual Terminal device. */ /* # CP * - This will be the code page for the Virtual Terminal device. */ /* # CS * - This will be the character set for the Virtual Terminal device. */ /* # C - Allow connection flag. Indicates if this client is allowed a sign-on panel. */ /* # S - Auto-signon flag. Indicates if this client can bypass the sign-on panel. */ /* # E - Encrypted password flag. Indicates if the client is required to send a */ /* # valid encrypted password before allowing the 'S' auto-signon flag function. */ /* # */ /* # * - this value can be negotiated by any TELNET client supporting RFC 1572 extensions. */ /*******************************************************************/ /* Read *LIBL/TELNET(MAP) file first, supersedes DISALLOW */ /*******************************************************************/ iMatch = 0; /*******************************************************************/ /* Read mapping table into local variable - up to 500 entries. */ /*******************************************************************/ sprintf(pcOpenFile, "%s(%s)", pszDataArea, pszAllow); if (NULL != (pFile = _Ropen(pcOpenFile, "rr")) ) { record("devinit2: Map file >%s< opened\n", pcOpenFile); /*****************************************************************/ /* Determine if source physical file or just physical file. */ /* Will be a 'Y' if source physical file, else 'N'. */ /*****************************************************************/ if (NULL != (pOFdBk = _Ropnfbk(pFile))) { if ('Y' == pOFdBk->src_file_indic) { /*************************************************************/ /* Step over first 12 bytes */ /*************************************************************/ iOffset = 12; } else { /*************************************************************/ /* Start at first byte */ /*************************************************************/ iOffset = 0; } /* endelse */ record("devinit2: SRCPF: '%c' iOffset: %d\n", pOFdBk->src_file_indic, iOffset); } /* endif */ record("devinit2: Attempting match for: %s\n", pClientIP); pFdBk = _Rreadf(pFile, pcRecord, sizeof(pcRecord), __NO_LOCK); i = 0; while (pFdBk->num_bytes > 0) { /***************************************************************/ /* If '#' character in first column, then this is a comment. */ /* Must offset by 12 bytes into a source physical file to step */ /* over record information. */ /***************************************************************/ if (pcRecord[iOffset] == '#') { record("devinit2: Line %d is a comment line\n", i+1); /*************************************************************/ /* Step mapping table index variable */ /*************************************************************/ i++; /*************************************************************/ /* Read next mapping table record */ /*************************************************************/ pFdBk = _Rreadn(pFile, pcRecord, sizeof(pcRecord), __NO_LOCK); continue; } /* endif */ record("devinit2: Reading line %d\n", i+1); /***************************************************************/ /* Read and parse mapping table. Must offset by 12 bytes into */ /* a source physical file to step over record information. */ /***************************************************************/ sscanf(&pcRecord[iOffset], "| %s | %s | %s | %s | %s | %s | %s " "| %s | %s " "| %c | %c | %c ", Map[i].ClientIP, Map[i].Profile, Map[i].Library, Map[i].Program, Map[i].Menu, Map[i].Device, Map[i].KBD, Map[i].cCP, Map[i].cCS, &(Map[i].AllowConnect), &(Map[i].AutoSignon), &(Map[i].SecureAutoSignon)); /***************************************************************/ /* Read IP address from table into compare structure */ /***************************************************************/ memset(&(IP.M1[0]), 0x00, (sizeof(IP)/2)); sscanf(Map[i].ClientIP, "%[^.].%[^.].%[^.].%s", IP.M1,IP.M2,IP.M3,IP.M4); buffer((char *)&IP, sizeof(IP)); /***************************************************************/ /* Compare IP address. Hops must match or be "*" wildcarded. */ /***************************************************************/ if ((!strcmp(IP.C1, IP.M1) || !strcmp("*", IP.M1)) && (!strcmp(IP.C2, IP.M2) || !strcmp("*", IP.M2)) && (!strcmp(IP.C3, IP.M3) || !strcmp("*", IP.M3)) && (!strcmp(IP.C4, IP.M4) || !strcmp("*", IP.M4))) { record("devinit2: IP Address match found!\n"); /*************************************************************/ /* Set exit prgm return values from mapping table values */ /* If field has '#' character, then do not set anything. */ /*************************************************************/ if ('#' != Map[i].Profile[0]) { /***********************************************************/ /* It is documented that this field must be padded */ /***********************************************************/ Pad(Map[i].Profile, 10); memcpy(pUser->User_profile, Map[i].Profile, 10); record("devinit2: Map Profile\n"); } else { /***********************************************************/ /* Don't do anything, leave return field unchanged. */ /***********************************************************/ record("devinit2: Default Profile (not mapped)\n"); } /* endif */ if ('#' != Map[i].Library[0]) { /***********************************************************/ /* It is documented that this field must be padded */ /***********************************************************/ Pad(Map[i].Library, 10); memcpy(pUser->Current_library, Map[i].Library, 10); record("devinit2: Map Library\n"); } else { /***********************************************************/ /* Don't do anything, leave return field unchanged. */ /***********************************************************/ record("devinit2: Default Library (not mapped)\n"); } /* endif */ if ('#' != Map[i].Program[0]) { /***********************************************************/ /* It is documented that this field must be padded */ /***********************************************************/ Pad(Map[i].Program, 10); memcpy(pUser->Program_to_call, Map[i].Program, 10); record("devinit2: Map Program\n"); } else { /***********************************************************/ /* Don't do anything, leave return field unchanged. */ /***********************************************************/ record("devinit2: Default Program (not mapped)\n"); } /* endif */ if ('#' != Map[i].Menu[0]) { /***********************************************************/ /* It is documented that this field must be padded */ /***********************************************************/ Pad(Map[i].Menu, 10); memcpy(pUser->Initial_menu, Map[i].Menu, 10); record("devinit2: Map Menu\n"); } else { /***********************************************************/ /* Don't do anything, leave return field unchanged. */ /***********************************************************/ record("devinit2: Default Menu (not mapped)\n"); } /* endif */ if ('#' != Map[i].Device[0]) { /***********************************************************/ /* It is documented that this field must be padded */ /***********************************************************/ Pad(Map[i].Device, 10); memcpy(pDevice->Device_name, Map[i].Device, 10); record("devinit2: Map Device\n"); } else { record("devinit2: Default Device (not mapped)\n"); } /* endif */ if ('#' != Map[i].KBD[0]) { memcpy(pDisplay->Keyboard_identifier, Map[i].KBD, 3); record("devinit2: Map KBD\n"); } else { /***********************************************************/ /* Don't do anything, leave return field unchanged. */ /***********************************************************/ record("devinit2: Default KBD (not mapped)\n"); } /* endif */ if ('#' != Map[i].cCP[0]) { /***********************************************************/ /* Convert the string version into a number */ /***********************************************************/ Map[i].CP = strtol(Map[i].cCP, &pEndPtr, 10); pDisplay->Code_page = Map[i].CP; record("devinit2: Map CP\n"); } else { /***********************************************************/ /* Let system default for this value. */ /***********************************************************/ Map[i].CP = -1; record("devinit2: Default CP (not mapped)\n"); } /* endif */ if ('#' != Map[i].cCS[0]) { /***********************************************************/ /* Convert the string version into a number */ /***********************************************************/ Map[i].CS = strtol(Map[i].cCS, &pEndPtr, 10); pDisplay->Char_set = Map[i].CS; record("devinit2: Map CS\n"); } else { /***********************************************************/ /* Let system default for this value. */ /***********************************************************/ Map[i].CS = -1; record("devinit2: Default CS (not mapped)\n"); } /* endif */ if ('#' != Map[i].AllowConnect) { *pAllowConnect = Map[i].AllowConnect; } else { record("devinit2: Default AllowConnect (not mapped)\n"); } /* endif */ /*************************************************************/ /* Retrieve the QRMTSIGN system value and decide if we want */ /* to allow auto-signon only for *VERIFY and *SAMEPRF */ /* settings. If so, we will default the pAllowAutoSignon */ /* flag to indicate if we should bother mapping the allow */ /* auto-signon flag from the MAP file. */ /* */ /* if QRMTSIGN is: *VERIFY, *SAMEPRF then */ /* set AllowAutoSignon to '1' (on) */ /* */ /* if QRMTSIGN is: *FRCSIGNON, *REJECT, lib/program then */ /* set AllowAutoSignon to '0' (off) */ /*************************************************************/ GetSysVal("QRMTSIGN", &pcRmtSign, &uiRmtSignLen); Trim(acRmtSign, MIN(uiRmtSignLen, sizeof(acRmtSign))); record("devinit2: QRMTSIGN value: >%s<\n", acRmtSign); if (NULL != strstr("*VERIFY *SAMEPRF ", acRmtSign)) { /***********************************************************/ /* Initialize to allow - map the flag from the MAP file */ /***********************************************************/ record("devinit2: QRMTSIGN allows auto-signon\n"); *pAllowAutoSignon = '1'; } else if (NULL != strstr("*FRCSIGNON *REJECT ",acRmtSign)) { /***********************************************************/ /* Initialize to disallow - ignore flag in the MAP file */ /***********************************************************/ record("devinit2: QRMTSIGN dis-allows auto-signon\n"); *pAllowAutoSignon = '0'; } else if ('*' != *acRmtSign) { /***********************************************************/ /* Initialize to disallow - ignore flag in the MAP file */ /***********************************************************/ record("devinit2: QRMTSIGN dis-allows auto-signon " "(lib/pgm)\n"); *pAllowAutoSignon = '0'; } else { /***********************************************************/ /* Initialize to disallow - ignore flag in the MAP file */ /***********************************************************/ record("devinit2: QRMTSIGN dis-allows auto-signon " "(indeterminant)\n"); *pAllowAutoSignon = '0'; } /* endelse */ record("devinit2: ==> Default *pAllowAutoSignon to '%c'\n", *pAllowAutoSignon); /*************************************************************/ /* Only map the allow auto-signon flag if the default value */ /* for pAllowAutoSignon is set to '1' by the QRMTSIGN code */ /* block above. That way we enforce the system value for */ /* our exit program checks (but this is optional, you could */ /* choose not to enforce the QRMTSIGN setting too). */ /* */ /* NOTE TO PROGRAMMER: */ /* */ /* You have a choice here about which pre-processor block to */ /* compile. You choice determines whether or not we use the */ /* QRMTSIGN setting in this exit program. */ /*************************************************************/ #if 0 /*************************************************************/ /* Enforce QRMTSIGN setting */ /*************************************************************/ record("devinit2: System value QRMTSIGN being enforced\n"); if (('#' != Map[i].AutoSignon) && ('1' == *pAllowAutoSignon)) { #else /*************************************************************/ /* Do not enforce QRMTSIGN setting */ /*************************************************************/ record("devinit2: System value QRMTSIGN being ignored\n"); if ('#' != Map[i].AutoSignon) { #endif /***********************************************************/ /* Check if want to enforce encrypted password validation */ /***********************************************************/ if (('1' == Map[i].SecureAutoSignon) || ('#' == Map[i].SecureAutoSignon)) { record("devinit2: Auto-signon requires encrypted " "password\n"); /*********************************************************/ /* Encrypted password validation IS in effect. */ /* Only encrypted passwords are allowed to auto-signon */ /*********************************************************/ if ('2' == pConnection->Secure_password_valid[0]) { /*******************************************************/ /* Encrypted password was valid. Allow client. */ /*******************************************************/ record("devinit2: Encrypted password valid\n"); record("devinit2: Map Auto-signon\n"); *pAllowAutoSignon = Map[i].AutoSignon; } else if ('1' == pConnection->Secure_password_valid[0]) { record("devinit2: Clear-text password valid, but " "encrypted required\n"); record("devinit2: Dis-allow Auto-signon\n"); *pAllowAutoSignon = '0'; } else { /*******************************************************/ /* Password was not encrypted or valid. Deny client. */ /*******************************************************/ record("devinit2: No password or password not valid\n"); record("devinit2: Dis-allow Auto-signon\n"); *pAllowAutoSignon = '0'; } /* endelse */ } else { /*********************************************************/ /* Don't care if we have a password. */ /*********************************************************/ record("devinit2: Map Auto-signon\n"); *pAllowAutoSignon = Map[i].AutoSignon; } /* endelse */ } else { /***********************************************************/ /* Don't do anything, leave return field unchanged. */ /***********************************************************/ record("devinit2: Default Auto-signon (not mapped)\n"); } /* endif */ /*************************************************************/ /* Show me the values selected (debug) */ /*************************************************************/ record("devinit2: Mapped output values being returned:\n"); record("devinit2: Map[%d].Profile: >%.10s<\n", i, Map[i].Profile); record("devinit2: Map[%d].Library: >%.10s<\n", i, Map[i].Library); record("devinit2: Map[%d].Program: >%.10s<\n", i, Map[i].Program); record("devinit2: Map[%d].Menu: >%.10s<\n", i, Map[i].Menu); record("devinit2: Map[%d].Device: >%.10s<\n", i, Map[i].Device); record("devinit2: Map[%d].KBD: >%.3s<\n", i, Map[i].KBD); record("devinit2: Map[%d].CP: >%.3s<\n", i, Map[i].cCP); record("devinit2: Map[%d].CS: >%.3s<\n", i, Map[i].cCS); record("devinit2: Map[%d].AllowConnect: '%c'\n", i, Map[i].AllowConnect); record("devinit2: Map[%d].AutoSignon: '%c'\n", i, Map[i].AutoSignon); record("devinit2: Map[%d].SecureAutoSignon: '%c'\n", i, Map[i].SecureAutoSignon); /*************************************************************/ /* Since a match was found, break out of loop */ /*************************************************************/ iMatch = 1; break; } /* endif */ /***************************************************************/ /* Step mapping table index variable */ /***************************************************************/ i++; /***************************************************************/ /* Read next mapping table record */ /***************************************************************/ pFdBk = _Rreadn(pFile, pcRecord, sizeof(pcRecord), __NO_LOCK); } /* endwhile */ /*****************************************************************/ /* Close mapping table file */ /*****************************************************************/ if (NULL != pFile) { record("devinit2: File >%s< closed\n", pcOpenFile); _Rclose(pFile); } /* endif */ } else { /*****************************************************************/ /* Tell me about errors in case we are debugging */ /*****************************************************************/ record("devinit2: Error opening file >%s<!\n", pcOpenFile); } /* endif */ /*******************************************************************/ /* # Client IP Description */ /* # ********* ****************************************************/ /* *.*.*.* # All users not listed in ALLOW will be refused. */ /* # */ /* # TELNET Client IP Address DISALLOW Mapping Rules: */ /* # */ /* # This file lists IP addresses that will NOT be allowed to */ /* # connect to the TELNET server. */ /* # */ /* # You must create a SRCPF (or PF) file object and download this */ /* # file as member ALLOW. The name of the file must be identified*/ /* # inside character *DTAARA object QTCP/QTGEXITS in the form */ /* # 'filelib/filename'. Do not put quotes in the *DTAARA. */ /* # Append '*MSGQ' and '*FILE' strings if you want messages logged*/ /* # to QTCP message queue and file member LOG, respectively. */ /* # */ /* # User profile QTCP must have at least *READ private authority */ /* # to this file, *USE or *CHANGE is recommended for logging. */ /* # */ /* # 1) Any line with a '#' in column 1 is a comment. */ /* # 2) A '*' serves as a wildcard in IP address hops. */ /* # 3) A '#' delimits an IP address and description on a line. */ /* # 4) Host and domain names are not allowed. */ /* # 5) Place wildcard entries last since file is read top to */ /* # bottom. The first matching IP address applies. Put */ /* # comment lines after IP addresses to find matches quicker. */ /* # 6) The ALLOW file has priority over the DISALLOW file, use */ /* # care when putting wildcards into the ALLOW file. */ /* # */ /* # QTCP/QTGEXITS Data Area Rules: */ /* # - Required to have library and name of map file as the first */ /* # string in the *DTAARA, separated by a slash. */ /* # - To log a message to QTCP user *MSGQ, append a blank */ /* # delimited '*MSGQ' string to the *DTAARA contents. */ /* # - To log a message the file member LOG, append a blank */ /* # delimited '*FILE' string to the *DTAARA contents. */ /* # */ /* # QTCP/QTGEXITS Data Area Example: */ /* # 'filelib/filename msgqflag fileflag' */ /* # 'USEREXIT/TELNET *MSGQ' */ /* # 'MYLIB/TELNET *MSGQ *FILE' */ /* # 'MYLIB/TELNET *FILE *DEBUG' */ /*******************************************************************/ /*******************************************************************/ /* Read mapping table into local variable - up to 500 entries. */ /*******************************************************************/ if (!iMatch) { /*****************************************************************/ /* Read *LIBL/TELNET(DISALLOW) file next, if no match from MAP */ /*****************************************************************/ iMatch = 0; memset(pcRecord, 0x00, sizeof(pcRecord)); /*****************************************************************/ /* Read mapping table into local variable - up to 1000 entries. */ /*****************************************************************/ sprintf(pcOpenFile, "%s(%s)", pszDataArea, pszDisallow); if (NULL != (pFile = _Ropen(pcOpenFile, "rr")) ) { record("devinit2: Map file >%s< opened\n", pcOpenFile); /***************************************************************/ /* Determine if source physical file or just physical file. */ /* Will be a 'Y' if source physical file, else 'N'. */ /***************************************************************/ if (NULL != (pOFdBk = _Ropnfbk(pFile))) { record("devinit2: pOFdBk->src_file_indic: %c\n", pOFdBk->src_file_indic); if ('Y' == pOFdBk->src_file_indic) { /***********************************************************/ /* Step over first 12 bytes */ /***********************************************************/ iOffset = 12; } else { /***********************************************************/ /* Start at first byte */ /***********************************************************/ iOffset = 0; } /* endelse */ } /* endif */ record("devinit2: Attempting match for: %s\n", pClientIP); i = 0; pFdBk = _Rreadf(pFile, pcRecord, sizeof(pcRecord), __NO_LOCK); while (pFdBk->num_bytes > 0) { /*************************************************************/ /* If '#' character in first column, then this is a comment. */ /* Must offset by 12 bytes into a source physical file to */ /* step over record information. */ /*************************************************************/ if (pcRecord[iOffset] == '#') { record("devinit2: Line %d is a comment line\n", i+1); /***********************************************************/ /* Step mapping table index variable */ /***********************************************************/ i++; /***********************************************************/ /* Read next mapping table record */ /***********************************************************/ pFdBk = _Rreadn(pFile, pcRecord, sizeof(pcRecord),__NO_LOCK); continue; } /* endif */ Trim(pcRecord, sizeof(pcRecord)-1); record("devinit2: Reading line %d\n", i+1); record("devinit2: pcRecord:\n"); buffer(pcRecord, strlen(pcRecord)); /*************************************************************/ /* Read and parse mapping table. Must offset by 12 bytes in */ /* a source physical file to step over record information. */ /* Comments ('#' delimiter) may exist on the line. */ /*************************************************************/ sscanf(&pcRecord[iOffset], " %s #", Map[i].ClientIP); /*************************************************************/ /* Read IP address from table into compare structure */ /*************************************************************/ memset(&(IP.M1[0]), 0x00, (sizeof(IP)/2)); sscanf(Map[i].ClientIP, "%[^.].%[^.].%[^.].%s", IP.M1,IP.M2,IP.M3,IP.M4); record("devinit2: IP Address compare structure:\n"); buffer((char *)&IP, sizeof(IP)); /*************************************************************/ /* Compare IP address. Hops must match or be "*" wildcarded.*/ /*************************************************************/ if ((!strcmp(IP.C1, IP.M1) || !strcmp("*", IP.M1)) && (!strcmp(IP.C2, IP.M2) || !strcmp("*", IP.M2)) && (!strcmp(IP.C3, IP.M3) || !strcmp("*", IP.M3)) && (!strcmp(IP.C4, IP.M4) || !strcmp("*", IP.M4))) { record("devinit2: Match found! Client is NOT allowed\n"); /***********************************************************/ /* Since a match was found, this client is NOT allowed in */ /***********************************************************/ *pAllowConnect = '0'; *pAllowAutoSignon = '0'; /***********************************************************/ /* Since a match was found, set flag and break out of loop */ /***********************************************************/ iMatch = 1; break; } /* endif */ record("devinit2: No match found\n"); /*************************************************************/ /* Step mapping table index variable */ /*************************************************************/ i++; /*************************************************************/ /* Read next mapping table record */ /*************************************************************/ pFdBk = _Rreadn(pFile, pcRecord, sizeof(pcRecord), __NO_LOCK); } /* endwhile */ record("devinit2: End of file reached\n"); /***************************************************************/ /* Close mapping table file */ /***************************************************************/ if (NULL != pFile) { record("devinit2: File >%s< closed\n", pcOpenFile); _Rclose(pFile); } /* endif */ } else { /***************************************************************/ /* Tell me about errors in case we are debugging */ /***************************************************************/ record("devinit2: Error opening file >%s<!\n", pcOpenFile); } /* endif */ } /* if (!iMatch) */ /*******************************************************************/ /* Post a success or failure message to user QTCP message queue */ /*******************************************************************/ record("devinit2: Posting connect message to QTCP msg queue\n"); time(<ime); sprintf(Time, "%.24s", ctime(<ime)); if ('1' == *pAllowConnect) { if ('#' != Map[i].Device[0]) { /***************************************************************/ /* If a specific device was requested, let us know the name */ /***************************************************************/ Trim(Map[i].Device, 10); sprintf(acMsg, "TELNET client %.15s port %d connect to %s at %.24s.", pClientIP, iClientPort, Map[i].Device, Time); } else { sprintf(acMsg, "TELNET client %.15s port %d connect at %.24s.", pClientIP, iClientPort, Time); } /* endif */ /*****************************************************************/ /* Change the message if it was allowed to auto-sigon */ /*****************************************************************/ if ('1' == *pAllowAutoSignon) { if ('#' != Map[i].Device[0]) { /*************************************************************/ /* If a specific device was requested, let us know the name */ /*************************************************************/ Trim(Map[i].Device, 10); sprintf(acMsg, "TELNET client %.15s port %d auto-signon to %s at %.24s.", pClientIP, iClientPort, Map[i].Device, Time); } else { sprintf(acMsg, "TELNET client %.15s port %d auto-signon at %.24s.", pClientIP, iClientPort, Time); } /* endif */ } /* endelse */ } else { sprintf(acMsg, "TELNET client %.15s port %d refused at %.24s.", pClientIP, iClientPort, Time); } /* endelse */ /*******************************************************************/ /* If data area found and special string found, send connect msg */ /*******************************************************************/ if (fLogMsg) { memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = sizeof(esErrCode);/* Ignore exceptions*/ QMHSNDM("CPF9897", QCPFMSG, acMsg, strlen(acMsg), MSG_INFO, "QTCP *USER ", 1, acMsgReply, acMsgKey,&esErrCode); } /* endif */ /*******************************************************************/ /* If data area found and special string found, log connect msg */ /*******************************************************************/ if (fLogFile) { /*****************************************************************/ /* Open database file for permanent log of test results. */ /*****************************************************************/ sprintf(pcOpenFile, "%s(%s)", pszDataArea, pszLog); sprintf(pcOpenParms, "a, lrecl=%d, recfm=v", RECORD_WIDTH); pLog=fopen(pcOpenFile, pcOpenParms); fwrite(acMsg, 1, strlen(acMsg), pLog); fclose(pLog); } /* endif */ /*******************************************************************/ /* Exit */ /*******************************************************************/ record("devinit2: <<<<< exit\n\n"); if (fDebug) { fclose(pDebug); } /* endif */ exit(0); } /* End main */ /**********************************************************************/ /*@function buffer() */ /* */ /* Parameters: */ /* */ /* char *buffer - points at buffer to dump */ /* int length - length of buffer to dump */ /* */ /* Description: */ /* */ /* Dumps out a buffer in both hex and readable form. */ /* */ /* 1934D8E3 D4E3E2D7 C3F0F0F2 40404040 |..QTMTSPC002 | */ /* 40404040 40404040 40404040 40404040 | | */ /* 00000000 00000000 00000000 00000000 |................| */ /**********************************************************************/ void buffer(char *Buffer, int Length) { int iRow = 0; int iCol = 0; int iLast = 0; int iTotalRows = 0; int iLen; int iBytes; unsigned char c; char acLine[20]; char Buff[512]; char *pBuff = Buff; /********************************************************************/ /* If debug is not active, nothing to do here... */ /********************************************************************/ if (!fDebug || (Buffer == (char *)NULL)) { record("buffer: Buffer is NULL\n"); return; } /* endif */ /********************************************************************/ /* Calculate total rows to fit all bytes */ /********************************************************************/ iTotalRows = (Length + 15) / 16; /********************************************************************/ /* Primary loop for rows */ /********************************************************************/ for (iRow = 0; iRow < iTotalRows; iRow++) { /******************************************************************/ /* Offset into line by 4 blank characters */ /******************************************************************/ *pBuff = ' '; pBuff++; *pBuff = ' '; pBuff++; *pBuff = ' '; pBuff++; *pBuff = ' '; pBuff++; /******************************************************************/ /* Print 16 bytes of the buffer as the hexadecimal dump section */ /* Primary loop for columns */ /******************************************************************/ for (iCol = 0; iCol < 16; iCol++) { /****************************************************************/ /* One printable character displays two hexadecimal characters */ /****************************************************************/ if (iCol + (iRow * 16) >= Length) { /**************************************************************/ /* No more data - just fill remaining positions with blanks */ /**************************************************************/ *pBuff = ' '; pBuff++; *pBuff = ' '; pBuff++; } else { /**************************************************************/ /* Print actual data */ /**************************************************************/ sprintf(pBuff, "%02X", Buffer[iCol + (iRow * 16)]); pBuff += 2; } /* endelse */ /****************************************************************/ /* Add a space separator every 4 hex bytes */ /****************************************************************/ if (iCol % 4 == 3) { *pBuff = ' '; pBuff++; /* Pad character */ } /* endif */ } /* endfor */ /******************************************************************/ /* Print the same 16 bytes as the "readable" text section */ /******************************************************************/ *pBuff = ' '; pBuff++; *pBuff = '|'; pBuff++; /* Left text bar */ /******************************************************************/ /* Print 16 bytes of the buffer as readable text section */ /* Secondary loop for columns */ /******************************************************************/ iLast = 0; /* Bytes last line */ for (iCol = 0; iCol < 16; iCol++) { if (iCol + (iRow * 16) >= Length) { /**************************************************************/ /* No more data - just fill remaining positions with blanks */ /**************************************************************/ *pBuff = ' '; pBuff++; } else { /**************************************************************/ /* Print actual data */ /**************************************************************/ iLast++; /* Actual data */ c = Buffer[iCol + (iRow * 16)]; if (isprint(c)) { *pBuff = c; pBuff++; /* Actual char */ } else if (c == 0x40) { *pBuff = ' '; pBuff++; /* Blank char */ } else { *pBuff = '.'; pBuff++; /* Unprintable */ } /* endelse */ } /* endelse */ } /* endfor */ *pBuff = '|'; pBuff++; /* Right text bar */ /******************************************************************/ /* Build the finished output */ /******************************************************************/ memset(acLine, 0x00, sizeof(acLine)); iBytes = (iRow * 16) + iLast; sprintf(acLine," Byte %d\n", iBytes); /* Byte count */ strcpy(pBuff, acLine); pBuff = &Buff[0]; /* Reset pointer */ printf("%s", Buff); } /* endfor */ return; } /**********************************************************************/ /*@function record() */ /* */ /* Parameters: */ /* */ /* variable arguments */ /* */ /* Log test result entry to standard out (normally console). This */ /* will only occur if DEBUG is active. If this occurs in a batch */ /* job, a spooled file is usually created holding output. */ /**********************************************************************/ void record(char *Format, ...) { va_list arg_ptr; int iLen; char record[512]; /********************************************************************/ /* If debug is not active, nothing to do here... */ /********************************************************************/ if (!fDebug) { return; } /* endif */ va_start(arg_ptr, Format); iLen = vsprintf(record, Format, arg_ptr); va_end(arg_ptr); record[iLen] = 0x00; printf("%s", record); return; } /**********************************************************************/ /*@function handler() */ /* */ /* Parameters: */ /* */ /* int iSignal - value of the signal which caused the handler to be */ /* invoked. SIGABRT, SIGTERM, etc. */ /**********************************************************************/ void handler(int iSignal) { ERRSTRUCT esErrCode; _INTRPT_Hndlr_Parms_T Signal; _INTRPT_Hndlr_Parms_T *pSignal = &Signal; char *pszMsgFile = NULL; struct { int Bytes_Return; int Bytes_Available; int Length_Message_Returned; int Length_Message_Available; int Length_Help_Returned; int Length_Help_Available; char Message[256]; char Message_Help[256]; } rtvm0100; char *Signals[] = { "SIGABRT", /* 1 Abnormal termination */ "SIGFPE", /* 2 Erroneous arithmetic operation */ "SIGILL", /* 3 Invalid hardware instruction */ "SIGINT", /* 4 Interactive attention signal */ "SIGSEGV", /* 5 Invalid memory reference */ "SIGTERM", /* 6 Termination signal */ "SIGUSR1", /* 7 Application defined signal 1 */ "SIGUSR2", /* 8 Application defined signal 2 */ "SIGIO", /* 9 I/O possible, or completed */ "SIGALL", /* 10 All signals */ "SIGOTHER", /* 11 ILE C/400 signal */ "SIGKILL", /* 12 Termination signal(cannot be caught,ignored)*/ "SIGPIPE", /* 13 Write on a pipe with no readers */ "SIGALRM", /* 14 Timeout signal */ "SIGHUP", /* 15 Hangup detected on controlling terminal */ "SIGQUIT", /* 16 Interactive termination signal */ "SIGSTOP", /* 17 Stop signal (cannot be caught or ignored) */ "SIGTSTP", /* 18 Interactive stop signal */ "SIGCONT", /* 19 Continue if stopped */ "SIGCHLD", /* 20 Child process terminated or stopped */ "SIGTTIN", /* 21 Background read from controlling terminal */ "SIGTTOU", /* 22 Background write to controlling terminal */ "SIGURG", /* 23 High bandwidth data is available at socket */ "SIGPOLL", /* 24 Pollable event */ "SIG25", /* 25 Not defined */ "SIG26", /* 26 Not defined */ "SIG27", /* 27 Not defined */ "SIG28", /* 28 Not defined */ "SIG29", /* 29 Not defined */ "SIG30", /* 30 Not defined */ "SIG31", /* 31 Not defined */ "SIGBUS", /* 32 Bus error (specification exception) */ "SIGDANGER", /* 33 system crash imminent */ "SIGPRE", /* 34 programming exception */ "SIGSYS", /* 35 Bad system call */ "SIGTRAP", /* 36 Trace/Breakpoint trap */ "SIGPROF", /* 37 Profiling timer expired */ "SIGVTALRM", /* 38 Virtual timer expired */ "SIGXCPU", /* 39 CPU time limit exceeded */ "SIGXFSZ" /* 40 File size limit exceeded */ }; record("handler: Caught signal %s\n", Signals[iSignal-1]); /********************************************************************/ /* Set file scoped flag so caller knows exception occurred */ /********************************************************************/ fException = fTrue; /********************************************************************/ /* Try and pull out the message text */ /********************************************************************/ _GetExcData(&Signal); if (!memcmp(Signal.Msg_Id, "TCP", 3) ) { /******************************************************************/ /* TCP Apps (and Stack with recursive call) message file */ /******************************************************************/ pszMsgFile = QTCPMSGF; } else if (!memcmp(Signal.Msg_Id, "C2M16", 5) ) { /******************************************************************/ /* ILE-C message file (primarily for signals, if use raise/abort) */ /******************************************************************/ pszMsgFile = QC2MSGF; } else if (!memcmp(Signal.Msg_Id, "CEE99", 5) ) { /******************************************************************/ /* ILE-C message file */ /******************************************************************/ pszMsgFile = QCEEMSG; } else { /******************************************************************/ /* Most CPFxxxx messages */ /******************************************************************/ pszMsgFile = QCPFMSG; } /* endif */ memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = sizeof(esErrCode); /* Ignore exceptions */ QMHRTVM(&rtvm0100, /* Message information */ sizeof(rtvm0100), /* Length of message information */ "RTVM0100", /* Format name */ Signal.Msg_Id, /* Message identifier */ pszMsgFile, /* Qualified message file name */ Signal.Ex_Data, /* Message data */ sizeof(Signal.Ex_Data), /* Length of message data */ "*YES ", /* Replace substitution values */ "*NO ", /* Return format control */ &esErrCode, /* Error Code */ "*MSGID ", /* Retrieve option */ 0, /* Convert to CCSID */ 0); /* Message data CCSID */ if (esErrCode.Bytes_Available || !rtvm0100.Length_Message_Returned) { record("handler: Escape message not found\n"); } else { record("handler: Escape message:\n"); buffer(rtvm0100.Message, rtvm0100.Length_Message_Returned); } /* endelse */ /********************************************************************/ /* Delete msg from job log if caller so desires (flag indicator) */ /********************************************************************/ if (fRemoveEscapeMsg) { record("handler: Remove escape message from job log\n"); memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = sizeof(esErrCode);/* Ignore exceptions */ QMHCHGEM(&Signal.Target, 0, &Signal.Msg_Ref_Key, "*REMOVE ", "", 0, &esErrCode); } else { record("handler: Escape message not removed from job log\n"); } /* endif */ record("handler: Reset signals -> handler\n"); signal(SIGALL, &handler); return; } /* Function Specification *********************************************/ /* */ /* Function Name: cl_command */ /* */ /* Descriptive Name: run any CL command as if on a command line. */ /* */ /* char * Command - null terminated string */ /* */ /* End Function Specification *****************************************/ void cl_command(char *Command) /* Entry point */ { /********************************************************************/ /* Local Variables */ /********************************************************************/ ERRSTRUCT esErrCode; Qca_PCMD_CPOP0100_t cpop0100; char cpop0100_out[512]; int cpop0100_len; /********************************************************************/ /* typedef _Packed struct Qca_PCMD_CPOP0100 { */ /* int Command_Process_Type; */ /* char DBCS_Data_Handling; */ /* char Prompter_Action; */ /* char Command_String_Syntax; */ /* char Message_Key[4]; */ /* char Reserved[9]; */ /* } Qca_PCMD_CPOP0100_t; */ /********************************************************************/ record("cl_command: Command:\n"); buffer(Command, strlen(Command)); memset(cpop0100_out, 0x00, sizeof(cpop0100_out)); memset(&cpop0100, 0x00, sizeof(Qca_PCMD_CPOP0100_t)); cpop0100.Command_Process_Type = 0; cpop0100.DBCS_Data_Handling = '0'; cpop0100.Prompter_Action = '0'; cpop0100.Command_String_Syntax = '0'; memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = 0L; /* Force exception to be signalled */ QCAPCMD(Command, strlen(Command), &cpop0100, sizeof(Qca_PCMD_CPOP0100_t), "CPOP0100", cpop0100_out, sizeof(cpop0100_out) - 1, &cpop0100_len, &esErrCode); return; } /* Function Specification *********************************************/ /* */ /* Function Name: Pad */ /* */ /* char *pszString - null terminated string, may or may not have */ /* blanks */ /* */ /* int iLength - maximum length to pad the string (includes NULL */ /* terminator). A NULL terminator WILL be added! */ /* */ /* iLength should be where you want the NULL term. */ /* */ /* Thus, iLength=10 means array position 10, not 9. */ /* */ /* Descriptive Name: pads a string with blanks */ /* */ /* End Function Specification *****************************************/ int Pad(char *pszString, int iLength) { int iLen = strlen(pszString); if (iLength <= iLen) { return(iLen); } /* endif */ /********************************************************************/ /* Start at the end of the string, and add blanks. */ /********************************************************************/ while (iLen < iLength) { pszString[iLen] = ' '; iLen++; } /* endwhile */ /********************************************************************/ /* Add the null terminator to make it a 'C' string. */ /********************************************************************/ pszString[iLen] = 0x00; iLen = strlen(pszString); return(iLen); } /* Function Specification *********************************************/ /* */ /* Function Name: Trim */ /* */ /* char *pszString - null terminated string, may have trailing */ /* blanks */ /* */ /* int iLength - start position of the string to be trimmed. This */ /* need not be the length of the string. You should */ /* specify the position of any NULL terminator if you */ /* are trimming a 'C' string. */ /* */ /* iLength should be where you want the NULL term. */ /* */ /* Thus, iLength=10 means array position 10, not 9. */ /* */ /* Descriptive Name: pads a string with blanks */ /* */ /* End Function Specification *****************************************/ int Trim(char *pszString, int iLength ) { int iLen = iLength - 1; /********************************************************************/ /* While we are not at the 1st char AND (a char is whitespace OR */ /* 0x00) keep backing up from end of string */ /********************************************************************/ pszString[iLength] = 0x00; while ( (iLen >= 0) && ((isspace(pszString[iLen]) ) || (0x00 == pszString[iLen])) ) { iLen--; } /* endwhile */ /********************************************************************/ /* if iLen unchanged, it means no trimming was done (no whitespace */ /* found). */ /********************************************************************/ if (iLen == (iLength-1)) { return(iLen); } /* endif */ /********************************************************************/ /* if iLen >= 0, we found 1 or more chars, so mark end with a */ /* NULL terminator. */ /********************************************************************/ else if (iLen >= 0) { pszString[iLen + 1] = 0x00; } /* endif */ /********************************************************************/ /* else iLen < 0, which means we didn't find anything but whitespace*/ /********************************************************************/ else { pszString[0] = 0x00; } /* endelse */ iLen = strlen(pszString); return(iLen); } /**********************************************************************/ /* @function: GetSysVal() */ /* */ /* Parameters: */ /* */ /* pszSysVal - pointer to system value to be retrieved */ /* pszData - pointer to callers variable into which actual */ /* value will be copied */ /* puiDataLen - pointer to callers variable into which actual */ /* length information will be copied */ /* */ /* Description: */ /* */ /* Reads any system variable or configuration value and returns a */ /* pointer to its setting. */ /* */ /* This function is limited in that it is only intended to retrieved a*/ /* single system value at a time. Multi-array output is possible, if */ /* you want to try it, but I actually only intend to use this to find */ /* system values with character strings. If your output is an int, */ /* then you need to cast the output to an int in the calling routine, */ /* since only string manipulation is being done here. Better know */ /* what kind of output your getting! */ /* */ /* Output over 300 chars is truncated. A pointer to the output is */ /* also returned, for convenience. */ /* */ /* The following are useful: */ /* */ /* QCCSID INT Coded Char Set Id */ /* QCHRID CHAR(20) Char Set and Code Page */ /* QIGC CHAR(1) DBCS Installed */ /* QIGCCDEFNT CHAR(20) Double-byte Code Font Name */ /* QLANGID CHAR(3) Language Identifier */ /* QINACTITV CHAR(10) Inactive Job Timeout */ /* QLMTDEVSSN CHAR(1) Limit Device Session */ /* QMAXSGNACN CHAR(1) Limit Security Officer */ /* QMAXSIGN CHAR(6) Maximum Sign-On action */ /* QPWDEXPITV CHAR(6) Days Password Valid */ /* QPWDMAXLEN INT Maximum Password Length */ /* QPWDMINLEN INT Minimum Password Length */ /* QDATE CHAR(7) System Date */ /* QTIME CHAR(9) System Time */ /* QDAY CHAR(3) Day */ /* QHOUR CHAR(2) Hour */ /* QMINUTE CHAR(2) Minute */ /* QSECOND CHAR(2) Second */ /* QMONTH CHAR(2) Month */ /* QYEAR CHAR(2) Year */ /* QMODEL CHAR(4) System Model */ /* QSECURITY CHAR(2) Security Level */ /* QSYSLIBL ARRAY(25) of CHAR(10) System Library List */ /* QUSRLIBL ARRAY(25) of CHAR(10) User Library List */ /**********************************************************************/ void GetSysVal(char *pszSysVal,char **ppcData,unsigned int *puiDataLen) { ERRSTRUCT esErrCode; /********************************************************************/ /* Type Definition for the Format of System Values to Retrieve. */ /********************************************************************/ typedef _Packed struct Qwc_Rsval_Sys_Value_Table { char System_Value[10]; char Type_Data; char Information_Status; int Length_Data; char Data[300]; } Qwc_Rsval_Sys_Value_Table_t; /********************************************************************/ /* Type Definition for the Format of Data Returned. */ /********************************************************************/ typedef _Packed struct Qwc_Rsval_Data_Rtnd { int Number_Sys_Vals_Rtnd; int Offset_Sys_Val_Table[1]; Qwc_Rsval_Sys_Value_Table_t System_Values[1]; } Qwc_Rsval_Data_Rtnd_t; /********************************************************************/ /* Type Definition for the Input Format of System Values. */ /********************************************************************/ typedef _Packed struct Input { char System_Value[10]; char Null_Terminator; } Input_t; /********************************************************************/ /* Must declare static or else stack operations may wipe out */ /* pointer in caller. */ /********************************************************************/ static Qwc_Rsval_Data_Rtnd_t OutputStruct; static Qwc_Rsval_Sys_Value_Table_t *ValueTable; static Input_t InputStruct[1]; /********************************************************************/ /* Pad() will step on InputStruct[0].Null_Terminator, which is OK */ /********************************************************************/ memcpy(InputStruct[0].System_Value, pszSysVal, 10); Pad(InputStruct[0].System_Value, sizeof(Input_t)); /********************************************************************/ /* Retrieve values one at a time. */ /********************************************************************/ memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = 0L; /* Force exception to be signalled */ QWCRSVAL(&OutputStruct, /* Receiver variable */ sizeof(OutputStruct), /* Length of receiver variable */ 1, /* Number of system values to rtv */ InputStruct, /* System value names */ &esErrCode); /* Error code */ ValueTable = (Qwc_Rsval_Sys_Value_Table_t *) ((char *)&OutputStruct + OutputStruct.Offset_Sys_Val_Table[0]); /********************************************************************/ /* Did we get any output? */ /********************************************************************/ if (0 < OutputStruct.Number_Sys_Vals_Rtnd) { /******************************************************************/ /* If I don't do this ppcData pointer-to-pointer hack, V3R6+ gets */ /* MCH6802 - Literal values cannot be changed. */ /******************************************************************/ *puiDataLen = ValueTable->Length_Data; memcpy(*ppcData, ValueTable->Data, ValueTable->Length_Data); } else { /******************************************************************/ /* Copy a NULL value into output */ /******************************************************************/ *ppcData = ""; *puiDataLen = 0; } /* endif */ return; } #undef _DEVINIT2_C /**********************************************************************/ /* END OF DEVINIT2.C */ /**********************************************************************/
Return to the TELNET exit program road map:
This is a working example of a TELNET termination exit program. It determines what happens when a TELNET user logs off of your AS/400.
Return to the TELNET exit program road map:
/* Module Description *************************************************/ /* */ /**********************************************************************/ /* */ /* DISCLAIMER: This material contains programming source code for */ /* your consideration. These examples have not been thoroughly */ /* tested under all conditions. IBM, therefore, cannot guarantee or */ /* imply reliability, serviceability, performance or function of */ /* these programs. All programs contained herein are provided to you */ /* "AS IS". THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */ /* FOR A PARTICULAR PURPOSE ARE EXPRESSLY DISCLAIMED. */ /* */ /* LIMITATION OF LIABILITY: Neither IBM nor the author shall be */ /* liable for any claims or damages whatsoever, including property */ /* damage, personal injury, intellectual property infringement, loss */ /* of profits, or interruption of business, or for any special, */ /* consequential or incidental damages, however caused, whether */ /* arising out of breach of warranty, contract, tort (including */ /* negligence), strict liability, or otherwise. */ /* */ /**********************************************************************/ /* */ /* Source File Name: devterm.c */ /* */ /* Module Name: TELNET Device Termination exit program. */ /* */ /* Source File Description: */ /* */ /* This module contains functions to allow a client browser to */ /* call AS/400 device termination processing for Virtual Devices. */ /* */ /* Client access controls are defined via access lists, where */ /* the client IP address determines if a connection is allowed or */ /* dis-allowed. Customers indicate the library/name of the access */ /* list/log *FILE object via character data area QUSRSYS/QTGEXITS. */ /* */ /* Data area layout expected: */ /* - required to have library and name of IP address mapping file */ /* as the first string in the *DTAARA, separated by a slash. */ /* - if a connect msg to QTCP *MSGQ is desired, append a *MSGQ */ /* string to the *DTAARA contents. */ /* - if a connect msg to SRCPF or PF member LOG is desired, append */ /* a *FILE string to the *DTAARA contents. */ /* - if debug msgs to SRCPF or PF member DEBUG are desired, append */ /* a *DEBUG string to the *DTAARA contents. */ /* */ /* Example: */ /* 'filelib/filename msgqflag fileflag debugflag' */ /* 'USEREXIT/TELNET *MSGQ *DEBUG' */ /* 'USEREXIT/TELNET *FILE *DEBUG' */ /* 'USEREXIT/TELNET *MSGQ *FILE' */ /* 'USEREXIT/TELNET *MSGQ *FILE *DEBUG' */ /* */ /* For termination purposes, this program will log a message to the */ /* QTCP user profile message queue and also to the *FILE object. */ /* */ /* End Module Description *********************************************/ #pragma comment (copyright, \ "(C) Copyright IBM Corp. 1997. \ All rights reserved. \ US Government Users Restricted Rights - \ Use, duplication or disclosure restricted \ by GSA ADP Schedule Contract with IBM Corp. \ Licensed Materials - Property of IBM.") #define _DEVTERM_C /**********************************************************************/ /* All file scoped includes go here */ /**********************************************************************/ /**********************************************************************/ /* Base Operating System Option 13 (QSYSINC) CUE's must be installed */ /**********************************************************************/ #ifndef ETGDEVEX_h #define ETGDEVEX_h /**********************************************************************/ /* Type Definition for the User Description Information */ /**********************************************************************/ typedef _Packed struct Qtg_User_Description { int Length_user_description; char User_profile[10]; char Current_library[10]; char Program_to_call[10]; char Initial_menu[10]; } Qtg_User_Description_t; /**********************************************************************/ /* Structures used by Device Description Information */ /**********************************************************************/ #define QTG_DSPD0100 "DSPD0100" typedef _Packed struct Qtg_DSPD0100 { char Keyboard_identifier[3]; char Reserved[1]; int Code_page; int Char_set; } Qtg_DSPD0100_t; /**********************************************************************/ /* Type Definition for the Device Description Information */ /**********************************************************************/ typedef _Packed struct Qtg_Device_Description { char Device_name[10]; char Device_format[8]; char Reserved[2]; int Offset_to_device_attributes; int Length_device_attributes; Qtg_DSPD0100_t Display_device; } Qtg_Device_Description_t; /**********************************************************************/ /* Type Definition for the Connection Description Information */ /**********************************************************************/ typedef _Packed struct Qtg_Connection_Description { int Length_connection_description; char Internet_address[20]; char Secure_password_valid[1]; char Workstation_type[14]; } Qtg_Connection_Description_t; #endif #include <recio.h> /* _Ropen, _Rwrite, _Rreadf */ #include <xxfdbk.h> /* _Ropnfbk */ #include <stdio.h> /* sprintf, printf, fopen, fread, */ #include <time.h> /* time(), ctime() */ #include <stdlib.h> /* system, atoi, free, malloc, exit, */ #include <string.h> /* strxxx, memxxx */ #include <ctype.h> /* isspace, toupper */ #include <except.h> /* _CNL_Hndlr_Parms_T structure */ #include <signal.h> /* _GetExcData(), signal() */ #include <stdarg.h> /* va_start(), va_arg(), va_end() */ #include <sys/types.h> /* Include for inet_ntoa() */ #include <sys/socket.h> /* Include for inet_ntoa() */ #include <netinet/in.h> /* Include for struct sockaddr_in */ #include <arpa/inet.h> /* Include for inet_ntoa() */ #include <unistd.h> /* File system API's (POSIX) */ #include <etgdevex.h> /* TELNET Device Exit Point programs */ #include <qdcrdevd.h> /* QDCRDEVD - Retrieve Device Desc */ #include <qcapcmd.h> /* QCAPCMD - Process CL commands */ #include <qmhrtvm.h> /* QMHRTVM - retrieve program message*/ #include <qmhsndm.h> /* QMHSNDM - send non-program msg */ #include <qmhchgem.h> /* QMHCHGEM - change exception msg */ #include <qwcrdtaa.h> /* QWCRDTAA() - retrieve data area */ #include <qmhsndpm.h> /* QMHSNDPM - send program message */ /**********************************************************************/ /* Make all API calls library qualified (valid only at V3R7 and up) */ /**********************************************************************/ #pragma map (QDCRDEVD, "QSYS/QDCRDEVD") #pragma map (QCAPCMD, "QSYS/QCAPCMD") #pragma map (QMHRTVM, "QSYS/QMHRTVM") #pragma map (QMHSNDM, "QSYS/QMHSNDM") #pragma map (QMHCHGEM, "QSYS/QMHCHGEM") #pragma map (QWCRDTAA, "QSYS/QWCRDTAA") #pragma map (QMHSNDPM, "QSYS/QMHSNDPM") /**********************************************************************/ /* All file scoped Constants go here */ /**********************************************************************/ const int fTrue = 1; const int fFalse = 0; /**********************************************************************/ /* All file scoped type declarations go here */ /**********************************************************************/ typedef struct _ERRSTRUCT { int Bytes_Provided; int Bytes_Available; char Exception_Id[7]; char Reserved; char Exception_Data[256]; } ERRSTRUCT; typedef ERRSTRUCT *PERRSTRUCT; /**********************************************************************/ /* All file scoped Macro invocations go here */ /**********************************************************************/ #define MAX(a,b) ( ((a) > (b)) ? (a) : (b) ) #define MIN(a,b) ( ((a) < (b)) ? (a) : (b) ) /**********************************************************************/ /* The following describe the IP address mapping file */ /**********************************************************************/ #define MAX_ADDRESSES 1000 #define RECORD_WIDTH 240 /**********************************************************************/ /* Some message API definitions */ /**********************************************************************/ #define MSG_DIAG "*DIAG " #define MSG_ESCAPE "*ESCAPE " #define MSG_INFO "*INFO " #define MSG_INQ "*INQ " #define MSG_COMP "*COMP " #define MSG_NOTIFY "*NOTIFY " #define MSG_RQS "*RQS " #define MSG_STATUS "*STATUS " #define MSQ_Q_CUR_PROG "* " #define QTCPMSG "QTCPMSG *LIBL " /* Stack MSGF */ #define QTCPMSGF "QTCPMSGF *LIBL " /* Apps MSGF */ #define QCPFMSG "QCPFMSG *LIBL " /* Most CPFxxxx */ #define QCEEMSG "QCEEMSG *LIBL " /* CEE9901 */ #define QC2MSGF "QC2MSGF *LIBL " /* ILE Signals */ /**********************************************************************/ /* All internal function prototypes go here */ /**********************************************************************/ /**********************************************************************/ /* If the fDebug flag is not true, then the buffer() and record() */ /* functions will not write anything to standard out. */ /**********************************************************************/ static void buffer(char *Buffer, int Length); static void record(char *Format, ...); void handler(int iSignal); static void cl_command(char *Command); static int Pad(char *pszString, int iLength); static int Trim(char *pszString, int iLength); /**********************************************************************/ /* All file scoped variable declarations go here */ /**********************************************************************/ static int fRemoveEscapeMsg; /* True if you want to hide exceptions */ static int fException; /* Set true only inside excp handler */ static int fDebug; /* True means to log records to DEBUG */ static int fLogMsg; /* True means send msg to QTCP msgq */ static int fLogFile; /* True means log msg to SRCPF or PF */ static char acMsg[256]; /* for QMHSNDM API call */ static char acMsgKey[4]; /* for QMHSNDM API call */ static char acMsgReply[20]; /* for QMHSNDM API call */ /* Function Specification *********************************************/ /* */ /* Function Name: Main */ /* */ /* Descriptive Name: TELNET Device Termination sample program. */ /* */ /* This exit program provides control over Virtual Device */ /* termination for TELNET clients. */ /* */ /* A message is logged to the QTCP user profile message queue, */ /* provided the QUSRSYS/QTGEXITS *DTAARA shows a "*MSGQ" string. */ /* */ /* Notes: The "argv[]" parameters are "char *" by definition. */ /* Reference integers as "*(int(argv[1]))", for example. */ /* Consider the method for passing them back to the caller. */ /* */ /* Dependencies: */ /* */ /* TELNET Server exit point QIBM_QTG_DEVTERM format */ /* TERM0100 was registered during TCP/IP LPP installation. */ /* */ /* You must grant user profile QTCP authorities to all objects */ /* and libraries you create or require here! */ /* */ /* Input: */ /* */ /* char * argv[1] - Virtual Device being terminated */ /* */ /* Exit Normal: Sends a message to the QTCP profile *MSGQ. */ /* */ /* Exit Error: None */ /* */ /* End Function Specification *****************************************/ void main(int argc, char *argv[]) { /********************************************************************/ /* Local Variables */ /********************************************************************/ ERRSTRUCT esErrCode; struct sockaddr_in *pSockAddr; /* Network address template */ int iClientPort; /* Up to 5 digits possible */ int iParms; /* # required parameters */ int iMin; /* Working variable */ char *pDevice; /* argv[1] */ char IP[16]; /* Copy Internet_Adress mbr */ char Type[11]; /* Copy Device_Category mbr */ struct _rtvm0100 { int Bytes_Return; int Bytes_Available; int Length_Message_Returned; int Length_Message_Available; int Length_Help_Returned; int Length_Help_Available; char Message[240]; /* commented out in qmhrtvm.h */ char Message_Help[240]; /* commented out in qmhrtvm.h */ } rtvm0100; /********************************************************************/ /* Type Definition for the DEVD1100 format - *PRT */ /* (Typedef names have been modified to avoid conflicts) */ /********************************************************************/ typedef _Packed struct Switch_Lines { char Switch_Line_Name[10]; } Switch_Lines_t; /* From qdcrdevd.h */ typedef _Packed struct Qdc_DEVD1100 { int Bytes_Returned; int Bytes_Available; char Date_Info_Retrieved[7]; char Time_Info_Retrieved[6]; char Device_Name[10]; char Device_Category[10]; char Online_At_IPL[10]; char Text_Desc[50]; char Reserved1[3]; int Font_ID_Point_Size; int Max_Length_Request_Unit; int Pacing; int Max_Pending_Requests; int Print_Request_Timer; int Char_ID_Graphic_Char_Set; int Char_ID_Code_Page; int DBCS_Feature_RAM_Size; int Inactive_Timer; int Activation_Timer; int Switch_Setting; int Device_Port; char Device_Class[10]; char Device_Type[10]; char Device_Model[10]; char Advanced_Function_Printing[10]; char AFP_Attachments[10]; char Local_Location_Addr[10]; char Attached_Non_Switch_Ctl_Name[10]; char Font_ID_ID[10]; char Form_Feed[10]; char Separator_Drawer[10]; char Printer_Error_Message[10]; char Message_Queue_Name[10]; char Message_Queue_Library[10]; char Application_Type[10]; char Print_While_Converting[10]; char Form_Definition_Name[10]; char Form_Definition_Library[10]; char Work_Station_Custom_Obj_Name[10]; char Work_Station_Custom_Obj_Lib[10]; char Remote_Location_Name[10]; char Local_Location_Name[10]; char Remote_Net_ID[10]; char Ctl_Session_Device_Desc[10]; char Mode_Name[10]; char DBCS_Feature_Matrix_Size[10]; char DBCS_Feature_Language_ID[10]; char DBCS_Feature_Last_Code_Point[10]; char SNA_Pass_Through_Device[10]; char SNA_Pass_Through_Group_Name[10]; char Emulated_Device[10]; char Emulated_Device_Model[10]; char Emulating_ASCII_Device[10]; char Physical_Attachment[10]; char Auxiliary_Printer[10]; char Language_Type[10]; char Line_Speed[10]; char Word_Length[10]; char Parity_Type[10]; char Stop_Bits[10]; char Num_Drawers[10]; char Print_Quality[10]; char Transform_Enabled[10]; char Manufacturer_Type_Model[20]; char Paper_Source_1[10]; char Paper_Source_2[10]; char Envelope_Source[10]; char ASCII_Code_Page_899_Support[10]; char Separator_Exit_Program_Name[10]; char Separator_Exit_Program_Lib[10]; char Host_Signon_Logon_Command[256]; char Automatically_Configured[10]; char Reserved[2]; int Offset_Switch_Lines; int Num_Switch_Lines; int Length_Switch_Lines; char Adapter_Addr[12]; char Adapter_Type[10]; char Adapter_Connect_Type[10]; char Network_Protocol[1]; /* NEW */ char Network_Protocol_Address[18]; /* NEW */ char Internet_Address[15]; /* NEW */ Switch_Lines_t Switch_Lines[2]; } DEVD1100_t; /* From qdcrdevd.h */ DEVD1100_t devd1100; /********************************************************************/ /* Type Definition for the DEVD0600 format - *DSP */ /* (Typedef names have been modified to avoid conflicts) */ /********************************************************************/ typedef _Packed struct Auxiliary_Devices { int Auxiliary_Device_Addr; char Auxiliary_Device_Type[10]; char Reserved[2]; } Auxiliary_Devices_t; /* From qdcrdevd.h */ typedef _Packed struct DEVD0600 { int Bytes_Returned; int Bytes_Available; char Date_Info_Retrieved[7]; char Time_Info_Retrieved[6]; char Device_Name[10]; char Device_Category[10]; char Online_At_IPL[10]; char Text_Desc[50]; char Reserved1[3]; int char_ID_Graphic_char_Set; int char_ID_Code_Page; int Max_Length_Request_Unit; int Inactive_Timer; int DBCS_Feature_RAM_Size; int Activation_Timer; int Switch_Setting; int Device_Port; int Max_Outstand_Frames; int Idle_Timer; int NRM_Poll_Timer; int Frame_Retry; int Offset_Auxiliary_Devices; int Num_Auxiliary_Devices; int Length_Auxiliary_Devices; char Device_Class[10]; char Device_Type[10]; char Device_Model[10]; char Local_Location_Addr[10]; char Attached_Non_Switch_Ctl_Name[10]; char Keyboard_Language_Type[10]; char Drop_Line_At_Signoff[10]; char Allow_Blinking_Cursor[10]; char Print_Device[10]; char Remote_Location_Name[10]; char Local_Location_Name[10]; char Remote_Net_ID[10]; char Ctl_Session_Device_Desc[10]; char Assoc_Printer_Name[10]; char Assoc_Printer_Remote_Net_ID[10]; char Alternate_Printer_Name[10]; char Alternate_Printer_Remote_Net_ID[10]; char Output_Queue_Name[10]; char Output_Queue_Library[10]; char Printer[10]; char Print_File_Name[10]; char Print_File_Library[10]; char Work_Station_Custom_Obj_Name[10]; char Work_Station_Custom_Obj_Lib[10]; char Application_Type[10]; char DBCS_Feature_Matrix_Size[10]; char DBCS_Feature_Language_ID[10]; char DBCS_Feature_Last_Code_Point[10]; char SNA_Pass_Through_Device[10]; char SNA_Pass_Through_Group_Name[10]; char Emulated_Device[10]; char Emulated_Device_Model[10]; char Emulating_ASCII_Device[10]; char Physical_Attachment[10]; char Line_Speed[10]; char Word_Length[10]; char Parity_Type[10]; char Stop_Bits[10]; char ASCII_Terminal_ID[20]; char Assoc_APPC_Device[10]; char Host_Signon_Logon_Command[256]; char Pass_Through_ID[1]; char Automatically_Configured[10]; char Reserved2[3]; int Shared_Session_Num; char Dependent_Location_Name[10]; char Network_Protocol[1]; /* NEW */ char Network_Protocol_Address[18]; /* NEW */ char Internet_Address[15]; /* NEW */ Auxiliary_Devices_t Aux_Devices[2]; } DEVD0600_t; /* From qdcrdevd.h */ DEVD0600_t devd0600; /********************************************************************/ /* typedef _Packed struct Qdc_DEVD0100 { */ /* int Bytes_Returned; */ /* int Bytes_Available; */ /* char Date_Info_Retrieved[7]; */ /* char Time_Info_Retrieved[6]; */ /* char Device_Name[10]; */ /* char Device_Category[10]; */ /* char Online_At_IPL[10]; */ /* char Text_Desc[50]; */ /* char Reserved[3]; */ /* } Qdc_DEVD0100_t; */ /********************************************************************/ Qdc_DEVD0100_t devd0100; time_t ltime; /* for time() call */ char Dev[10+1]; /* Log file device name */ char IPAddr[15+1]; /* Log file IP address */ char Port[5+1]; /* Log file port number */ char Time[24+1]; /* Log file timestamp */ char Msg[80+1]; /* Log file description */ char acLog[RECORD_WIDTH]; /* Log file buffer */ /********************************************************************/ /* Data area layout expected: */ /* - required to have library and name of IP address mapping file */ /* - if a connect msg is desired to QTCP msgq, add *MSG string */ /* - if debug msgs are desired to DEBUG member, add *DEBUG string */ /* */ /* Example: */ /* 'filelib/filename msgqflag debugflag' */ /* 'QUSRSYS/QTGEXITS *MSGQ *DEBUG' */ /********************************************************************/ char *pszDtaara = "QTGEXITS QUSRSYS "; char *pszDataArea = "QUSRSYS/QTGEXITS"; /* Where are pgms/logs */ char *pszDebug = "DEBUG"; /* Debug log file member */ char *pszLog = "LOG"; /* Access log file member */ char pcRecord[RECORD_WIDTH]; /* Buffer for reads */ char pcOpenFile[RECORD_WIDTH]; /* fopen() file name */ char pcOpenParms[RECORD_WIDTH]; /* fopen() attributes */ FILE *pLog; /* Log file pointer */ FILE *pDebug; /* Debug file pointer */ int iOffset; /* Varies for SRCPF vs. PF */ typedef struct _Dtaara { /******************************************************************/ /* typedef _Packed struct Qwc_Rdtaa_Data_Returned { */ /* int Bytes_Available; */ /* int Bytes_Returned; */ /* char Type_Value_Returned[10]; */ /* char Library_Name[10]; */ /* int Length_Value_Returned; */ /* int Number_Decimal_Positions; */ /* char Value[]; commented out */ /* } Qwc_Rdtaa_Data_Returned_t; */ /******************************************************************/ Qwc_Rdtaa_Data_Returned_t returned; char contents[257]; } Dtaara_t; Dtaara_t dtaara; /********************************************************************/ /* Initialize flags */ /********************************************************************/ fException = fFalse; /* Exception was trapped by signal() */ fRemoveEscapeMsg = fFalse; /* Remove exception msg from joblog */ fDebug = fFalse; /* Log debug msgs to DEBUG member */ fLogMsg = fFalse; /* Log connect msg to QTCP msg queue */ fLogFile = fFalse; /* Log connect msg to SRCPF or PF */ signal(SIGALL, &handler); /* Trap all signals with our handler */ /********************************************************************/ /* Read fixed data area to see if debug logging is active. We */ /* force exceptions to be signalled so we know if the data area */ /* exists or not by checking the fException flag. */ /********************************************************************/ memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = 0L; /* Force exception to be signalled */ memset(&dtaara, 0x00, sizeof(dtaara)); dtaara.returned.Bytes_Available = sizeof(dtaara); fException = fFalse; /* Initialize - reset in handler */ fRemoveEscapeMsg = fTrue; /* Initialize - want to hide errors */ QWCRDTAA(&dtaara, sizeof(dtaara), pszDtaara, -1, sizeof(dtaara.contents) - 1, &esErrCode); fRemoveEscapeMsg = fFalse; /* Reset - want to see errors */ /********************************************************************/ /* If data area found and contents exist, process contents */ /********************************************************************/ if (!fException && dtaara.returned.Bytes_Returned) { if (NULL != strstr(dtaara.contents, "*MSGQ")) { /****************************************************************/ /* If find *MSGQ, means log connect msg to QTCP msg queue */ /****************************************************************/ fLogMsg = fTrue; } /* endif */ if (NULL != strstr(dtaara.contents, "*FILE")) { /****************************************************************/ /* If find *FILE, means log connect msg to SRCPF or PF */ /****************************************************************/ fLogFile = fTrue; } /* endif */ if (NULL != strstr(dtaara.contents, "*DEBUG")) { /****************************************************************/ /* If find *DEBUG, log flight records to map file member DEBUG */ /****************************************************************/ fDebug = fTrue; } /* endif */ /******************************************************************/ /* Map file is supposed to be the first string in the data area */ /******************************************************************/ pszDataArea = strtok(dtaara.contents, " "); } /* endif */ if (fDebug) { /******************************************************************/ /* record() output goes to standard out */ /******************************************************************/ sprintf(pcOpenFile, "%s(%s)", pszDataArea, pszDebug); sprintf(pcOpenParms, "a+, lrecl=%d, recfm=v", RECORD_WIDTH); pDebug = freopen(pcOpenFile, pcOpenParms, stdout); } /* endif */ record("\ndevterm: >>>>> entry\n"); record("devterm: Signals -> handler\n"); iParms = 1; /* Total of 1 parms on the interface */ record("devterm: argc: %d iParms: %d\n", argc, iParms); if ((argc-1) != iParms) { record("devterm: Invalid number of parameters!\n"); /******************************************************************/ /* TCP7117 - Program &1 in library &2 received an invalid number */ /* parameters. */ /******************************************************************/ sprintf(acMsg, "DEVTERM *LIBL "); iMin = strlen(acMsg); memcpy(&acMsg[iMin], &iParms, sizeof(int)); iMin += sizeof(int); QMHSNDPM("TCP7117", QTCPMSGF, acMsg, iMin, MSG_INFO, MSQ_Q_CUR_PROG, 0, acMsgKey, &esErrCode); record("devterm: <<<<< exit\n\n"); return; } /* endif */ pDevice = argv[1]; record("devterm: argv[1]: Device: >%.10s<\n", pDevice); /********************************************************************/ /* First, need to know if the device is a *DSP or *PRT, which will */ /* determine whether we need to use DEVD0600 or DEVD1100 for our */ /* detailed call. */ /********************************************************************/ record("devterm: Call QDCRDEVD API using DEVD0100\n"); memset(&devd0100, 0x00, sizeof(devd0100)); memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = 0L; /* Force exception to be signalled */ QDCRDEVD(&devd0100,sizeof(devd0100), "DEVD0100", pDevice, &esErrCode); if (!memcmp(devd0100.Device_Category, "*PRT ", 10)) { /******************************************************************/ /* Want to log the IP address for this device too - call the API */ /******************************************************************/ record("devterm: Call QDCRDEVD API using DEVD1100\n"); memset(&devd1100, 0x00, sizeof(devd1100)); memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = 0L; /* Force exception to be signalled*/ QDCRDEVD(&devd1100,sizeof(devd1100),"DEVD1100",pDevice,&esErrCode); /******************************************************************/ /* For the Network Protocol Address, we can use the sockaddr_in */ /* structure as a template if we account for the fact the sin_len */ /* member is not given, and sin_family starts at Network_Protocol.*/ /* Point the structure ad Network_Protocol to include sin_family */ /* and subtract 1 to account for sin_len missing. Note that this */ /* means you cannot use the sin_len member! */ /******************************************************************/ pSockAddr = (struct sockaddr_in *)(devd1100.Network_Protocol - 1); iClientPort = pSockAddr->sin_port; Trim(devd1100.Internet_Address,sizeof(devd1100.Internet_Address)-1); strcpy(IP, devd1100.Internet_Address); Trim(devd0100.Device_Category,sizeof(devd0100.Device_Category)-1); strcpy(Type, devd0100.Device_Category); } else if (!memcmp(devd0100.Device_Category, "*DSP ", 10)) { /******************************************************************/ /* Want to log the IP address for this device too - call the API */ /******************************************************************/ record("devterm: Call QDCRDEVD API using DEVD0600\n"); memset(&devd0600, 0x00, sizeof(devd0600)); memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = 0L; /* Force exception to be signalled*/ QDCRDEVD(&devd0600,sizeof(devd0600),"DEVD0600",pDevice,&esErrCode); /******************************************************************/ /* For the Network Protocol Address, we can use the sockaddr_in */ /* structure as a template if we account for the fact the sin_len */ /* member is not given, and sin_family starts at Network_Protocol.*/ /* Point the structure ad Network_Protocol to include sin_family */ /* and subtract 1 to account for sin_len missing. Note that this */ /* means you cannot use the sin_len member! */ /******************************************************************/ pSockAddr = (struct sockaddr_in *)(devd0600.Network_Protocol - 1); iClientPort = pSockAddr->sin_port; Trim(devd0600.Internet_Address,sizeof(devd0600.Internet_Address)-1); strcpy(IP, devd0600.Internet_Address); Trim(devd0100.Device_Category,sizeof(devd0100.Device_Category)-1); strcpy(Type, devd0100.Device_Category); } else { record("devterm: Device is neither *PRT or *DSP!\n"); record("devterm: <<<<< exit\n\n"); if (fDebug) { fclose(pDebug); } /* endif */ exit(0); } /********************************************************************/ /* Build timestamp */ /********************************************************************/ time(<ime); sprintf(Time, "%.24s", ctime(<ime)); /********************************************************************/ /* If data area found and special string found, send connect msg */ /********************************************************************/ if (fLogMsg) { record("devterm: Posting disconnect message to QTCP msg queue\n"); sprintf(acMsg, "TELNET client %.15s port %d type %s disconnect at %.24s.", IP, iClientPort, Type, Time); memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = sizeof(esErrCode);/* Ignore exceptions */ QMHSNDM("CPF9897", QCPFMSG, acMsg, strlen(acMsg), MSG_INFO, "QTCP *USER ", 1, acMsgReply, acMsgKey,&esErrCode); } /* endif */ /********************************************************************/ /* If data area found and special string found, log connect msg */ /********************************************************************/ if (fLogFile) { /******************************************************************/ /* Open database file for permanent log of test results. */ /******************************************************************/ sprintf(pcOpenFile, "%s(%s)", pszDataArea, pszLog); sprintf(pcOpenParms, "a, lrecl=%d, recfm=v", RECORD_WIDTH); pLog=fopen(pcOpenFile, pcOpenParms); fwrite(acMsg, 1, strlen(acMsg), pLog); fclose(pLog); } /* endif */ /********************************************************************/ /* Exit */ /********************************************************************/ record("devterm: <<<<< exit\n\n"); if (fDebug) { fclose(pDebug); } /* endif */ exit(0); } /* End main */ /**********************************************************************/ /*@function buffer() */ /* */ /* Parameters: */ /* */ /* char *buffer - points at buffer to dump */ /* int length - length of buffer to dump */ /* */ /* Description: */ /* */ /* Dumps out a buffer in both hex and readable form. */ /* */ /* 1934D8E3 D4E3E2D7 C3F0F0F2 40404040 |..QTMTSPC002 | */ /* 40404040 40404040 40404040 40404040 | | */ /* 00000000 00000000 00000000 00000000 |................| */ /**********************************************************************/ void buffer(char *Buffer, int Length) { int iRow = 0; int iCol = 0; int iLast = 0; int iTotalRows = 0; int iLen; int iBytes; unsigned char c; char acLine[20]; char Buff[512]; char *pBuff = Buff; /********************************************************************/ /* If debug is not active, nothing to do here... */ /********************************************************************/ if (!fDebug || (Buffer == (char *)NULL)) { record("buffer: Buffer is NULL\n"); return; } /* endif */ /********************************************************************/ /* Calculate total rows to fit all bytes */ /********************************************************************/ iTotalRows = (Length + 15) / 16; /********************************************************************/ /* Primary loop for rows */ /********************************************************************/ for (iRow = 0; iRow < iTotalRows; iRow++) { /******************************************************************/ /* Offset into line by 4 blank characters */ /******************************************************************/ *pBuff = ' '; pBuff++; *pBuff = ' '; pBuff++; *pBuff = ' '; pBuff++; *pBuff = ' '; pBuff++; /******************************************************************/ /* Print 16 bytes of the buffer as the hexadecimal dump section */ /* Primary loop for columns */ /******************************************************************/ for (iCol = 0; iCol < 16; iCol++) { /****************************************************************/ /* One printable character displays two hexadecimal characters */ /****************************************************************/ if (iCol + (iRow * 16) >= Length) { /**************************************************************/ /* No more data - just fill remaining positions with blanks */ /**************************************************************/ *pBuff = ' '; pBuff++; *pBuff = ' '; pBuff++; } else { /**************************************************************/ /* Print actual data */ /**************************************************************/ sprintf(pBuff, "%02X", Buffer[iCol + (iRow * 16)]); pBuff += 2; } /* endelse */ /****************************************************************/ /* Add a space separator every 4 hex bytes */ /****************************************************************/ if (iCol % 4 == 3) { *pBuff = ' '; pBuff++; /* Pad character */ } /* endif */ } /* endfor */ /******************************************************************/ /* Print the same 16 bytes as the "readable" text section */ /******************************************************************/ *pBuff = ' '; pBuff++; *pBuff = '|'; pBuff++; /* Left text bar */ /******************************************************************/ /* Print 16 bytes of the buffer as readable text section */ /* Secondary loop for columns */ /******************************************************************/ iLast = 0; /* Bytes last line */ for (iCol = 0; iCol < 16; iCol++) { if (iCol + (iRow * 16) >= Length) { /**************************************************************/ /* No more data - just fill remaining positions with blanks */ /**************************************************************/ *pBuff = ' '; pBuff++; } else { /**************************************************************/ /* Print actual data */ /**************************************************************/ iLast++; /* Actual data */ c = Buffer[iCol + (iRow * 16)]; if (isprint(c)) { *pBuff = c; pBuff++; /* Actual char */ } else if (c == 0x40) { *pBuff = ' '; pBuff++; /* Blank char */ } else { *pBuff = '.'; pBuff++; /* Unprintable */ } /* endelse */ } /* endelse */ } /* endfor */ *pBuff = '|'; pBuff++; /* Right text bar */ /******************************************************************/ /* Build the finished output */ /******************************************************************/ memset(acLine, 0x00, sizeof(acLine)); iBytes = (iRow * 16) + iLast; sprintf(acLine," Byte %d\n", iBytes); /* Byte count */ strcpy(pBuff, acLine); pBuff = &Buff[0]; /* Reset pointer */ printf("%s", Buff); } /* endfor */ return; } /**********************************************************************/ /*@function record() */ /* */ /* Parameters: */ /* */ /* variable arguments */ /* */ /* Log test result entry to standard out (normally console). This */ /* will only occur if DEBUG is active. If this occurs in a batch */ /* job, a spooled file is usually created holding output. */ /**********************************************************************/ void record(char *Format, ...) { va_list arg_ptr; int iLen; char record[512]; /********************************************************************/ /* If debug is not active, nothing to do here... */ /********************************************************************/ if (!fDebug) { return; } /* endif */ va_start(arg_ptr, Format); iLen = vsprintf(record, Format, arg_ptr); va_end(arg_ptr); record[iLen] = 0x00; printf("%s", record); return; } /**********************************************************************/ /*@function handler() */ /* */ /* Parameters: */ /* */ /* int iSignal - value of the signal which caused the handler to be */ /* invoked. SIGABRT, SIGTERM, etc. */ /**********************************************************************/ void handler(int iSignal) { ERRSTRUCT esErrCode; _INTRPT_Hndlr_Parms_T Signal; _INTRPT_Hndlr_Parms_T *pSignal = &Signal; char *pszMsgFile = NULL; struct { int Bytes_Return; int Bytes_Available; int Length_Message_Returned; int Length_Message_Available; int Length_Help_Returned; int Length_Help_Available; char Message[256]; char Message_Help[256]; } rtvm0100; char *Signals[] = { "SIGABRT", /* 1 Abnormal termination */ "SIGFPE", /* 2 Erroneous arithmetic operation */ "SIGILL", /* 3 Invalid hardware instruction */ "SIGINT", /* 4 Interactive attention signal */ "SIGSEGV", /* 5 Invalid memory reference */ "SIGTERM", /* 6 Termination signal */ "SIGUSR1", /* 7 Application defined signal 1 */ "SIGUSR2", /* 8 Application defined signal 2 */ "SIGIO", /* 9 I/O possible, or completed */ "SIGALL", /* 10 All signals */ "SIGOTHER", /* 11 ILE C/400 signal */ "SIGKILL", /* 12 Termination signal (cannot be caught,ignored) */ "SIGPIPE", /* 13 Write on a pipe with no readers */ "SIGALRM", /* 14 Timeout signal */ "SIGHUP", /* 15 Hangup detected on controlling terminal */ "SIGQUIT", /* 16 Interactive termination signal */ "SIGSTOP", /* 17 Stop signal (cannot be caught or ignored) */ "SIGTSTP", /* 18 Interactive stop signal */ "SIGCONT", /* 19 Continue if stopped */ "SIGCHLD", /* 20 Child process terminated or stopped */ "SIGTTIN", /* 21 Background read from controlling terminal */ "SIGTTOU", /* 22 Background write to controlling terminal */ "SIGURG", /* 23 High bandwidth data is available at a socket */ "SIGPOLL", /* 24 Pollable event */ "SIG25", /* 25 Not defined */ "SIG26", /* 26 Not defined */ "SIG27", /* 27 Not defined */ "SIG28", /* 28 Not defined */ "SIG29", /* 29 Not defined */ "SIG30", /* 30 Not defined */ "SIG31", /* 31 Not defined */ "SIGBUS", /* 32 Bus error (specification exception) */ "SIGDANGER", /* 33 system crash imminent */ "SIGPRE", /* 34 programming exception */ "SIGSYS", /* 35 Bad system call */ "SIGTRAP", /* 36 Trace/Breakpoint trap */ "SIGPROF", /* 37 Profiling timer expired */ "SIGVTALRM", /* 38 Virtual timer expired */ "SIGXCPU", /* 39 CPU time limit exceeded */ "SIGXFSZ" /* 40 File size limit exceeded */ }; record("handler: Caught signal %s\n", Signals[iSignal-1]); /********************************************************************/ /* Set file scoped flag so caller knows exception occurred */ /********************************************************************/ fException = fTrue; /********************************************************************/ /* Try and pull out the message text */ /********************************************************************/ _GetExcData(&Signal); if (!memcmp(Signal.Msg_Id, "TCP", 3) ) { /******************************************************************/ /* TCP Apps (and Stack with recursive call) message file */ /******************************************************************/ pszMsgFile = QTCPMSGF; } else if (!memcmp(Signal.Msg_Id, "C2M16", 5) ) { /******************************************************************/ /* ILE-C message file (primarily for signals, if use raise/abort) */ /******************************************************************/ pszMsgFile = QC2MSGF; } else if (!memcmp(Signal.Msg_Id, "CEE99", 5) ) { /******************************************************************/ /* ILE-C message file */ /******************************************************************/ pszMsgFile = QCEEMSG; } else { /******************************************************************/ /* Most CPFxxxx messages */ /******************************************************************/ pszMsgFile = QCPFMSG; } /* endif */ memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = sizeof(esErrCode); /* Ignore exceptions */ QMHRTVM(&rtvm0100, /* Message information */ sizeof(rtvm0100), /* Length of message information */ "RTVM0100", /* Format name */ Signal.Msg_Id, /* Message identifier */ pszMsgFile, /* Qualified message file name */ Signal.Ex_Data, /* Message data */ sizeof(Signal.Ex_Data), /* Length of message data */ "*YES ", /* Replace substitution values */ "*NO ", /* Return format control */ &esErrCode, /* Error Code */ "*MSGID ", /* Retrieve option */ 0, /* Convert to CCSID */ 0); /* Message data CCSID */ if (esErrCode.Bytes_Available || !rtvm0100.Length_Message_Returned) { record("handler: Error - message not found\n"); } else { record("handler: Escape message:\n"); buffer(rtvm0100.Message, rtvm0100.Length_Message_Returned); } /* endelse */ /********************************************************************/ /* Delete msg from job log if caller so desires (flag indicator) */ /********************************************************************/ if (fRemoveEscapeMsg) { record("handler: Remove escape message from job log\n"); memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = sizeof(esErrCode); QMHCHGEM(&Signal.Target, 0, &Signal.Msg_Ref_Key, "*REMOVE ", "", 0, &esErrCode); } else { record("handler: Escape message not removed from job log\n"); } /* endif */ record("handler: Reset signals -> handler\n"); signal(SIGALL, &handler); return; } /* Function Specification *********************************************/ /* */ /* Function Name: cl_command */ /* */ /* Descriptive Name: run any CL command as if on a command line. */ /* */ /* char * Command - null terminated string */ /* */ /* End Function Specification *****************************************/ void cl_command(char *Command) /* Entry point */ { /********************************************************************/ /* Local Variables */ /********************************************************************/ ERRSTRUCT esErrCode; Qca_PCMD_CPOP0100_t cpop0100; char cpop0100_out[512]; int cpop0100_len; /********************************************************************/ /* typedef _Packed struct Qca_PCMD_CPOP0100 { */ /* int Command_Process_Type; */ /* char DBCS_Data_Handling; */ /* char Prompter_Action; */ /* char Command_String_Syntax; */ /* char Message_Key[4]; */ /* char Reserved[9]; */ /* } Qca_PCMD_CPOP0100_t; */ /********************************************************************/ record("cl_command: Command:\n"); buffer(Command, strlen(Command)); memset(cpop0100_out, 0x00, sizeof(cpop0100_out)); memset(&cpop0100, 0x00, sizeof(Qca_PCMD_CPOP0100_t)); cpop0100.Command_Process_Type = 0; cpop0100.DBCS_Data_Handling = '0'; cpop0100.Prompter_Action = '0'; cpop0100.Command_String_Syntax = '0'; memset(&esErrCode, 0x00, sizeof(esErrCode)); esErrCode.Bytes_Provided = 0L; /* Force exceptions to be signalled */ QCAPCMD(Command, strlen(Command), &cpop0100, sizeof(Qca_PCMD_CPOP0100_t), "CPOP0100", cpop0100_out, sizeof(cpop0100_out) - 1, &cpop0100_len, &esErrCode); return; } /* Function Specification *********************************************/ /* */ /* Function Name: Pad */ /* */ /* char *pszString - null terminated string, may or may not have */ /* blanks */ /* */ /* int iLength - maximum length to pad the string (includes NULL */ /* terminator). A NULL terminator WILL be added! */ /* */ /* iLength should be where you want the NULL term. */ /* */ /* Thus, iLength=10 means array position 10, not 9. */ /* */ /* Descriptive Name: pads a string with blanks */ /* */ /* End Function Specification *****************************************/ int Pad(char *pszString, int iLength) { int iLen = strlen(pszString); if (iLength <= iLen) { return(iLen); } /* endif */ /********************************************************************/ /* Start at the end of the string, and add blanks. */ /********************************************************************/ while (iLen < iLength) { pszString[iLen] = ' '; iLen++; } /* endwhile */ /********************************************************************/ /* Add the null terminator to make it a 'C' string. */ /********************************************************************/ pszString[iLen] = 0x00; iLen = strlen(pszString); return(iLen); } /* Function Specification *********************************************/ /* */ /* Function Name: Trim */ /* */ /* char *pszString - null terminated string, may have trailing */ /* blanks */ /* */ /* int iLength - start position of the string to be trimmed. This */ /* need not be the length of the string. You should */ /* specify the position of any NULL terminator if you */ /* are trimming a 'C' string. */ /* */ /* iLength should be where you want the NULL term. */ /* */ /* Thus, iLength=10 means array position 10, not 9. */ /* */ /* Descriptive Name: pads a string with blanks */ /* */ /* End Function Specification *****************************************/ int Trim(char *pszString, int iLength ) { int iLen = iLength - 1; /********************************************************************/ /* While we are not at the 1st char AND (a char is whitespace OR */ /* 0x00) keep backing up from end of string */ /********************************************************************/ pszString[iLength] = 0x00; while ( (iLen >= 0) && ((isspace(pszString[iLen]) ) || (0x00 == pszString[iLen])) ) { iLen--; } /* endwhile */ /********************************************************************/ /* if iLen unchanged, it means no trimming was done (no whitespace */ /* found). */ /********************************************************************/ if (iLen == (iLength-1)) { return(iLen); } /* endif */ /********************************************************************/ /* if iLen >= 0, we found 1 or more chars, so mark end with a */ /* NULL terminator. */ /********************************************************************/ else if (iLen >= 0) { pszString[iLen + 1] = 0x00; } /* endif */ /********************************************************************/ /* else iLen < 0, which means we didn't find anything but whitespace*/ /********************************************************************/ else { pszString[0] = 0x00; } /* endelse */ iLen = strlen(pszString); return(iLen); } #undef _DEVTERM_C /**********************************************************************/ /* END OF DEVTERM.C */ /**********************************************************************/
Return to the TELNET exit program road map:
This is an API header file used by TELNET exit initialization (log on) programs. It is here for your reference should you decide to write your own TELNET exit programs.
Return to the TELNET exit program road map:
/*** START HEADER FILE SPECIFICATIONS *****************************/ /* */ /* Header File Name: ETGDEVEX */ /* */ /* Descriptive Name: TELNET Server Device Exit Program */ /* */ /* 5769-TC1 (C) Copyright IBM Corp. 1997,1997 */ /* All rights reserved. */ /* US Government Users Restricted Rights - */ /* Use, duplication or disclosure restricted */ /* by GSA ADP Schedule Contract with IBM Corp. */ /* */ /* Licensed Materials-Property of IBM */ /* */ /* Description: The TELNET server device exit program provides */ /* device initialization and termination for */ /* clients. */ /* */ /* Header Files Included: None. */ /* */ /* Macros List: QTG_DSPD0100 */ /* */ /* Structure List: Qtg_User_Description_t */ /* Qtg_DSPD0100_t */ /* Qtg_Device_Description_t */ /* Qtg_Connection_Description_t */ /* */ /* Function Prototype List: None. */ /* */ /* Change Activity: */ /* */ /* CFD List: */ /* */ /* FLAG REASON LEVEL DATE PGMR CHANGE DESCRIPTION */ /* ---- ------------ ----- ------ --------- ----------------------*/ /* $A0= D95273 4D20 970423 JSS New Include */ /* */ /* End CFD List. */ /* */ /* Additional notes about the Change Activity */ /* End Change Activity. */ /*** END HEADER FILE SPECIFICATIONS *******************************/ #ifndef ETGDEVEX_h #define ETGDEVEX_h /******************************************************************/ /* Type Definition for the User Description Information */ /******************************************************************/ typedef _Packed struct Qtg_User_Description { int Length_user_description; char User_profile[10]; char Current_library[10]; char Program_to_call[10]; char Initial_menu[10]; } Qtg_User_Description_t; /******************************************************************/ /* Structures used by Device Description Information */ /******************************************************************/ #define QTG_DSPD0100 "DSPD0100" typedef _Packed struct Qtg_DSPD0100 { char Keyboard_identifier[3]; char Reserved[1]; int Code_page; int Char_set; } Qtg_DSPD0100_t; /******************************************************************/ /* Type Definition for the Device Description Information */ /******************************************************************/ typedef _Packed struct Qtg_Device_Description { char Device_name[10]; char Device_format[8]; char Reserved[2]; int Offset_to_device_attributes; int Length_device_attributes; /*Qtg_DSPD0100_t Display_device; */ /* Varying length */ } Qtg_Device_Description_t; /******************************************************************/ /* Type Definition for the Connection Description Information */ /******************************************************************/ typedef _Packed struct Qtg_Connection_Description { int Length_connection_description; char Internet_address[20]; char Secure_password_valid[1]; char Workstation_type[14]; } Qtg_Connection_Description_t; #endif
Return to the TELNET exit program road map: