int
main (int argc, char **argv)
{
	short supports_tls=FALSE;
	int n = 0;
	double elapsed_time;
	long microsec;
	int result = STATE_UNKNOWN;
	char *cmd_str = NULL;
	char *helocmd = NULL;
	char *error_msg = "";
	struct timeval tv;

	/* Catch pipe errors in read/write - sometimes occurs when writing QUIT */
	(void) signal (SIGPIPE, SIG_IGN);

	setlocale (LC_ALL, "");
	bindtextdomain (PACKAGE, LOCALEDIR);
	textdomain (PACKAGE);

	/* Parse extra opts if any */
	argv=np_extra_opts (&argc, argv, progname);

	if (process_arguments (argc, argv) == ERROR)
		usage4 (_("Could not parse arguments"));

	/* If localhostname not set on command line, use gethostname to set */
	if(! localhostname){
		localhostname = malloc (HOST_MAX_BYTES);
		if(!localhostname){
			printf(_("malloc() failed!\n"));
			return STATE_CRITICAL;
		}
		if(gethostname(localhostname, HOST_MAX_BYTES)){
			printf(_("gethostname() failed!\n"));
			return STATE_CRITICAL;
		}
	}
	if(use_ehlo)
		xasprintf (&helocmd, "%s%s%s", SMTP_EHLO, localhostname, "\r\n");
	else
		xasprintf (&helocmd, "%s%s%s", SMTP_HELO, localhostname, "\r\n");

	if (verbose)
		printf("HELOCMD: %s", helocmd);

	/* initialize the MAIL command with optional FROM command  */
	xasprintf (&cmd_str, "%sFROM:<%s>%s", mail_command, from_arg, "\r\n");

	if (verbose && send_mail_from)
		printf ("FROM CMD: %s", cmd_str);

	/* initialize alarm signal handling */
	(void) signal (SIGALRM, socket_timeout_alarm_handler);

	/* set socket timeout */
	(void) alarm (socket_timeout);

	/* start timer */
	gettimeofday (&tv, NULL);

	/* try to connect to the host at the given port number */
	result = my_tcp_connect (server_address, server_port, &sd);

	if (result == STATE_OK) { /* we connected */

		/* watch for the SMTP connection string and */
		/* return a WARNING status if we couldn't read any data */
		if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) {
			printf (_("recv() failed\n"));
			return STATE_WARNING;
		}
		else {
			if (verbose)
				printf ("%s", buffer);
			/* strip the buffer of carriage returns */
			strip (buffer);
			/* make sure we find the response we are looking for */
			if (!strstr (buffer, server_expect)) {
				if (server_port == SMTP_PORT)
					printf (_("Invalid SMTP response received from host: %s\n"), buffer);
				else
					printf (_("Invalid SMTP response received from host on port %d: %s\n"),
									server_port, buffer);
				return STATE_WARNING;
			}
		}

		/* send the HELO/EHLO command */
		send(sd, helocmd, strlen(helocmd), 0);

		/* allow for response to helo command to reach us */
		if (recvlines(buffer, MAX_INPUT_BUFFER) <= 0) {
			printf (_("recv() failed\n"));
			return STATE_WARNING;
		} else if(use_ehlo){
			if(strstr(buffer, "250 STARTTLS") != NULL ||
			   strstr(buffer, "250-STARTTLS") != NULL){
				supports_tls=TRUE;
			}
		}

		if(use_ssl && ! supports_tls){
			printf(_("WARNING - TLS not supported by server\n"));
			smtp_quit();
			return STATE_WARNING;
		}

#ifdef HAVE_SSL
		if(use_ssl) {
		  /* send the STARTTLS command */
		  send(sd, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0);

		  recvlines(buffer, MAX_INPUT_BUFFER); /* wait for it */
		  if (!strstr (buffer, server_expect)) {
		    printf (_("Server does not support STARTTLS\n"));
		    smtp_quit();
		    return STATE_UNKNOWN;
		  }
		  result = np_net_ssl_init(sd);
		  if(result != STATE_OK) {
		    printf (_("CRITICAL - Cannot create SSL context.\n"));
		    np_net_ssl_cleanup();
		    close(sd);
		    return STATE_CRITICAL;
		  } else {
			ssl_established = 1;
		  }

		/*
		 * Resend the EHLO command.
		 *
		 * RFC 3207 (4.2) says: ``The client MUST discard any knowledge
		 * obtained from the server, such as the list of SMTP service
		 * extensions, which was not obtained from the TLS negotiation
		 * itself.  The client SHOULD send an EHLO command as the first
		 * command after a successful TLS negotiation.''  For this
		 * reason, some MTAs will not allow an AUTH LOGIN command before
		 * we resent EHLO via TLS.
		 */
		if (my_send(helocmd, strlen(helocmd)) <= 0) {
			printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS."));
			my_close();
			return STATE_UNKNOWN;
		}
		if (verbose)
			printf(_("sent %s"), helocmd);
		if ((n = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) {
			printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS."));
			my_close();
			return STATE_UNKNOWN;
		}
		if (verbose) {
			printf("%s", buffer);
		}

#  ifdef USE_OPENSSL
		  if ( check_cert ) {
                    result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit);
		    my_close();
		    return result;
		  }
#  endif /* USE_OPENSSL */
		}
#endif

		if (send_mail_from) {
		  my_send(cmd_str, strlen(cmd_str));
		  if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose)
		    printf("%s", buffer);
		}

		while (n < ncommands) {
			xasprintf (&cmd_str, "%s%s", commands[n], "\r\n");
			my_send(cmd_str, strlen(cmd_str));
			if (recvlines(buffer, MAX_INPUT_BUFFER) >= 1 && verbose)
				printf("%s", buffer);
			strip (buffer);
			if (n < nresponses) {
				cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
				errcode = regcomp (&preg, responses[n], cflags);
				if (errcode != 0) {
					regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
					printf (_("Could Not Compile Regular Expression"));
					return ERROR;
				}
				excode = regexec (&preg, buffer, 10, pmatch, eflags);
				if (excode == 0) {
					result = STATE_OK;
				}
				else if (excode == REG_NOMATCH) {
					result = STATE_WARNING;
					printf (_("SMTP %s - Invalid response '%s' to command '%s'\n"), state_text (result), buffer, commands[n]);
				}
				else {
					regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER);
					printf (_("Execute Error: %s\n"), errbuf);
					result = STATE_UNKNOWN;
				}
			}
			n++;
		}

		if (authtype != NULL) {
			if (strcmp (authtype, "LOGIN") == 0) {
				char *abuf;
				int ret;
				do {
					if (authuser == NULL) {
						result = STATE_CRITICAL;
						xasprintf(&error_msg, _("no authuser specified, "));
						break;
					}
					if (authpass == NULL) {
						result = STATE_CRITICAL;
						xasprintf(&error_msg, _("no authpass specified, "));
						break;
					}

					/* send AUTH LOGIN */
					my_send(SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN));
					if (verbose)
						printf (_("sent %s\n"), "AUTH LOGIN");

					if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) {
						xasprintf(&error_msg, _("recv() failed after AUTH LOGIN, "));
						result = STATE_WARNING;
						break;
					}
					if (verbose)
						printf (_("received %s\n"), buffer);

					if (strncmp (buffer, "334", 3) != 0) {
						result = STATE_CRITICAL;
						xasprintf(&error_msg, _("invalid response received after AUTH LOGIN, "));
						break;
					}

					/* encode authuser with base64 */
					base64_encode_alloc (authuser, strlen(authuser), &abuf);
					xasprintf(&abuf, "%s\r\n", abuf);
					my_send(abuf, strlen(abuf));
					if (verbose)
						printf (_("sent %s\n"), abuf);

					if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) {
						result = STATE_CRITICAL;
						xasprintf(&error_msg, _("recv() failed after sending authuser, "));
						break;
					}
					if (verbose) {
						printf (_("received %s\n"), buffer);
					}
					if (strncmp (buffer, "334", 3) != 0) {
						result = STATE_CRITICAL;
						xasprintf(&error_msg, _("invalid response received after authuser, "));
						break;
					}
					/* encode authpass with base64 */
					base64_encode_alloc (authpass, strlen(authpass), &abuf);
					xasprintf(&abuf, "%s\r\n", abuf);
					my_send(abuf, strlen(abuf));
					if (verbose) {
						printf (_("sent %s\n"), abuf);
					}
					if ((ret = recvlines(buffer, MAX_INPUT_BUFFER)) <= 0) {
						result = STATE_CRITICAL;
						xasprintf(&error_msg, _("recv() failed after sending authpass, "));
						break;
					}
					if (verbose) {
						printf (_("received %s\n"), buffer);
					}
					if (strncmp (buffer, "235", 3) != 0) {
						result = STATE_CRITICAL;
						xasprintf(&error_msg, _("invalid response received after authpass, "));
						break;
					}
					break;
				} while (0);
			} else {
				result = STATE_CRITICAL;
				xasprintf(&error_msg, _("only authtype LOGIN is supported, "));
			}
		}

		/* tell the server we're done */
		smtp_quit();

		/* finally close the connection */
		close (sd);
	}

	/* reset the alarm */
	alarm (0);

	microsec = deltime (tv);
	elapsed_time = (double)microsec / 1.0e6;

	if (result == STATE_OK) {
		if (check_critical_time && elapsed_time > critical_time)
			result = STATE_CRITICAL;
		else if (check_warning_time && elapsed_time > warning_time)
			result = STATE_WARNING;
	}

	printf (_("SMTP %s - %s%.3f sec. response time%s%s|%s\n"),
			state_text (result),
			error_msg,
			elapsed_time,
			verbose?", ":"", verbose?buffer:"",
			fperfdata ("time", elapsed_time, "s",
				(int)check_warning_time, warning_time,
				(int)check_critical_time, critical_time,
				TRUE, 0, FALSE, 0));

	return result;
}
int
main (int argc, char **argv)
{
	int result = STATE_UNKNOWN;
	int i;
	char *status = NULL;
	struct timeval tv;
	size_t len;
	int match = -1;

	setlocale (LC_ALL, "");
	bindtextdomain (PACKAGE, LOCALEDIR);
	textdomain (PACKAGE);

	/* determine program- and service-name quickly */
	progname = strrchr(argv[0], '/');
	if(progname != NULL) progname++;
	else progname = argv[0];

	len = strlen(progname);
	if(len > 6 && !memcmp(progname, "check_", 6)) {
		SERVICE = strdup(progname + 6);
		for(i = 0; i < len - 6; i++)
			SERVICE[i] = toupper(SERVICE[i]);
	}

	/* set up a resonable buffer at first (will be realloc()'ed if
	 * user specifies other options) */
	server_expect = calloc(sizeof(char *), 2);

	/* determine defaults for this service's protocol */
	if (!strncmp(SERVICE, "UDP", 3)) {
		PROTOCOL = IPPROTO_UDP;
	}
	else if (!strncmp(SERVICE, "FTP", 3)) {
		EXPECT = "220";
		QUIT = "QUIT\r\n";
		PORT = 21;
	}
	else if (!strncmp(SERVICE, "POP", 3) || !strncmp(SERVICE, "POP3", 4)) {
		EXPECT = "+OK";
		QUIT = "QUIT\r\n";
		PORT = 110;
	}
	else if (!strncmp(SERVICE, "SMTP", 4)) {
		EXPECT = "220";
		QUIT = "QUIT\r\n";
		PORT = 25;
	}
	else if (!strncmp(SERVICE, "IMAP", 4)) {
		EXPECT = "* OK";
		QUIT = "a1 LOGOUT\r\n";
		PORT = 143;
	}
#ifdef HAVE_SSL
	else if (!strncmp(SERVICE, "SIMAP", 5)) {
		EXPECT = "* OK";
		QUIT = "a1 LOGOUT\r\n";
		flags |= FLAG_SSL;
		PORT = 993;
	}
	else if (!strncmp(SERVICE, "SPOP", 4)) {
		EXPECT = "+OK";
		QUIT = "QUIT\r\n";
		flags |= FLAG_SSL;
		PORT = 995;
	}
	else if (!strncmp(SERVICE, "SSMTP", 5)) {
		EXPECT = "220";
		QUIT = "QUIT\r\n";
		flags |= FLAG_SSL;
		PORT = 465;
	}
	else if (!strncmp(SERVICE, "JABBER", 6)) {
		SEND = "<stream:stream to=\'host\' xmlns=\'jabber:client\' xmlns:stream=\'http://etherx.jabber.org/streams\'>\n";
		EXPECT = "<?xml version=\'1.0\'?><stream:stream xmlns=\'jabber:client\' xmlns:stream=\'http://etherx.jabber.org/streams\'";
		QUIT = "</stream:stream>\n";
		flags |= FLAG_HIDE_OUTPUT;
		PORT = 5222;
	}
	else if (!strncmp (SERVICE, "NNTPS", 5)) {
		server_expect_count = 2;
		server_expect[0] = "200";
		server_expect[1] = "201";
		QUIT = "QUIT\r\n";
		flags |= FLAG_SSL;
		PORT = 563;
	}
#endif
	else if (!strncmp (SERVICE, "NNTP", 4)) {
		server_expect_count = 2;
		server_expect = malloc(sizeof(char *) * server_expect_count);
		server_expect[0] = strdup("200");
		server_expect[1] = strdup("201");
		QUIT = "QUIT\r\n";
		PORT = 119;
	}
	else if (!strncmp(SERVICE, "CLAMD", 5)) {
		SEND = "PING";
		EXPECT = "PONG";
		QUIT = NULL;
		PORT = 3310;
	}
	/* fallthrough check, so it's supposed to use reverse matching */
	else if (strcmp (SERVICE, "TCP"))
		usage (_("CRITICAL - Generic check_tcp called with unknown service\n"));

	server_address = "127.0.0.1";
	server_port = PORT;
	server_send = SEND;
	server_quit = QUIT;
	status = NULL;

	/* Parse extra opts if any */
	argv=np_extra_opts (&argc, argv, progname);

	if (process_arguments (argc, argv) == ERROR)
		usage4 (_("Could not parse arguments"));

	if(flags & FLAG_VERBOSE) {
		printf("Using service %s\n", SERVICE);
		printf("Port: %d\n", server_port);
		printf("flags: 0x%x\n", (int)flags);
	}

	if(EXPECT && !server_expect_count)
		server_expect_count++;

	if(PROTOCOL==IPPROTO_UDP && !(server_expect_count && server_send)){
		usage(_("With UDP checks, a send/expect string must be specified."));
	}

	/* set up the timer */
	signal (SIGALRM, socket_timeout_alarm_handler);
	alarm (socket_timeout);

	/* try to connect to the host at the given port number */
	gettimeofday (&tv, NULL);

	result = np_net_connect (server_address, server_port, &sd, PROTOCOL);
	if (result == STATE_CRITICAL) return STATE_CRITICAL;

#ifdef HAVE_SSL
	if (flags & FLAG_SSL){
		result = np_net_ssl_init(sd);
		if (result == STATE_OK && check_cert == TRUE) {
			result = np_net_ssl_check_cert(days_till_exp);
		}
	}
	if(result != STATE_OK || check_cert == TRUE){
		np_net_ssl_cleanup();
		if(sd) close(sd);
		return result;
	}
#endif /* HAVE_SSL */

	if (server_send != NULL) {		/* Something to send? */
		my_send(server_send, strlen(server_send));
	}

	if (delay > 0) {
		tv.tv_sec += delay;
		sleep (delay);
	}

	if(flags & FLAG_VERBOSE) {
		if (server_send) {
			printf("Send string: %s\n", server_send);
		}
		if (server_quit) {
			printf("Quit string: %s\n", server_quit);
		}
		printf("server_expect_count: %d\n", (int)server_expect_count);
		for(i = 0; i < server_expect_count; i++)
			printf("\t%d: %s\n", i, server_expect[i]);
	}

	/* if(len) later on, we know we have a non-NULL response */
	len = 0;
	if (server_expect_count) {

		/* watch for the expect string */
		while ((i = my_recv(buffer, sizeof(buffer))) > 0) {
			status = realloc(status, len + i + 1);
			memcpy(&status[len], buffer, i);
			len += i;

			/* stop reading if user-forced or data-starved */
			if(i < sizeof(buffer) || (maxbytes && len >= maxbytes))
				break;

			if (maxbytes && len >= maxbytes)
				break;
		}

		/* no data when expected, so return critical */
		if (len == 0)
			die (STATE_CRITICAL, _("No data received from host\n"));

		/* force null-termination and strip whitespace from end of output */
		status[len--] = '\0';
		/* print raw output if we're debugging */
		if(flags & FLAG_VERBOSE)
			printf("received %d bytes from host\n#-raw-recv-------#\n%s\n#-raw-recv-------#\n",
			       (int)len + 1, status);
		while(isspace(status[len])) status[len--] = '\0';

		match = np_expect_match(status,
                                server_expect,
                                server_expect_count,
                                (flags & FLAG_MATCH_ALL ? TRUE : FALSE),
                                (flags & FLAG_EXACT_MATCH ? TRUE : FALSE),
                                (flags & FLAG_VERBOSE ? TRUE : FALSE));
	}

	if (server_quit != NULL) {
		my_send(server_quit, strlen(server_quit));
	}
#ifdef HAVE_SSL
	np_net_ssl_cleanup();
#endif
	if (sd) close (sd);

	microsec = deltime (tv);
	elapsed_time = (double)microsec / 1.0e6;

	if (flags & FLAG_TIME_CRIT && elapsed_time > critical_time)
		result = STATE_CRITICAL;
	else if (flags & FLAG_TIME_WARN && elapsed_time > warning_time)
		result = STATE_WARNING;

	/* did we get the response we hoped? */
	if(match == FALSE && result != STATE_CRITICAL)
		result = expect_mismatch_state;

	/* reset the alarm */
	alarm (0);

	/* this is a bit stupid, because we don't want to print the
	 * response time (which can look ok to the user) if we didn't get
	 * the response we were looking for. if-else */
	printf("%s %s - ", SERVICE, state_text(result));

	if(match == FALSE && len && !(flags & FLAG_HIDE_OUTPUT))
		printf("Unexpected response from host/socket: %s", status);
	else {
		if(match == FALSE)
			printf("Unexpected response from host/socket on ");
		else
			printf("%.3f second response time on ", elapsed_time);
		if(server_address[0] != '/')
			printf("port %d", server_port);
		else
			printf("socket %s", server_address);
	}

	if (match != FALSE && !(flags & FLAG_HIDE_OUTPUT) && len)
		printf (" [%s]", status);

	/* perf-data doesn't apply when server doesn't talk properly,
	 * so print all zeroes on warn and crit. Use fperfdata since
	 * localisation settings can make different outputs */
	if(match == FALSE)
		printf ("|%s",
				fperfdata ("time", elapsed_time, "s",
				(flags & FLAG_TIME_WARN ? TRUE : FALSE), 0,
				(flags & FLAG_TIME_CRIT ? TRUE : FALSE), 0,
				TRUE, 0,
				TRUE, socket_timeout)
			);
	else
		printf("|%s",
				fperfdata ("time", elapsed_time, "s",
				(flags & FLAG_TIME_WARN ? TRUE : FALSE), warning_time,
				(flags & FLAG_TIME_CRIT ? TRUE : FALSE), critical_time,
				TRUE, 0,
				TRUE, socket_timeout)
			);

	putchar('\n');
	return result;
}