The TN3270e protocol is a method of emulating 3270 terminal and printer devices using Telnet. It is used by terminal emulation software such as Rumba® and Hummingbird's HostExplorer® for direct connections to mainframes.
Similar to Telnet, a typical TN3270e session consists of two main parts:
The first part of a typical recorded script of a TN3270e session looks like this:
WebTcpipConnect(hWeb0, "10.19.111.201", 7230); WebTcpipRecvExact(hWeb0, NULL, 3); WebTcpipSendBin(hWeb0, "\hFFFB28", 3); // ·û( WebTcpipRecvExact(hWeb0, NULL, 7); WebTcpipSendBin(hWeb0, "\hFFFA28020749424D2D333237382D342D" // ·ú(··IBM-3278-4- "\h45FFF0", 19); // E·ð WebTcpipRecvExact(hWeb0, NULL, 28); WebTcpipSendBin(hWeb0, "\hFFFA2803070004FFF0", 9); // ·ú(·····ð WebTcpipRecvExact(hWeb0, NULL, 9); WebTcpipRecvExact(hWeb0, NULL, 347);
This looks similar to the first part of the Telnet session from the previous section because the TN3270e protocol is based on the Telnet protocol. Again, nothing needs to be changed here. Because this is a stateful, connection-oriented protocol, session handling is not an issue.
The traffic from the first part of the session is translated into TELNET code in the following table:
Server-to-Client | Client-to-Server |
---|---|
FF FD 28 (IAC DO TN3270E) | |
FF FB 28 (IAC WILL TN3270E) | |
FF FA 28 08 02 FF F0 (IAC SB TN3270E SEND DEVICE-TYPE IAC SE) | |
FF FA 28 02 07 49 42 4D 2D 33 32 37 38 2D 34 2D 45 FF F0 (IAC SB TN3270E DEVICE-TYPE REQUEST "IBM-3278-4-E" IAC SE) | |
FF FA 28 02 04 49 42 4D 2D 33 32 37 38 2D 34 2D 45 01 54 39 35 49 54 51 4D 55 FF F0 (IAC SB TN3270E DEVICE-TYPE IS "IBM-3278-4-E" CONNECT "T95ITQMU" IAC SE) | |
FF FA 28 03 07 00 04 FF F0 (IAC SB TN3270E FUNCTIONS REQUEST [BIND-IMAGE SYSREQ] IAC SE) | |
FF FA 28 03 04 00 04 FF F0 (IAC SB TN3270E FUNCTIONS IS [BIND-IMAGE SYSREQ] IAC SE) |
In summary, server and client agree on a protocol, a device type (IBM-3278-4-E), and on whether or not to use certain protocol features. Note that the terminal name (T95ITQMU) is assigned by the server, not the client. This is because the server system holds a database that handles the mapping of client IP addresses to terminal names.
The second part of the script contains the user interaction. The following request-response pair example represents an interaction where the user enters an account number (238729) into a text field and then hits the RETURN key:
WebTcpipSendBin(hWeb0, "\h00000000007DC6C61140C4F311C640F2" // ·····}ÆÆ·@Äó·Æ@ò "\hF3F8F7F2F9FFEF", 23); // óø÷òù·ï WebTcpipRecvExact(hWeb0, NULL, 199);
Knowing that the traffic is encoded in EBCDIC (rather than ASCII), the account number can be found in the request string using an EBCDIC code table: 238729 is represented as the binary byte sequence 0xF2 F3 F8 F7 F2 F9 in EBCDIC. This is the part that is relevant to customization; the rest may be left unchanged.
Here's an account of the other parts of this message. The first five bytes represent the TN3270e message header (RFC 2355):
Field | Length | Value in our example | |
---|---|---|---|
Data type | 1 byte | 0x00 | 3270-DATA |
Request flag | 1 byte | 0x00 | ERR-COND-CLEARED |
Response flag | 1 byte | 0x00 | NO-RESPONSE |
Sequence number | 2 bytes | 0x0000 | Sequence numbers may or may not be used. In this case, they aren't. |
Each client request is terminated by a two-byte sequence: 0xFF EF.
The remainder of the request data (bytes #6 - #21 in this example) is application data containing screen positions, key codes, and text.
The server response can be analyzed in the corresponding log or TrueLog files from the recording session. In this example, the response is a 199-byte data block beginning with five zero bytes (0x00) and ending with 0xFF EF, just like the request data. The data content in between is similar to the request data: cursor positions, formatting, and text content (encoded in EBCDIC).
A simple customization of the script extract above using the function library would look like this:
ResetRequestData(); AppendRequestData("\h00000000007DC6C61140C4F311C640", 15); AppendRequestData(ASC2EBCDIC(sAccountNr) + "\hFFEF"); SendTcpipRequest(hWeb0); WebTcpipRecvUntil(hWeb, sResp, sizeof(sResp), nRecv, "\hFFEF", 2);
Here a string variable sAccountNr has been introduced for the account number (where 238729 was used during recording). The function ASC2EBCDIC that converts between ASCII and EBCDIC is explained in the following section.
Finally, the inflexible WebTcpipRecvExact has been replaced with WebTcpipRecvUntil, which is appropriate here because the trailing byte sequence is known. The response data is stored in the string variable sResp, and nRecv contains the number of received bytes.
For logging and verification purposes, the server response should be translated from EBCDIC to ASCII. For example:
Writeln("Response: " + EBCDIC2ASC(sResp, nRecv));
When necessary, single response data (such as a new ID that's needed as input data for subsequent requests) can be extracted from this response using the StrSearchDelimited function.
Taking customization a step further, it's good practice to replace the WebTcpipRecvUntil call in the script above with a MyWebTcpipRecv function that encapsulates all the necessary actions on server responses:
In the Silk Performer script, each client request should be followed by a call to this new function, replacing the WebTcpipRecvExact function calls from the recording session.