summaryrefslogtreecommitdiffstats
path: root/nmap_tty.cc
blob: 0f2b5ff8494ce28b70c8402f6a42a9747caedbdd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
/***************************************************************************
 * nmap_tty.cc -- Handles runtime interaction with Nmap, so you can        *
 * increase verbosity/debugging or obtain a status line upon request.      *
 *                                                                         *
 ***********************IMPORTANT NMAP LICENSE TERMS************************
 *
 * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
 * Project"). Nmap is also a registered trademark of the Nmap Project.
 *
 * This program is distributed under the terms of the Nmap Public Source
 * License (NPSL). The exact license text applying to a particular Nmap
 * release or source code control revision is contained in the LICENSE
 * file distributed with that version of Nmap or source code control
 * revision. More Nmap copyright/legal information is available from
 * https://nmap.org/book/man-legal.html, and further information on the
 * NPSL license itself can be found at https://nmap.org/npsl/ . This
 * header summarizes some key points from the Nmap license, but is no
 * substitute for the actual license text.
 *
 * Nmap is generally free for end users to download and use themselves,
 * including commercial use. It is available from https://nmap.org.
 *
 * The Nmap license generally prohibits companies from using and
 * redistributing Nmap in commercial products, but we sell a special Nmap
 * OEM Edition with a more permissive license and special features for
 * this purpose. See https://nmap.org/oem/
 *
 * If you have received a written Nmap license agreement or contract
 * stating terms other than these (such as an Nmap OEM license), you may
 * choose to use and redistribute Nmap under those terms instead.
 *
 * The official Nmap Windows builds include the Npcap software
 * (https://npcap.com) for packet capture and transmission. It is under
 * separate license terms which forbid redistribution without special
 * permission. So the official Nmap Windows builds may not be redistributed
 * without special permission (such as an Nmap OEM license).
 *
 * Source is provided to this software because we believe users have a
 * right to know exactly what a program is going to do before they run it.
 * This also allows you to audit the software for security holes.
 *
 * Source code also allows you to port Nmap to new platforms, fix bugs, and add
 * new features. You are highly encouraged to submit your changes as a Github PR
 * or by email to the dev@nmap.org mailing list for possible incorporation into
 * the main distribution. Unless you specify otherwise, it is understood that
 * you are offering us very broad rights to use your submissions as described in
 * the Nmap Public Source License Contributor Agreement. This is important
 * because we fund the project by selling licenses with various terms, and also
 * because the inability to relicense code has caused devastating problems for
 * other Free Software projects (such as KDE and NASM).
 *
 * The free version of Nmap is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
 * indemnification and commercial support are all available through the
 * Npcap OEM program--see https://nmap.org/oem/
 *
 ***************************************************************************/

#ifndef WIN32
#include "nmap_config.h"
#endif

#include "nmap.h"

#include <sys/types.h>
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if HAVE_FCNTL_H
#include <fcntl.h>
#endif
#if HAVE_TERMIOS_H
#include <termios.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>

#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif

#include "nmap_tty.h"
#include "NmapOps.h"

extern NmapOps o;

#ifdef WIN32
#include <conio.h>

// Microsoft's runtime makes this fairly simple. :)
void tty_init() { return; }
static int tty_getchar() { return _kbhit() ? _getch() : -1; }
static void tty_done() { return; }

static void tty_flush(void)
{
        static HANDLE stdinput = GetStdHandle(STD_INPUT_HANDLE);

        FlushConsoleInputBuffer(stdinput);
}

#else  //!win32
#include <signal.h>
#if !defined(O_NONBLOCK) && defined(O_NDELAY)
#define O_NONBLOCK			O_NDELAY
#endif

#ifdef __CYGWIN32__
#include <string.h>
#include <sys/socket.h>
#ifndef __CYGWIN__
extern int tcgetattr(int fd, struct termios *termios_p);
extern int tcsetattr(int fd, int actions, struct termios *termios_p);
#endif
#endif

static int tty_fd = 0;
static struct termios saved_ti;

static int tty_getchar()
{
        int c, numChars;
#ifdef __CYGWIN32__
        fd_set set;
        struct timeval tv;
#endif

        if (tty_fd && tcgetpgrp(tty_fd) == getpgrp()) {

        // This is so that when the terminal has been disconnected, it will be
        // reconnected when possible. If it slows things down, just remove it
        // tty_init();

#ifdef __CYGWIN32__
                FD_ZERO(&set); FD_SET(tty_fd, &set);
                tv.tv_sec = 0; tv.tv_usec = 0;
                if (select(tty_fd + 1, &set, NULL, NULL, &tv) <= 0)
                        return -1;
#endif
                c = 0;
                numChars = read(tty_fd, &c, 1);
                if (numChars > 0) return c;
        }

        return -1;
}

static void tty_done()
{
        if (!tty_fd) return;

        tcsetattr(tty_fd, TCSANOW, &saved_ti);

        close(tty_fd);
        tty_fd = 0;
}

static void tty_flush(void)
{
        /* we don't need to test for tty_fd==0 here because
         * this isn't called unless we succeeded
         */

        tcflush(tty_fd, TCIFLUSH);
}

static void install_handler(int signo, void (*handler) (int signo))
{
        struct sigaction sa;
        sa.sa_handler = handler;
        sigfillset(&sa.sa_mask); /* block all signals during handler execution */
        sa.sa_flags = 0;
        sigaction(signo, &sa, NULL);
}

static void shutdown_clean(int signo)
{
        sigset_t set;

/* We reinstall the default handler and call tty_done */
        install_handler(signo, SIG_DFL);
        tty_done();

/* Unblock signo and raise it (thus allowing the default handler to occur) */
        sigemptyset(&set);
        sigaddset(&set, signo);
        sigprocmask(SIG_UNBLOCK, &set, NULL);
        raise(signo); /* This _should_ kill us */
        _exit(EXIT_FAILURE); /* If it does not */
}

static void install_all_handlers() {
        install_handler(SIGINT, shutdown_clean);
        install_handler(SIGTERM, shutdown_clean);
        install_handler(SIGQUIT, shutdown_clean);
}

/*
 * Initializes the terminal for unbuffered non-blocking input. Also
 * registers tty_done() via atexit().  You need to call this before
 * you ever call keyWasPressed().
 */
void tty_init()
{
        struct termios ti;

        if(o.noninteractive)
                return;

        install_all_handlers();

        if (tty_fd)
                return;

        if ((tty_fd = open("/dev/tty", O_RDONLY | O_NONBLOCK)) < 0) {
          o.noninteractive = true;
          return;
        }

#ifndef __CYGWIN32__
        if (tcgetpgrp(tty_fd) != getpgrp()) {
                close(tty_fd); return;
        }
#endif

        tcgetattr(tty_fd, &ti);
        saved_ti = ti;
        ti.c_lflag &= ~(ICANON | ECHO);
        ti.c_cc[VMIN] = 1;
        ti.c_cc[VTIME] = 0;
        tcsetattr(tty_fd, TCSANOW, &ti);

        atexit(tty_done);
}

#endif  //!win32

/* Catches all of the predefined
   keypresses and interpret them, and it will also tell you if you
   should print anything. A value of true being returned means a
   nonstandard key has been pressed and the calling method should
   print a status message */
bool keyWasPressed()
{
  /* Where we keep the automatic stats printing schedule. */
  static struct timeval stats_time = { 0 };
  int c;

  if (o.noninteractive)
    return false;

  if ((c = tty_getchar()) >= 0) {
    tty_flush(); /* flush input queue */

    // printf("You pressed key '%c'!\n", c);
    if (c == 'v') {
       if (o.verbose < 10) o.verbose++;
       log_write(LOG_STDOUT, "Verbosity Increased to %d.\n", o.verbose);
    } else if (c == 'V') {
       if (o.verbose > 0) o.verbose--;
       log_write(LOG_STDOUT, "Verbosity Decreased to %d.\n", o.verbose);
    } else if (c == 'd') {
       if (o.debugging < 10) o.debugging++;
       log_write(LOG_STDOUT, "Debugging Increased to %d.\n", o.debugging);
    } else if (c == 'D') {
       if (o.debugging > 0) o.debugging--;
       log_write(LOG_STDOUT, "Debugging Decreased to %d.\n", o.debugging);
    } else if (c == 'p') {
       o.setPacketTrace(true);
       log_write(LOG_STDOUT, "Packet Tracing enabled.\n");
    } else if (c == 'P') {
       o.setPacketTrace(false);
       log_write(LOG_STDOUT, "Packet Tracing disabled.\n");
    } else if (c == '?') {
      log_write(LOG_STDOUT,
                "Interactive keyboard commands:\n"
                "?               Display this information\n"
                "v/V             Increase/decrease verbosity\n"
                "d/D             Increase/decrease debugging\n"
                "p/P             Enable/disable packet tracing\n"
                "anything else   Print status\n"
                "More help: https://nmap.org/book/man-runtime-interaction.html\n");
    } else {
       printStatusMessage();
       return true;
    }
  }

  /* Check if we need to print a status update according to the --stats-every
     option. */
  if (o.stats_interval != 0.0) {
    struct timeval now;

    gettimeofday(&now, NULL);
    if (stats_time.tv_sec == 0) {
      /* Initialize the scheduled stats time. */
      stats_time = *o.getStartTime();
      TIMEVAL_ADD(stats_time, stats_time, (time_t) (o.stats_interval * 1000000));
    }

    if (TIMEVAL_AFTER(now, stats_time)) {
      /* Advance to the next print time. */
      TIMEVAL_ADD(stats_time, stats_time, (time_t) (o.stats_interval * 1000000));
      /* If it's still in the past, catch it up to the present,
       * plus half a second to avoid double-printing without any progress. */
      if (TIMEVAL_AFTER(now, stats_time))
        TIMEVAL_MSEC_ADD(stats_time, now, 500);
      printStatusMessage();
      /* Instruct the caller to print status too. */
      return true;
    }
  }

  return false;
}