All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.galasa.zos3270.internal.comms.NetworkServer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright contributors to the Galasa project
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package dev.galasa.zos3270.internal.comms;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import dev.galasa.zos3270.spi.NetworkException;

public class NetworkServer /* extends Thread */ {

    public static final Charset ascii7 = Charset.forName("us-ascii");

    private final Log           logger = LogFactory.getLog(getClass());

    private final Socket        socket;
    private final OutputStream  outputStream;
    private final InputStream   inputStream;
    private String              deviceName;

    public NetworkServer(Socket socket) throws NetworkException {
        try {
            this.socket = socket;
            this.socket.setTcpNoDelay(true);
            this.socket.setKeepAlive(true);

            this.inputStream = this.socket.getInputStream();
            this.outputStream = this.socket.getOutputStream();

            negotiate(this.inputStream, this.outputStream);
        } catch (IOException e) {
            throw new NetworkException("Unable to initialise the server", e);
        }

    }

    public void close() {
        if (socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                logger.warn("Problem closing socket", e);
            }
        }
    }

    public InputStream getInputStream() {
        return this.inputStream;
    }

    public void negotiate(InputStream inputStream, OutputStream outputStream) throws NetworkException {
        try {
            // *** Send the DO TN3270E initialisation message
            byte[] doTN3270 = new byte[] { NetworkThread.IAC, NetworkThread.DO, NetworkThread.TN3270E };
            outputStream.write(doTN3270);
            outputStream.flush();

            // *** Let see what the client says, we are only going to support TN3270E
            // clients
            expect(inputStream, NetworkThread.IAC, NetworkThread.WILL, NetworkThread.TN3270E);

            byte[] sendDeviceType = new byte[] { NetworkThread.IAC, NetworkThread.SB, NetworkThread.TN3270E, NetworkThread.SEND,
                    NetworkThread.DEVICE_TYPE, NetworkThread.IAC, NetworkThread.SE };
            outputStream.write(sendDeviceType);
            outputStream.flush();

            String deviceType = "";
            boolean gotCorrectDeviceType = false;
            while (!gotCorrectDeviceType) {
                // *** Get the requested device type, only accepting IBM-3278-2 terminals for
                // the moment
                ByteBuffer buffer = readSbSeMessage(inputStream);
                expect(buffer, NetworkThread.DEVICE_TYPE, NetworkThread.REQUEST);
                deviceType = "";
                deviceName = "";
                boolean readingType = true;
                while (buffer.hasRemaining()) {
                    byte[] b = new byte[1];
                    buffer.get(b);
                    if (b[0] == NetworkThread.CONNECT || b[0] == NetworkThread.ASSOCIATE) {
                        readingType = false;
                    } else {
                        String bs = new String(b, ascii7);
                        if (readingType) {
                            deviceType += bs;
                        } else {
                            deviceName += bs;
                        }
                    }
                }

                if ("IBM-3278-2".equals(deviceType) || "IBM-3278-2-E".equals(deviceType)
                        || "IBM-3278-3".equals(deviceType) || "IBM-3278-3-E".equals(deviceType)
                        || "IBM-3278-4".equals(deviceType) || "IBM-3278-4-E".equals(deviceType)
                        || "IBM-3278-5".equals(deviceType) || "IBM-3278-5-E".equals(deviceType)
                        || "IBM-3279-2".equals(deviceType) || "IBM-3279-2-E".equals(deviceType)
                        || "IBM-3279-3".equals(deviceType) 
                        || "IBM-DYNAMIC".equals(deviceType)) {
                    break;
                }

                // *** Reject Device Type
                byte[] rejectDeviceType = new byte[] { NetworkThread.IAC, NetworkThread.SB, NetworkThread.TN3270E, NetworkThread.DEVICE_TYPE,
                        NetworkThread.REJECT, NetworkThread.REASON, NetworkThread.INV_DEVICE_TYPE, NetworkThread.IAC, NetworkThread.SE };
                outputStream.write(rejectDeviceType);
                outputStream.flush();
            }

            if (deviceName.isEmpty()) {
                deviceName = "TERM0001";
            }

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            baos.write(NetworkThread.IAC);
            baos.write(NetworkThread.SB);
            baos.write(NetworkThread.TN3270E);
            baos.write(NetworkThread.DEVICE_TYPE);
            baos.write(NetworkThread.IS);
            baos.write(deviceType.getBytes(ascii7));
            baos.write(NetworkThread.CONNECT);
            baos.write(deviceName.getBytes(ascii7));
            baos.write(NetworkThread.IAC);
            baos.write(NetworkThread.SE);
            outputStream.write(baos.toByteArray());
            outputStream.flush();

            // *** Negotiate functions, we are going to accept any
            ByteBuffer buffer = readSbSeMessage(inputStream);
            expect(buffer, NetworkThread.FUNCTIONS, NetworkThread.REQUEST);

            byte[] noFunctions = new byte[] { NetworkThread.IAC, NetworkThread.SB, NetworkThread.TN3270E, NetworkThread.FUNCTIONS, NetworkThread.IS,
                    NetworkThread.IAC, NetworkThread.SE };
            outputStream.write(noFunctions);
            outputStream.flush();
        } catch (IOException e) {
            throw new NetworkException("IOException during terminal negotiation", e);
        }
    }

    public void sendDatastream(byte[] outboundDatastream) throws NetworkException {
        sendDatastream(outputStream, outboundDatastream);
    }

    public void sendDatastream(OutputStream outputStream, byte[] outboundDatastream) throws NetworkException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            byte[] header = new byte[] { 0, 0, 0, 0, 0 };
            byte[] trailer = new byte[] { (byte) 0xff, (byte) 0xef };

            baos.write(header);
            baos.write(outboundDatastream);
            baos.write(trailer);

            outputStream.write(baos.toByteArray());
            outputStream.flush();
        } catch (IOException e) {
            throw new NetworkException("Unable to write outbound datastream", e);
        }

    }

    public static void expect(InputStream inputStream, byte... expected) throws IOException, NetworkException {
        byte[] received = new byte[expected.length];

        int length = inputStream.read(received);

        if (length != expected.length) {
            throw new NetworkException("Expected " + expected.length + " but received only " + length + " bytes");
        }

        if (!Arrays.equals(expected, received)) {
            String expectedString = Hex.encodeHexString(expected);
            String receivedString = Hex.encodeHexString(received);
            throw new NetworkException("Expected " + expectedString + " but received " + receivedString);
        }
    }

    public static void expect(ByteBuffer buffer, byte... expected) throws NetworkException {
        byte[] received = new byte[expected.length];

        buffer.get(received);

        if (!Arrays.equals(expected, received)) {
            String expectedString = Hex.encodeHexString(expected);
            String receivedString = Hex.encodeHexString(received);
            throw new NetworkException("Expected " + expectedString + " but received " + receivedString);
        }
    }

    public static ByteBuffer readSbSeMessage(InputStream messageStream) throws IOException, NetworkException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        expect(messageStream, NetworkThread.IAC, NetworkThread.SB, NetworkThread.TN3270E);

        byte[] b = new byte[1];
        boolean lastByteFF = false;
        boolean terminated = false;
        while (messageStream.read(b) == 1) {
            if (b[0] == NetworkThread.IAC) {
                if (lastByteFF) {
                    byteArrayOutputStream.write(b);
                    lastByteFF = false;
                } else {
                    lastByteFF = true;
                }
            } else {
                if (b[0] == NetworkThread.SE && lastByteFF) {
                    terminated = true;
                    break;
                }

                byteArrayOutputStream.write(b);
            }
        }

        if (!terminated) {
            throw new NetworkException("3270 message did not terminate with IAC SE");
        }

        byte[] bytes = byteArrayOutputStream.toByteArray();

        return ByteBuffer.wrap(bytes);
    }

    public String getDeviceName() {
        return this.deviceName;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy