1 /*
2  * netcat.c -- main project file
3  * Part of the GNU netcat project
4  *
5  * Author: Giovanni Giacobbi <giovanni@giacobbi.net>
6  * Copyright (C) 2002 - 2003  Giovanni Giacobbi
7  *
8  * $Id: netcat.c,v 1.63 2003/08/21 15:27:18 themnemonic Exp $
9  */
10 
11 /***************************************************************************
12  *                                                                         *
13  *   This program is free software; you can redistribute it and/or modify  *
14  *   it under the terms of the GNU General Public License as published by  *
15  *   the Free Software Foundation; either version 2 of the License, or     *
16  *   (at your option) any later version.                                   *
17  *                                                                         *
18  *   This program is distributed in the hope that it will be useful,       *
19  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
20  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
21  *   GNU General Public License for more details.                          *
22  *                                                                         *
23  ***************************************************************************/
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include "netcat.h"
30 #include <signal.h>
31 #include <getopt.h>
32 #include <time.h>		/* time(2) used as random seed */
33 
34 /* int gatesidx = 0; */		/* LSRR hop count */
35 /* int gatesptr = 4; */		/* initial LSRR pointer, settable */
36 /* nc_host_t **gates = NULL; */	/* LSRR hop hostpoop */
37 /* char *optbuf = NULL; */	/* LSRR or sockopts */
38 FILE *output_fp = NULL;		/* output fd (FIXME: i don't like this) */
39 bool use_stdin = TRUE;		/* tells wether stdin was closed or not */
40 bool signal_handler = TRUE;	/* handle the signals externally */
41 bool got_sigterm = FALSE;	/* when this TRUE the application must exit */
42 bool got_sigint = FALSE;	/* when this TRUE the application should exit */
43 bool got_sigusr1 = FALSE;	/* when set, the application should print stats */
44 bool commandline_need_newline = FALSE;	/* fancy output handling */
45 
46 /* global options flags */
47 nc_mode_t netcat_mode = 0;	/* Netcat working modality */
48 bool opt_eofclose = FALSE;	/* close connection on EOF from stdin */
49 bool opt_debug = FALSE;		/* debugging output */
50 bool opt_numeric = FALSE;	/* don't resolve hostnames */
51 bool opt_random = FALSE;	/* use random ports */
52 bool opt_udpmode = FALSE;	/* use udp protocol instead of tcp */
53 bool opt_telnet = FALSE;	/* answer in telnet mode */
54 bool opt_hexdump = FALSE;	/* hexdump traffic */
55 bool opt_zero = FALSE;		/* zero I/O mode (don't expect anything) */
56 int opt_interval = 0;		/* delay (in seconds) between lines/ports */
57 int opt_verbose = 0;		/* be verbose (> 1 to be MORE verbose) */
58 int opt_wait = 0;		/* wait time */
59 char *opt_outputfile = NULL;	/* hexdump output file */
60 char *opt_exec = NULL;		/* program to exec after connecting */
61 nc_proto_t opt_proto = NETCAT_PROTO_TCP; /* protocol to use for connections */
62 
63 
64 /* signal handling */
65 
got_term(int z)66 static void got_term(int z)
67 {
68   if (!got_sigterm)
69     ncprint(NCPRINT_VERB1, _("Terminated."));
70   debug_v(("_____ RECEIVED SIGTERM _____ [signal_handler=%s]",
71 	  BOOL_TO_STR(signal_handler)));
72   got_sigterm = TRUE;
73   if (signal_handler)			/* default action */
74     exit(EXIT_FAILURE);
75 }
76 
got_int(int z)77 static void got_int(int z)
78 {
79   if (!got_sigint)
80     ncprint(NCPRINT_VERB1, _("Exiting."));
81   debug_v(("_____ RECEIVED SIGINT _____ [signal_handler=%s]",
82 	  BOOL_TO_STR(signal_handler)));
83   got_sigint = TRUE;
84   if (signal_handler) {			/* default action */
85     if (commandline_need_newline)	/* if we were waiting for input */
86       printf("\n");
87     netcat_printstats(FALSE);
88     exit(EXIT_FAILURE);
89   }
90 }
91 
got_usr1(int z)92 static void got_usr1(int z)
93 {
94   debug_dv(("_____ RECEIVED SIGUSR1 _____ [signal_handler=%s]",
95 	   BOOL_TO_STR(signal_handler)));
96   if (signal_handler)			/* default action */
97     netcat_printstats(TRUE);
98   else
99     got_sigusr1 = TRUE;
100 }
101 
102 /* Execute an external file making its stdin/stdout/stderr the actual socket */
103 
ncexec(nc_sock_t * ncsock)104 static void ncexec(nc_sock_t *ncsock)
105 {
106   int saved_stderr;
107   char *p;
108   assert(ncsock && (ncsock->fd >= 0));
109 
110   /* save the stderr fd because we may need it later */
111   saved_stderr = dup(STDERR_FILENO);
112 
113   /* duplicate the socket for the child program */
114   dup2(ncsock->fd, STDIN_FILENO);	/* the precise order of fiddlage */
115   close(ncsock->fd);			/* is apparently crucial; this is */
116   dup2(STDIN_FILENO, STDOUT_FILENO);	/* swiped directly out of "inetd". */
117   dup2(STDIN_FILENO, STDERR_FILENO);	/* also duplicate the stderr channel */
118 
119   /* change the label for the executed program */
120   if ((p = strrchr(opt_exec, '/')))
121     p++;			/* shorter argv[0] */
122   else
123     p = opt_exec;
124 
125   /* replace this process with the new one */
126 #ifndef USE_OLD_COMPAT
127   execl("/bin/sh", p, "-c", opt_exec, NULL);
128 #else
129   execl(opt_exec, p, NULL);
130 #endif
131   dup2(saved_stderr, STDERR_FILENO);
132   ncprint(NCPRINT_ERROR | NCPRINT_EXIT, _("Couldn't execute %s: %s"),
133 	  opt_exec, strerror(errno));
134 }				/* end of ncexec() */
135 
136 /* main: handle command line arguments and listening status */
137 
main(int argc,char * argv[])138 int main(int argc, char *argv[])
139 {
140   int c, glob_ret = EXIT_FAILURE;
141   int total_ports, left_ports, accept_ret = -1, connect_ret = -1;
142   struct sigaction sv;
143   nc_port_t local_port;		/* local port specified with -p option */
144   nc_host_t local_host;		/* local host for bind()ing operations */
145   nc_host_t remote_host;
146   nc_sock_t listen_sock;
147   nc_sock_t connect_sock;
148   nc_sock_t stdio_sock;
149 
150   memset(&local_port, 0, sizeof(local_port));
151   memset(&local_host, 0, sizeof(local_host));
152   memset(&remote_host, 0, sizeof(remote_host));
153   memset(&listen_sock, 0, sizeof(listen_sock));
154   memset(&connect_sock, 0, sizeof(listen_sock));
155   memset(&stdio_sock, 0, sizeof(stdio_sock));
156   listen_sock.domain = PF_INET;
157   connect_sock.domain = PF_INET;
158 
159 #ifdef ENABLE_NLS
160   setlocale(LC_MESSAGES, "");
161   bindtextdomain(PACKAGE, LOCALEDIR);
162   textdomain(PACKAGE);
163 #endif
164 
165   /* set up the signal handling system */
166   sigemptyset(&sv.sa_mask);
167   sv.sa_flags = 0;
168   sv.sa_handler = got_int;
169   sigaction(SIGINT, &sv, NULL);
170   sv.sa_handler = got_term;
171   sigaction(SIGTERM, &sv, NULL);
172   sv.sa_handler = got_usr1;
173   sigaction(SIGUSR1, &sv, NULL);
174   /* ignore some boring signals */
175   sv.sa_handler = SIG_IGN;
176   sigaction(SIGPIPE, &sv, NULL);
177   sigaction(SIGURG, &sv, NULL);
178 
179   /* if no args given at all, take them from stdin and generate argv */
180   if (argc == 1)
181     netcat_commandline_read(&argc, &argv);
182 
183   /* check for command line switches */
184   while (TRUE) {
185     int option_index = 0;
186     static const struct option long_options[] = {
187 	{ "close",	no_argument,		NULL, 'c' },
188 	{ "debug",	no_argument,		NULL, 'd' },
189 	{ "exec",	required_argument,	NULL, 'e' },
190 	{ "gateway",	required_argument,	NULL, 'g' },
191 	{ "pointer",	required_argument,	NULL, 'G' },
192 	{ "help",	no_argument,		NULL, 'h' },
193 	{ "interval",	required_argument,	NULL, 'i' },
194 	{ "listen",	no_argument,		NULL, 'l' },
195 	{ "tunnel",	required_argument,	NULL, 'L' },
196 	{ "dont-resolve", no_argument,		NULL, 'n' },
197 	{ "output",	required_argument,	NULL, 'o' },
198 	{ "local-port",	required_argument,	NULL, 'p' },
199 	{ "tunnel-port", required_argument,	NULL, 'P' },
200 	{ "randomize",	no_argument,		NULL, 'r' },
201 	{ "source",	required_argument,	NULL, 's' },
202 	{ "tunnel-source", required_argument,	NULL, 'S' },
203 #ifndef USE_OLD_COMPAT
204 	{ "tcp",	no_argument,		NULL, 't' },
205 	{ "telnet",	no_argument,		NULL, 'T' },
206 #else
207 	{ "tcp",	no_argument,		NULL, 1 },
208 	{ "telnet",	no_argument,		NULL, 't' },
209 #endif
210 	{ "udp",	no_argument,		NULL, 'u' },
211 	{ "verbose",	no_argument,		NULL, 'v' },
212 	{ "version",	no_argument,		NULL, 'V' },
213 	{ "hexdump",	no_argument,		NULL, 'x' },
214 	{ "wait",	required_argument,	NULL, 'w' },
215 	{ "zero",	no_argument,		NULL, 'z' },
216 	{ 0, 0, 0, 0 }
217     };
218 
219     c = getopt_long(argc, argv, "cde:g:G:hi:lL:no:p:P:rs:S:tTuvVxw:z",
220 		    long_options, &option_index);
221     if (c == -1)
222       break;
223 
224     switch (c) {
225     case 'c':			/* close connection on EOF from stdin */
226       opt_eofclose = TRUE;
227       break;
228     case 'd':			/* enable debugging */
229       opt_debug = TRUE;
230       break;
231     case 'e':			/* prog to exec */
232       if (opt_exec)
233 	ncprint(NCPRINT_ERROR | NCPRINT_EXIT,
234 		_("Cannot specify `-e' option double"));
235       opt_exec = strdup(optarg);
236       break;
237     case 'G':			/* srcrt gateways pointer val */
238       break;
239     case 'g':			/* srcroute hop[s] */
240       break;
241     case 'h':			/* display help and exit */
242       netcat_printhelp(argv[0]);
243       exit(EXIT_SUCCESS);
244     case 'i':			/* line/ports interval time (seconds) */
245       opt_interval = atoi(optarg);
246       if (opt_interval <= 0)
247 	ncprint(NCPRINT_ERROR | NCPRINT_EXIT,
248 		_("Invalid interval time \"%s\""), optarg);
249       break;
250     case 'l':			/* mode flag: listen mode */
251       if (netcat_mode != NETCAT_UNSPEC)
252 	ncprint(NCPRINT_ERROR | NCPRINT_EXIT,
253 		_("You can specify mode flags (`-l' and `-L') only once"));
254       netcat_mode = NETCAT_LISTEN;
255       break;
256     case 'L':			/* mode flag: tunnel mode */
257       if (netcat_mode != NETCAT_UNSPEC)
258 	ncprint(NCPRINT_ERROR | NCPRINT_EXIT,
259 		_("You can specify mode flags (`-l' and `-L') only once"));
260       if (opt_zero)
261 	ncprint(NCPRINT_ERROR | NCPRINT_EXIT,
262 		_("`-L' and `-z' options are incompatible"));
263       do {
264 	char *div = strchr(optarg, ':');
265 
266 	if (div && *(div + 1))
267 	  *div++ = '\0';
268 	else
269 	  ncprint(NCPRINT_ERROR | NCPRINT_EXIT,
270 		_("Invalid target string for `-L' option"));
271 
272 	/* lookup the remote address and the remote port for tunneling */
273 	if (!netcat_resolvehost(&connect_sock.host, optarg))
274 	  ncprint(NCPRINT_ERROR | NCPRINT_EXIT,
275 	  	  _("Couldn't resolve tunnel target host: %s"), optarg);
276 	if (!netcat_getport(&connect_sock.port, div, 0))
277 	  ncprint(NCPRINT_ERROR | NCPRINT_EXIT,
278 	  	  _("Invalid tunnel target port: %s"), div);
279 
280 	connect_sock.proto = opt_proto;
281 	connect_sock.timeout = opt_wait;
282 	netcat_mode = NETCAT_TUNNEL;
283       } while (FALSE);
284       break;
285     case 'n':			/* numeric-only, no DNS lookups */
286       opt_numeric = TRUE;
287       break;
288     case 'o':			/* output hexdump log to file */
289       opt_outputfile = strdup(optarg);
290       opt_hexdump = TRUE;	/* implied */
291       break;
292     case 'p':			/* local source port */
293       if (!netcat_getport(&local_port, optarg, 0))
294 	ncprint(NCPRINT_ERROR | NCPRINT_EXIT, _("Invalid local port: %s"),
295 		optarg);
296       break;
297     case 'P':			/* used only in tunnel mode (source port) */
298       if (!netcat_getport(&connect_sock.local_port, optarg, 0))
299 	ncprint(NCPRINT_ERROR | NCPRINT_EXIT,
300 		_("Invalid tunnel connect port: %s"), optarg);
301       break;
302     case 'r':			/* randomize various things */
303       opt_random = TRUE;
304       break;
305     case 's':			/* local source address */
306       /* lookup the source address and assign it to the connection address */
307       if (!netcat_resolvehost(&local_host, optarg))
308 	ncprint(NCPRINT_ERROR | NCPRINT_EXIT,
309 		_("Couldn't resolve local host: %s"), optarg);
310       break;
311     case 'S':			/* used only in tunnel mode (source ip) */
312       if (!netcat_resolvehost(&connect_sock.local_host, optarg))
313 	ncprint(NCPRINT_ERROR | NCPRINT_EXIT,
314 		_("Couldn't resolve tunnel local host: %s"), optarg);
315       break;
316     case 1:			/* use TCP protocol (default) */
317 #ifndef USE_OLD_COMPAT
318     case 't':
319 #endif
320       opt_proto = NETCAT_PROTO_TCP;
321       break;
322 #ifdef USE_OLD_COMPAT
323     case 't':
324 #endif
325     case 'T':			/* answer telnet codes */
326       opt_telnet = TRUE;
327       break;
328     case 'u':			/* use UDP protocol */
329       opt_proto = NETCAT_PROTO_UDP;
330       break;
331     case 'v':			/* be verbose (twice=more verbose) */
332       opt_verbose++;
333       break;
334     case 'V':			/* display version and exit */
335       netcat_printversion();
336       exit(EXIT_SUCCESS);
337     case 'w':			/* wait time (in seconds) */
338       opt_wait = atoi(optarg);
339       if (opt_wait <= 0)
340 	ncprint(NCPRINT_ERROR | NCPRINT_EXIT, _("Invalid wait-time: %s"),
341 		optarg);
342       break;
343     case 'x':			/* hexdump traffic */
344       opt_hexdump = TRUE;
345       break;
346     case 'z':			/* little or no data xfer */
347       if (netcat_mode == NETCAT_TUNNEL)
348 	ncprint(NCPRINT_ERROR | NCPRINT_EXIT,
349 		_("`-L' and `-z' options are incompatible"));
350       opt_zero = TRUE;
351       break;
352     default:
353       ncprint(NCPRINT_EXIT, _("Try `%s --help' for more information."), argv[0]);
354     }
355   }
356 
357   if (opt_zero && opt_exec)
358     ncprint(NCPRINT_ERROR | NCPRINT_EXIT,
359 		_("`-e' and `-z' options are incompatible"));
360 
361   /* initialize the flag buffer to keep track of the specified ports */
362   netcat_flag_init(65535);
363 
364 #ifndef DEBUG
365   /* check for debugging support */
366   if (opt_debug)
367     ncprint(NCPRINT_WARNING,
368 	    _("Debugging support not compiled, option `-d' discarded. Using maximum verbosity."));
369 #endif
370 
371   /* randomize only if needed */
372   if (opt_random)
373 #ifdef USE_RANDOM
374     SRAND(time(0));
375 #else
376     ncprint(NCPRINT_WARNING,
377 	    _("Randomization support not compiled, option `-r' discarded."));
378 #endif
379 
380   /* handle the -o option. exit on failure */
381   if (opt_outputfile) {
382     output_fp = fopen(opt_outputfile, "w");
383     if (!output_fp)
384       ncprint(NCPRINT_ERROR | NCPRINT_EXIT, _("Failed to open output file: %s"),
385 	      strerror(errno));
386   }
387   else
388     output_fp = stderr;
389 
390   debug_v(("Trying to parse non-args parameters (argc=%d, optind=%d)", argc,
391 	  optind));
392 
393   /* try to get an hostname parameter */
394   if (optind < argc) {
395     char *myhost = argv[optind++];
396     if (!netcat_resolvehost(&remote_host, myhost))
397       ncprint(NCPRINT_ERROR | NCPRINT_EXIT, _("Couldn't resolve host \"%s\""),
398 	      myhost);
399   }
400 
401   /* now loop all the other (maybe optional) parameters for port-ranges */
402   while (optind < argc) {
403     const char *get_argv = argv[optind++];
404     char *q, *parse = strdup(get_argv);
405     int port_lo = 0, port_hi = 65535;
406     nc_port_t port_tmp;
407 
408     if (!(q = strchr(parse, '-')))	/* simple number? */
409       q = strchr(parse, ':');		/* try with the other separator */
410 
411     if (!q) {
412       if (netcat_getport(&port_tmp, parse, 0))
413 	netcat_flag_set(port_tmp.num, TRUE);
414       else
415 	goto got_err;
416     }
417     else {		/* could be in the forms: N1-N2, -N2, N1- */
418       *q++ = 0;
419       if (*parse) {
420 	if (netcat_getport(&port_tmp, parse, 0))
421 	  port_lo = port_tmp.num;
422 	else
423 	  goto got_err;
424       }
425       if (*q) {
426 	if (netcat_getport(&port_tmp, q, 0))
427 	  port_hi = port_tmp.num;
428 	else
429 	  goto got_err;
430       }
431       if (!*parse && !*q)		/* don't accept the form '-' */
432 	goto got_err;
433 
434       /* now update the flagset (this is int, so it's ok even if hi == 65535) */
435       while (port_lo <= port_hi)
436 	netcat_flag_set(port_lo++, TRUE);
437     }
438 
439     free(parse);
440     continue;
441 
442  got_err:
443     free(parse);
444     ncprint(NCPRINT_ERROR, _("Invalid port specification: %s"), get_argv);
445     exit(EXIT_FAILURE);
446   }
447 
448   debug_dv(("Arguments parsing complete! Total ports=%d", netcat_flag_count()));
449 #if 0
450   /* pure debugging code */
451   c = 0;
452   while ((c = netcat_flag_next(c))) {
453     printf("Got port=%d\n", c);
454   }
455   exit(0);
456 #endif
457 
458   /* Handle listen mode and tunnel mode (whose index number is higher) */
459   if (netcat_mode > NETCAT_CONNECT) {
460     /* in tunnel mode the opt_zero flag is illegal, while on listen mode it
461        means that no connections should be accepted.  For UDP it means that
462        no remote addresses should be used as default endpoint, which means
463        that we can't send anything.  In both situations, stdin is no longer
464        useful, so close it. */
465     if (opt_zero) {
466       close(STDIN_FILENO);
467       use_stdin = FALSE;
468     }
469 
470     /* prepare the socket var and start listening */
471     listen_sock.proto = opt_proto;
472     listen_sock.timeout = opt_wait;
473     memcpy(&listen_sock.local_host, &local_host, sizeof(listen_sock.local_host));
474     memcpy(&listen_sock.local_port, &local_port, sizeof(listen_sock.local_port));
475     memcpy(&listen_sock.host, &remote_host, sizeof(listen_sock.host));
476     accept_ret = core_listen(&listen_sock);
477 
478     /* in zero I/O mode the core_tcp_listen() call will always return -1
479        (ETIMEDOUT) since no connections are accepted, because of this our job
480        is completed now. */
481     if (accept_ret < 0) {
482       /* since i'm planning to make `-z' compatible with `-L' I need to check
483          the exact error that caused this failure. */
484       if (opt_zero && (errno == ETIMEDOUT))
485 	exit(0);
486 
487       ncprint(NCPRINT_VERB1 | NCPRINT_EXIT, _("Listen mode failed: %s"),
488 	      strerror(errno));
489     }
490 
491     /* if we are in listen mode, run the core loop and exit when it returns.
492        otherwise now it's the time to connect to the target host and tunnel
493        them together (which means passing to the next section. */
494     if (netcat_mode == NETCAT_LISTEN) {
495       if (opt_exec) {
496 	ncprint(NCPRINT_VERB2, _("Passing control to the specified program"));
497 	ncexec(&listen_sock);		/* this won't return */
498       }
499       core_readwrite(&listen_sock, &stdio_sock);
500       debug_dv(("Listen: EXIT"));
501     }
502     else {
503       /* otherwise we are in tunnel mode.  The connect_sock var was already
504          initialized by the command line arguments. */
505       assert(netcat_mode == NETCAT_TUNNEL);
506       connect_ret = core_connect(&connect_sock);
507 
508       /* connection failure? (we cannot get this in UDP mode) */
509       if (connect_ret < 0) {
510 	assert(opt_proto != NETCAT_PROTO_UDP);
511 	ncprint(NCPRINT_VERB1, "%s: %s",
512 		netcat_strid(&connect_sock.host, &connect_sock.port),
513 		strerror(errno));
514       }
515       else {
516 	glob_ret = EXIT_SUCCESS;
517 	core_readwrite(&listen_sock, &connect_sock);
518 	debug_dv(("Tunnel: EXIT (ret=%d)", glob_ret));
519       }
520     }
521 
522     /* all jobs should be ok, go to the cleanup */
523     goto main_exit;
524   }				/* end of listen and tunnel mode handling */
525 
526   /* we need to connect outside, this is the connect mode */
527   netcat_mode = NETCAT_CONNECT;
528 
529   /* first check that a host parameter was given */
530   if (!remote_host.iaddrs[0].s_addr) {
531     /* FIXME: The Networking specifications state that host address "0" is a
532        valid host to connect to but this broken check will assume as not
533        specified. */
534     ncprint(NCPRINT_NORMAL, _("%s: missing hostname argument"), argv[0]);
535     ncprint(NCPRINT_EXIT, _("Try `%s --help' for more information."), argv[0]);
536   }
537 
538   /* since ports are the second argument, checking ports might be enough */
539   total_ports = netcat_flag_count();
540   if (total_ports == 0)
541     ncprint(NCPRINT_ERROR | NCPRINT_EXIT,
542 	    _("No ports specified for connection"));
543 
544   c = 0;			/* must be set to 0 for netcat_flag_next() */
545   left_ports = total_ports;
546   while (left_ports > 0) {
547     /* `c' is the port number independently of the sorting method (linear
548        or random).  While in linear mode it is also used to fetch the next
549        port number */
550     if (opt_random)
551       c = netcat_flag_rand();
552     else
553       c = netcat_flag_next(c);
554     left_ports--;		/* decrease the total ports number to try */
555 
556     /* since we are nonblocking now, we can start as many connections as we want
557        but it's not a great idea connecting more than one host at time */
558     connect_sock.proto = opt_proto;
559     connect_sock.timeout = opt_wait;
560     memcpy(&connect_sock.local_host, &local_host,
561 	   sizeof(connect_sock.local_host));
562     memcpy(&connect_sock.local_port, &local_port,
563 	   sizeof(connect_sock.local_port));
564     memcpy(&connect_sock.host, &remote_host, sizeof(connect_sock.host));
565     netcat_getport(&connect_sock.port, NULL, c);
566 
567     /* FIXME: in udp mode and NETCAT_CONNECT, opt_zero is senseless */
568     connect_ret = core_connect(&connect_sock);
569 
570     /* connection failure? (we cannot get this in UDP mode) */
571     if (connect_ret < 0) {
572       int ncprint_flags = NCPRINT_VERB1;
573       assert(connect_sock.proto != NETCAT_PROTO_UDP);
574 
575       /* if we are portscanning or multiple connecting show only open
576          ports with verbosity level 1. */
577       if (total_ports > 1)
578 	ncprint_flags = NCPRINT_VERB2;
579 
580       ncprint(ncprint_flags, "%s: %s",
581 	      netcat_strid(&connect_sock.host, &connect_sock.port),
582 	      strerror(errno));
583       continue;			/* go with next port */
584     }
585 
586     /* when portscanning (or checking a single port) we are happy if AT LEAST
587        ONE port is available. */
588     glob_ret = EXIT_SUCCESS;
589 
590     if (opt_zero) {
591       shutdown(connect_ret, 2);
592       close(connect_ret);
593     }
594     else {
595       if (opt_exec) {
596 	ncprint(NCPRINT_VERB2, _("Passing control to the specified program"));
597 	ncexec(&connect_sock);		/* this won't return */
598       }
599       core_readwrite(&connect_sock, &stdio_sock);
600       /* FIXME: add a small delay */
601       debug_v(("Connect: EXIT"));
602 
603       /* both signals are handled inside core_readwrite(), but while the
604          SIGINT signal is fully handled, the SIGTERM requires some action
605          from outside that function, because of this that flag is not
606          cleared. */
607       if (got_sigterm)
608 	break;
609     }
610   }			/* end of while (left_ports > 0) */
611 
612   /* all basic modes should return here for the final cleanup */
613  main_exit:
614   debug_v(("Main: EXIT (cleaning up)"));
615 
616   netcat_printstats(FALSE);
617   return glob_ret;
618 }				/* end of main() */
619