001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.net.examples.telnet;
019
020import java.io.FileOutputStream;
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.OutputStream;
024import java.nio.charset.Charset;
025import java.time.Duration;
026import java.util.StringTokenizer;
027
028import org.apache.commons.net.telnet.EchoOptionHandler;
029import org.apache.commons.net.telnet.InvalidTelnetOptionException;
030import org.apache.commons.net.telnet.SimpleOptionHandler;
031import org.apache.commons.net.telnet.SuppressGAOptionHandler;
032import org.apache.commons.net.telnet.TelnetClient;
033import org.apache.commons.net.telnet.TelnetNotificationHandler;
034import org.apache.commons.net.telnet.TerminalTypeOptionHandler;
035
036/**
037 * This is a simple example of use of TelnetClient. An external option handler (SimpleTelnetOptionHandler) is used. Initial configuration requested by
038 * TelnetClient will be: WILL ECHO, WILL SUPPRESS-GA, DO SUPPRESS-GA. VT100 terminal type will be subnegotiated.
039 * <p>
040 * Also, use of the sendAYT(), getLocalOptionState(), getRemoteOptionState() is demonstrated. When connected, type AYT to send an AYT command to the server and
041 * see the result. Type OPT to see a report of the state of the first 25 options.
042 */
043public class TelnetClientExample implements Runnable, TelnetNotificationHandler {
044    private static TelnetClient tc;
045
046    /**
047     * Main for the TelnetClientExample.
048     *
049     * @param args input params
050     * @throws Exception on error
051     */
052    public static void main(final String[] args) throws Exception {
053        FileOutputStream fout = null;
054
055        if (args.length < 1) {
056            System.err.println("Usage: TelnetClientExample <remote-ip> [<remote-port>]");
057            System.exit(1);
058        }
059
060        final String remoteip = args[0];
061
062        final int remoteport;
063
064        if (args.length > 1) {
065            remoteport = Integer.parseInt(args[1]);
066        } else {
067            remoteport = 23;
068        }
069
070        try {
071            fout = new FileOutputStream("spy.log", true);
072        } catch (final IOException e) {
073            System.err.println("Exception while opening the spy file: " + e.getMessage());
074        }
075
076        tc = new TelnetClient();
077
078        final TerminalTypeOptionHandler ttopt = new TerminalTypeOptionHandler("VT100", false, false, true, false);
079        final EchoOptionHandler echoopt = new EchoOptionHandler(true, false, true, false);
080        final SuppressGAOptionHandler gaopt = new SuppressGAOptionHandler(true, true, true, true);
081
082        try {
083            tc.addOptionHandler(ttopt);
084            tc.addOptionHandler(echoopt);
085            tc.addOptionHandler(gaopt);
086        } catch (final InvalidTelnetOptionException e) {
087            System.err.println("Error registering option handlers: " + e.getMessage());
088        }
089
090        while (true) {
091            boolean end_loop = false;
092            try {
093                tc.connect(remoteip, remoteport);
094
095                final Thread reader = new Thread(new TelnetClientExample());
096                tc.registerNotifHandler(new TelnetClientExample());
097                System.out.println("TelnetClientExample");
098                System.out.println("Type AYT to send an AYT telnet command");
099                System.out.println("Type OPT to print a report of status of options (0-24)");
100                System.out.println("Type REGISTER to register a new SimpleOptionHandler");
101                System.out.println("Type UNREGISTER to unregister an OptionHandler");
102                System.out.println("Type SPY to register the spy (connect to port 3333 to spy)");
103                System.out.println("Type UNSPY to stop spying the connection");
104                System.out.println("Type ^[A-Z] to send the control character; use ^^ to send ^");
105
106                reader.start();
107                final OutputStream outstr = tc.getOutputStream();
108
109                final byte[] buff = new byte[1024];
110                int readCount = 0;
111
112                do {
113                    try {
114                        readCount = System.in.read(buff);
115                        if (readCount > 0) {
116                            final Charset charset = Charset.defaultCharset();
117                            final String line = new String(buff, 0, readCount, charset); // deliberate use of default charset
118                            if (line.startsWith("AYT")) {
119                                try {
120                                    System.out.println("Sending AYT");
121
122                                    System.out.println("AYT response:" + tc.sendAYT(Duration.ofSeconds(5)));
123                                } catch (final IOException e) {
124                                    System.err.println("Exception waiting AYT response: " + e.getMessage());
125                                }
126                            } else if (line.startsWith("OPT")) {
127                                System.out.println("Status of options:");
128                                for (int ii = 0; ii < 25; ii++) {
129                                    System.out.println("Local Option " + ii + ":" + tc.getLocalOptionState(ii) + " Remote Option " + ii + ":"
130                                            + tc.getRemoteOptionState(ii));
131                                }
132                            } else if (line.startsWith("REGISTER")) {
133                                final StringTokenizer st = new StringTokenizer(new String(buff, charset));
134                                try {
135                                    st.nextToken();
136                                    final int opcode = Integer.parseInt(st.nextToken());
137                                    final boolean initlocal = Boolean.parseBoolean(st.nextToken());
138                                    final boolean initremote = Boolean.parseBoolean(st.nextToken());
139                                    final boolean acceptlocal = Boolean.parseBoolean(st.nextToken());
140                                    final boolean acceptremote = Boolean.parseBoolean(st.nextToken());
141                                    final SimpleOptionHandler opthand = new SimpleOptionHandler(opcode, initlocal, initremote, acceptlocal, acceptremote);
142                                    tc.addOptionHandler(opthand);
143                                } catch (final Exception e) {
144                                    if (e instanceof InvalidTelnetOptionException) {
145                                        System.err.println("Error registering option: " + e.getMessage());
146                                    } else {
147                                        System.err.println("Invalid REGISTER command.");
148                                        System.err.println("Use REGISTER optcode initlocal initremote acceptlocal acceptremote");
149                                        System.err.println("(optcode is an integer.)");
150                                        System.err.println("(initlocal, initremote, acceptlocal, acceptremote are boolean)");
151                                    }
152                                }
153                            } else if (line.startsWith("UNREGISTER")) {
154                                final StringTokenizer st = new StringTokenizer(new String(buff, charset));
155                                try {
156                                    st.nextToken();
157                                    final int opcode = Integer.parseInt(st.nextToken());
158                                    tc.deleteOptionHandler(opcode);
159                                } catch (final Exception e) {
160                                    if (e instanceof InvalidTelnetOptionException) {
161                                        System.err.println("Error unregistering option: " + e.getMessage());
162                                    } else {
163                                        System.err.println("Invalid UNREGISTER command.");
164                                        System.err.println("Use UNREGISTER optcode");
165                                        System.err.println("(optcode is an integer)");
166                                    }
167                                }
168                            } else if (line.startsWith("SPY")) {
169                                tc.registerSpyStream(fout);
170                            } else if (line.startsWith("UNSPY")) {
171                                tc.stopSpyStream();
172                            } else if (line.matches("^\\^[A-Z^]\\r?\\n?$")) {
173                                final byte toSend = buff[1];
174                                if (toSend == '^') {
175                                    outstr.write(toSend);
176                                } else {
177                                    outstr.write(toSend - 'A' + 1);
178                                }
179                                outstr.flush();
180                            } else {
181                                try {
182                                    outstr.write(buff, 0, readCount);
183                                    outstr.flush();
184                                } catch (final IOException e) {
185                                    end_loop = true;
186                                }
187                            }
188                        }
189                    } catch (final IOException e) {
190                        System.err.println("Exception while reading keyboard:" + e.getMessage());
191                        end_loop = true;
192                    }
193                } while (readCount > 0 && !end_loop);
194
195                try {
196                    tc.disconnect();
197                } catch (final IOException e) {
198                    System.err.println("Exception while connecting:" + e.getMessage());
199                }
200            } catch (final IOException e) {
201                System.err.println("Exception while connecting:" + e.getMessage());
202                System.exit(1);
203            }
204        }
205    }
206
207    /**
208     * Callback method called when TelnetClient receives an option negotiation command.
209     *
210     * @param negotiation_code - type of negotiation command received (RECEIVED_DO, RECEIVED_DONT, RECEIVED_WILL, RECEIVED_WONT, RECEIVED_COMMAND)
211     * @param option_code      - code of the option negotiated
212     */
213    @Override
214    public void receivedNegotiation(final int negotiation_code, final int option_code) {
215        String command;
216        switch (negotiation_code) {
217        case TelnetNotificationHandler.RECEIVED_DO:
218            command = "DO";
219            break;
220        case TelnetNotificationHandler.RECEIVED_DONT:
221            command = "DONT";
222            break;
223        case TelnetNotificationHandler.RECEIVED_WILL:
224            command = "WILL";
225            break;
226        case TelnetNotificationHandler.RECEIVED_WONT:
227            command = "WONT";
228            break;
229        case TelnetNotificationHandler.RECEIVED_COMMAND:
230            command = "COMMAND";
231            break;
232        default:
233            command = Integer.toString(negotiation_code); // Should not happen
234            break;
235        }
236        System.out.println("Received " + command + " for option code " + option_code);
237    }
238
239    /**
240     * Reader thread: Reads lines from the TelnetClient and echoes them on the screen.
241     */
242    @Override
243    public void run() {
244        final InputStream instr = tc.getInputStream();
245
246        try {
247            final byte[] buff = new byte[1024];
248            int readCount = 0;
249
250            do {
251                readCount = instr.read(buff);
252                if (readCount > 0) {
253                    System.out.print(new String(buff, 0, readCount, Charset.defaultCharset()));
254                }
255            } while (readCount >= 0);
256        } catch (final IOException e) {
257            System.err.println("Exception while reading socket:" + e.getMessage());
258        }
259
260        try {
261            tc.disconnect();
262        } catch (final IOException e) {
263            System.err.println("Exception while closing telnet:" + e.getMessage());
264        }
265    }
266}