summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKe Zhao <kzhao@redhat.com>2019-03-06 13:23:24 -0500
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2019-03-16 19:56:48 +0100
commit3d123d737412d952148b117c1546367bcbec8af5 (patch)
tree64c0da672b00fd470d635444e6ad9754a547385b
parent9dca575622586e4c94ced9e453ae0a91f346d711 (diff)
downloadgnutls-3d123d737412d952148b117c1546367bcbec8af5.tar.gz
gnutls-cli: Add option "--logfile" to redirect information message output
First, add an option "--logfile" so user could choose a specific file to store all the informational messages. In some cases, informational messages may cause unexpected result if the output is standard output. With this option, user could redirect these messages to a specific file. This will be helpful in testing and tracking. Second, replace printf() function with log_msg() function This log_msg() function is used when "--logfile" is enabled. Third, add a functionality test for "--logfile" option Add a test script to test if "--logfile" option works as it should be. Signed-off-by: Ke Zhao <kzhao@redhat.com>
-rw-r--r--src/cli-args.def7
-rw-r--r--src/cli.c106
-rw-r--r--src/common.c275
-rw-r--r--src/common.h3
-rw-r--r--src/socket.c27
-rw-r--r--tests/Makefile.am2
-rwxr-xr-xtests/logfile-option.sh113
7 files changed, 346 insertions, 187 deletions
diff --git a/src/cli-args.def b/src/cli-args.def
index 1e06e12d3a..518a97466e 100644
--- a/src/cli-args.def
+++ b/src/cli-args.def
@@ -416,6 +416,13 @@ flag = {
doc = "";
};
+flag = {
+ name = logfile;
+ arg-type = string;
+ descrip = "Redirect informational messages to a specific file.";
+ doc = "";
+};
+
doc-section = {
ds-type = 'SEE ALSO'; // or anything else
ds-format = 'texi'; // or texi or mdoc format
diff --git a/src/cli.c b/src/cli.c
index 87191aa448..a770c74bcc 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -363,21 +363,21 @@ static int cert_verify_callback(gnutls_session_t session)
if (ca_verify) {
rc = cert_verify(session, host, GNUTLS_KP_TLS_WWW_SERVER);
if (rc == 0) {
- printf
- ("*** PKI verification of server certificate failed...\n");
+ log_msg
+ (stdout, "*** PKI verification of server certificate failed...\n");
if (!insecure && !ssh)
return -1;
} else if (ENABLED_OPT(OCSP) && gnutls_ocsp_status_request_is_checked(session, 0) == 0) { /* off-line verification succeeded. Try OCSP */
rc = cert_verify_ocsp(session);
if (rc == -1) {
- printf
- ("*** Verifying (with OCSP) server certificate chain failed...\n");
+ log_msg
+ (stdout, "*** Verifying (with OCSP) server certificate chain failed...\n");
if (!insecure && !ssh)
return -1;
} else if (rc == 0)
- printf("*** OCSP: nothing to check.\n");
+ log_msg(stdout, "*** OCSP: nothing to check.\n");
else
- printf("*** OCSP: verified %d certificate(s).\n", rc);
+ log_msg(stdout, "*** OCSP: verified %d certificate(s).\n", rc);
}
}
@@ -518,10 +518,10 @@ cert_callback(gnutls_session_t session,
/* Print the server's trusted CAs
*/
if (nreqs > 0)
- printf("- Server's trusted authorities:\n");
+ log_msg(stdout, "- Server's trusted authorities:\n");
else
- printf
- ("- Server did not send us any trusted authorities names.\n");
+ log_msg
+ (stdout, "- Server did not send us any trusted authorities names.\n");
/* print the names (if any) */
for (i = 0; i < nreqs; i++) {
@@ -530,8 +530,8 @@ cert_callback(gnutls_session_t session,
gnutls_x509_rdn_get(&req_ca_rdn[i], issuer_dn,
&len);
if (ret >= 0) {
- printf(" [%d]: ", i);
- printf("%s\n", issuer_dn);
+ log_msg(stdout, " [%d]: ", i);
+ log_msg(stdout, "%s\n", issuer_dn);
}
}
}
@@ -550,8 +550,8 @@ cert_callback(gnutls_session_t session,
if (x509_key != NULL) {
*pkey = x509_key;
} else {
- printf
- ("- Could not find a suitable key to send to server\n");
+ log_msg
+ (stdout, "- Could not find a suitable key to send to server\n");
return -1;
}
@@ -560,7 +560,7 @@ cert_callback(gnutls_session_t session,
}
}
- printf("- Successfully sent %u certificate(s) to server.\n",
+ log_msg(stdout, "- Successfully sent %u certificate(s) to server.\n",
*pcert_length);
return 0;
@@ -717,7 +717,7 @@ static int handle_error(socket_st * hd, int err)
str = gnutls_alert_get_name(alert);
if (str == NULL)
str = str_unknown;
- printf("*** Received alert [%d]: %s\n", alert, str);
+ log_msg(stdout, "*** Received alert [%d]: %s\n", alert, str);
}
check_server_cmd(hd, err);
@@ -813,7 +813,7 @@ static int try_rehandshake(socket_st * hd)
gnutls_perror(ret);
return ret;
} else {
- printf("- ReHandshake was completed\n");
+ log_msg(stdout, "- ReHandshake was completed\n");
return 0;
}
}
@@ -830,7 +830,7 @@ static int try_rekey(socket_st * hd, unsigned peer)
fprintf(stderr, "*** Rekey has failed: %s\n", gnutls_strerror(ret));
return ret;
} else {
- printf("- Rekey was completed\n");
+ log_msg(stdout, "- Rekey was completed\n");
return 0;
}
}
@@ -854,13 +854,13 @@ static int try_resume(socket_st * hd)
hd->rdata.data = NULL;
}
- printf("- Disconnecting\n");
+ log_msg(stdout, "- Disconnecting\n");
socket_bye(hd, 1);
canonicalize_host(hostname, service, sizeof(service));
- printf
- ("\n\n- Connecting again- trying to resume previous session\n");
+ log_msg
+ (stdout, "\n\n- Connecting again- trying to resume previous session\n");
if (HAVE_OPT(STARTTLS_PROTO))
socket_flags |= SOCKET_FLAG_STARTTLS;
else if (fastopen)
@@ -886,9 +886,9 @@ static int try_resume(socket_st * hd)
socket_open3(hd, hostname, service, OPT_ARG(STARTTLS_PROTO),
socket_flags, CONNECT_MSG, &rdata, &edata);
- printf("- Resume Handshake was completed\n");
+ log_msg(stdout, "- Resume Handshake was completed\n");
if (gnutls_session_is_resumed(hd->session) != 0)
- printf("*** This is a resumed session\n");
+ log_msg(stdout, "*** This is a resumed session\n");
return 0;
}
@@ -1128,12 +1128,22 @@ int main(int argc, char **argv)
int socket_flags = SOCKET_FLAG_DONT_PRINT_ERRORS;
FILE *server_fp = NULL;
FILE *client_fp = NULL;
+ FILE *logfile = NULL;
#ifndef _WIN32
struct sigaction new_action;
#endif
cmd_parser(argc, argv);
+ if (HAVE_OPT(LOGFILE)) {
+ logfile = fopen(OPT_ARG(LOGFILE), "w+");
+ if (!logfile) {
+ log_msg(stderr, "Unable to open '%s'!\n", OPT_ARG(LOGFILE));
+ exit(1);
+ }
+ log_set(logfile);
+ }
+
gnutls_global_set_log_function(tls_log_func);
gnutls_global_set_log_level(OPT_VALUE_DEBUG);
@@ -1178,7 +1188,7 @@ int main(int argc, char **argv)
hd.verbose = verbose;
if (hd.secure) {
- printf("- Handshake was completed\n");
+ log_msg(stdout, "- Handshake was completed\n");
if (resume != 0)
if (try_resume(&hd)) {
@@ -1191,7 +1201,7 @@ int main(int argc, char **argv)
/* Warning! Do not touch this text string, it is used by external
programs to search for when gnutls-cli has reached this point. */
- printf("\n- Simple Client Mode:\n\n");
+ log_msg(stdout, "\n- Simple Client Mode:\n\n");
if (rehandshake)
if (try_rehandshake(&hd)) {
@@ -1246,8 +1256,8 @@ int main(int argc, char **argv)
ret = socket_recv(&hd, buffer, MAX_BUF);
if (ret == 0 || (ret == GNUTLS_E_PREMATURE_TERMINATION && user_term)) {
- printf
- ("- Peer has closed the GnuTLS connection\n");
+ log_msg
+ (stdout, "- Peer has closed the GnuTLS connection\n");
break;
} else if (handle_error(&hd, ret) < 0) {
fprintf(stderr,
@@ -1256,7 +1266,7 @@ int main(int argc, char **argv)
break;
} else if (ret > 0) {
if (verbose != 0)
- printf("- Received[%d]: ", ret);
+ log_msg(stdout, "- Received[%d]: ", ret);
for (ii = 0; ii < ret; ii++) {
fputc(buffer[ii], stdout);
}
@@ -1346,7 +1356,7 @@ int main(int argc, char **argv)
if (ret > 0) {
if (verbose != 0)
- printf("- Sent: %d bytes\n", ret);
+ log_msg(stdout, "- Sent: %d bytes\n", ret);
} else
handle_error(&hd, ret);
@@ -1364,6 +1374,10 @@ int main(int argc, char **argv)
cleanup:
socket_bye(&hd, 0);
+ if (logfile) {
+ fclose(logfile);
+ logfile = NULL;
+ }
#ifdef ENABLE_SRP
if (srp_cred)
@@ -1392,21 +1406,21 @@ void print_priority_list(void)
const char *str;
unsigned int lineb = 0;
- printf("Priority strings in GnuTLS %s:\n", gnutls_check_version(NULL));
+ log_msg(stdout, "Priority strings in GnuTLS %s:\n", gnutls_check_version(NULL));
fputs("\t", stdout);
for (idx=0;;idx++) {
str = gnutls_priority_string_list(idx, GNUTLS_PRIORITY_LIST_INIT_KEYWORDS);
if (str == NULL)
break;
- lineb += printf("%s ", str);
+ lineb += log_msg(stdout, "%s ", str);
if (lineb > 64) {
lineb = 0;
- printf("\n\t");
+ log_msg(stdout, "\n\t");
}
}
- printf("\n\nSpecial strings:\n");
+ log_msg(stdout, "\n\nSpecial strings:\n");
lineb = 0;
fputs("\t", stdout);
for (idx=0;;idx++) {
@@ -1415,13 +1429,13 @@ void print_priority_list(void)
break;
if (str[0] == 0)
continue;
- lineb += printf("%%%s ", str);
+ lineb += log_msg(stdout, "%%%s ", str);
if (lineb > 64) {
lineb = 0;
- printf("\n\t");
+ log_msg(stdout, "\n\t");
}
}
- printf("\n");
+ log_msg(stdout, "\n");
return;
}
@@ -1593,15 +1607,15 @@ static void check_server_cmd(socket_st * socket, int ret)
* the server thinks we ignored his request.
* This is a bad design of this client.
*/
- printf("*** Received rehandshake request\n");
+ log_msg(stdout, "*** Received rehandshake request\n");
/* gnutls_alert_send( session, GNUTLS_AL_WARNING, GNUTLS_A_NO_RENEGOTIATION); */
ret = do_handshake(socket);
if (ret == 0) {
- printf("*** Rehandshake was performed.\n");
+ log_msg(stdout, "*** Rehandshake was performed.\n");
} else {
- printf("*** Rehandshake Failed: %s\n", gnutls_strerror(ret));
+ log_msg(stdout, "*** Rehandshake Failed: %s\n", gnutls_strerror(ret));
}
} else if (ret == GNUTLS_E_REAUTH_REQUEST) {
do {
@@ -1609,9 +1623,9 @@ static void check_server_cmd(socket_st * socket, int ret)
} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
if (ret == 0) {
- printf("*** Re-auth was performed.\n");
+ log_msg(stdout, "*** Re-auth was performed.\n");
} else {
- printf("*** Re-auth failed: %s\n", gnutls_strerror(ret));
+ log_msg(stdout, "*** Re-auth failed: %s\n", gnutls_strerror(ret));
}
}
}
@@ -1678,11 +1692,11 @@ psk_callback(gnutls_session_t session, char **username,
size_t res_size;
gnutls_datum_t tmp;
- printf("- PSK client callback. ");
+ log_msg(stdout, "- PSK client callback. ");
if (hint)
- printf("PSK hint '%s'\n", hint);
+ log_msg(stdout, "PSK hint '%s'\n", hint);
else
- printf("No PSK hint\n");
+ log_msg(stdout, "No PSK hint\n");
if (HAVE_OPT(PSKUSERNAME))
*username = gnutls_strdup(OPT_ARG(PSKUSERNAME));
@@ -1690,7 +1704,7 @@ psk_callback(gnutls_session_t session, char **username,
char *p = NULL;
size_t n;
- printf("Enter PSK identity: ");
+ log_msg(stdout, "Enter PSK identity: ");
fflush(stdout);
ret = getline(&p, &n, stdin);
@@ -1805,7 +1819,7 @@ static void init_global_tls_stuff(void)
fprintf(stderr, "Error setting the x509 trust file: %s\n", gnutls_strerror(ret));
exit(1);
} else {
- printf("Processed %d CA certificate(s).\n", ret);
+ log_msg(stdout, "Processed %d CA certificate(s).\n", ret);
}
if (x509_crlfile != NULL) {
@@ -1818,7 +1832,7 @@ static void init_global_tls_stuff(void)
"Error setting the x509 CRL file: %s\n", gnutls_strerror(ret));
exit(1);
} else {
- printf("Processed %d CRL(s).\n", ret);
+ log_msg(stdout, "Processed %d CRL(s).\n", ret);
}
}
diff --git a/src/common.c b/src/common.c
index b1cb519e2f..f0fdf9e00d 100644
--- a/src/common.c
+++ b/src/common.c
@@ -52,6 +52,7 @@
const char str_unknown[] = "(unknown)";
+static FILE *logfile = NULL;
/* Hex encodes the given data adding a semicolon between hex bytes.
*/
const char *raw_to_string(const unsigned char *raw, size_t raw_size)
@@ -145,7 +146,7 @@ static void print_x509_info_compact(gnutls_session_t session, int print_crt_stat
ret = gnutls_x509_crt_print(crt, GNUTLS_CRT_PRINT_COMPACT, &cinfo);
if (ret == 0) {
- printf("- X.509 cert: %s\n", cinfo.data);
+ log_msg(stdout, "- X.509 cert: %s\n", cinfo.data);
gnutls_free(cinfo.data);
}
@@ -249,12 +250,12 @@ int cert_verify(gnutls_session_t session, const char *hostname, const char *purp
rc = gnutls_certificate_verify_peers(session, data, elements, &status);
if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND) {
- printf("- Peer did not send any certificate.\n");
+ log_msg(stdout, "- Peer did not send any certificate.\n");
return 0;
}
if (rc < 0) {
- printf("- Could not verify certificate (err: %s)\n",
+ log_msg(stdout, "- Could not verify certificate (err: %s)\n",
gnutls_strerror(rc));
return 0;
}
@@ -263,12 +264,12 @@ int cert_verify(gnutls_session_t session, const char *hostname, const char *purp
rc = gnutls_certificate_verification_status_print(status, type,
&out, 0);
if (rc < 0) {
- printf("- Could not print verification flags (err: %s)\n",
+ log_msg(stdout, "- Could not print verification flags (err: %s)\n",
gnutls_strerror(rc));
return 0;
}
- printf("- Status: %s\n", out.data);
+ log_msg(stdout, "- Status: %s\n", out.data);
gnutls_free(out.data);
@@ -298,12 +299,12 @@ print_dh_info(gnutls_session_t session, const char *str, int print)
return;
}
- printf("- %sDiffie-Hellman parameters\n", str);
- printf(" - Using prime: %d bits\n",
+ log_msg(stdout, "- %sDiffie-Hellman parameters\n", str);
+ log_msg(stdout, " - Using prime: %d bits\n",
gnutls_dh_get_prime_bits(session));
- printf(" - Secret key: %d bits\n",
+ log_msg(stdout, " - Secret key: %d bits\n",
gnutls_dh_get_secret_bits(session));
- printf(" - Peer's public key: %d bits\n",
+ log_msg(stdout, " - Peer's public key: %d bits\n",
gnutls_dh_get_peers_public_bits(session));
ret = gnutls_dh_get_group(session, &raw_gen, &raw_prime);
@@ -354,7 +355,7 @@ print_dh_info(gnutls_session_t session, const char *str, int print)
goto out;
}
- printf(" - PKCS#3 format:\n\n%.*s\n",
+ log_msg(stdout, " - PKCS#3 format:\n\n%.*s\n",
(int) params_data_size, params_data);
out:
@@ -372,12 +373,12 @@ static void print_ecdh_info(gnutls_session_t session, const char *str, int print
if (!print)
return;
- printf("- %sEC Diffie-Hellman parameters\n", str);
+ log_msg(stdout, "- %sEC Diffie-Hellman parameters\n", str);
curve = gnutls_ecc_curve_get(session);
- printf(" - Using curve: %s\n", gnutls_ecc_curve_get_name(curve));
- printf(" - Curve size: %d bits\n",
+ log_msg(stdout, " - Using curve: %s\n", gnutls_ecc_curve_get_name(curve));
+ log_msg(stdout, " - Curve size: %d bits\n",
gnutls_ecc_curve_get_size(curve) * 8);
}
@@ -396,13 +397,13 @@ int print_info(gnutls_session_t session, int verbose, int flags)
int rc;
desc = gnutls_session_get_desc(session);
- printf("- Description: %s\n", desc);
+ log_msg(stdout, "- Description: %s\n", desc);
gnutls_free(desc);
/* print session ID */
gnutls_session_get_id(session, session_id, &session_id_size);
if (session_id_size > 0) {
- printf("- Session ID: %s\n",
+ log_msg(stdout, "- Session ID: %s\n",
raw_to_string(session_id, session_id_size));
}
@@ -426,7 +427,7 @@ int print_info(gnutls_session_t session, int verbose, int flags)
* side.
*/
if (gnutls_srp_server_get_username(session) != NULL)
- printf("- SRP authentication. Connected as '%s'\n",
+ log_msg(stdout, "- SRP authentication. Connected as '%s'\n",
gnutls_srp_server_get_username(session));
break;
#endif
@@ -435,12 +436,12 @@ int print_info(gnutls_session_t session, int verbose, int flags)
/* This returns NULL in server side.
*/
if (gnutls_psk_client_get_hint(session) != NULL)
- printf("- PSK authentication. PSK hint '%s'\n",
+ log_msg(stdout, "- PSK authentication. PSK hint '%s'\n",
gnutls_psk_client_get_hint(session));
/* This returns NULL in client side.
*/
if (gnutls_psk_server_get_username(session) != NULL)
- printf("- PSK authentication. Connected as '%s'\n",
+ log_msg(stdout, "- PSK authentication. Connected as '%s'\n",
gnutls_psk_server_get_username(session));
if (kx == GNUTLS_KX_DHE_PSK)
print_dh_info(session, "Ephemeral ", verbose);
@@ -449,7 +450,7 @@ int print_info(gnutls_session_t session, int verbose, int flags)
break;
#endif
case GNUTLS_CRD_IA:
- printf("- TLS/IA authentication\n");
+ log_msg(stdout, "- TLS/IA authentication\n");
break;
case GNUTLS_CRD_CERTIFICATE:
{
@@ -460,13 +461,13 @@ int print_info(gnutls_session_t session, int verbose, int flags)
/* This fails in client side */
if (gnutls_server_name_get
(session, dns, &dns_size, &type, 0) == 0) {
- printf("- Given server name[%d]: %s\n",
+ log_msg(stdout, "- Given server name[%d]: %s\n",
type, dns);
}
}
if ((flags & P_WAIT_FOR_CERT) && gnutls_certificate_get_ours(session) == 0)
- printf("- No certificate was sent to peer\n");
+ log_msg(stdout, "- No certificate was sent to peer\n");
if (flags& P_PRINT_CERT)
print_cert_info(session, verbose, (flags&P_PRINT_CERT));
@@ -483,18 +484,18 @@ int print_info(gnutls_session_t session, int verbose, int flags)
version = gnutls_protocol_get_version(session);
tmp =
SU(gnutls_protocol_get_name(version));
- printf("- Version: %s\n", tmp);
+ log_msg(stdout, "- Version: %s\n", tmp);
if (version < GNUTLS_TLS1_3) {
tmp = SU(gnutls_kx_get_name(kx));
- printf("- Key Exchange: %s\n", tmp);
+ log_msg(stdout, "- Key Exchange: %s\n", tmp);
}
if (gnutls_sign_algorithm_get(session) != GNUTLS_SIGN_UNKNOWN) {
tmp =
SU(gnutls_sign_get_name
(gnutls_sign_algorithm_get(session)));
- printf("- Server Signature: %s\n", tmp);
+ log_msg(stdout, "- Server Signature: %s\n", tmp);
}
if (gnutls_sign_algorithm_get_client(session) !=
@@ -502,41 +503,41 @@ int print_info(gnutls_session_t session, int verbose, int flags)
tmp =
SU(gnutls_sign_get_name
(gnutls_sign_algorithm_get_client(session)));
- printf("- Client Signature: %s\n", tmp);
+ log_msg(stdout, "- Client Signature: %s\n", tmp);
}
tmp = SU(gnutls_cipher_get_name(gnutls_cipher_get(session)));
- printf("- Cipher: %s\n", tmp);
+ log_msg(stdout, "- Cipher: %s\n", tmp);
tmp = SU(gnutls_mac_get_name(gnutls_mac_get(session)));
- printf("- MAC: %s\n", tmp);
+ log_msg(stdout, "- MAC: %s\n", tmp);
}
- printf("- Options:");
+ log_msg(stdout, "- Options:");
if (gnutls_session_ext_master_secret_status(session)!=0)
- printf(" extended master secret,");
+ log_msg(stdout, " extended master secret,");
if (gnutls_safe_renegotiation_status(session)!=0)
- printf(" safe renegotiation,");
+ log_msg(stdout, " safe renegotiation,");
if (gnutls_session_etm_status(session)!=0)
- printf(" EtM,");
+ log_msg(stdout, " EtM,");
#ifdef ENABLE_OCSP
if (gnutls_ocsp_status_request_is_checked(session, GNUTLS_OCSP_SR_IS_AVAIL)!=0) {
- printf(" OCSP status request%s,", gnutls_ocsp_status_request_is_checked(session,0)!=0?"":"[ignored]");
+ log_msg(stdout, " OCSP status request%s,", gnutls_ocsp_status_request_is_checked(session,0)!=0?"":"[ignored]");
}
#endif
- printf("\n");
+ log_msg(stdout, "\n");
#ifdef ENABLE_DTLS_SRTP
rc = gnutls_srtp_get_selected_profile(session, &srtp_profile);
if (rc == 0)
- printf("- SRTP profile: %s\n",
+ log_msg(stdout, "- SRTP profile: %s\n",
gnutls_srtp_get_profile_name(srtp_profile));
#endif
#ifdef ENABLE_ALPN
rc = gnutls_alpn_get_selected_protocol(session, &p);
if (rc == 0)
- printf("- Application protocol: %.*s\n", p.size, p.data);
+ log_msg(stdout, "- Application protocol: %.*s\n", p.size, p.data);
#endif
if (verbose) {
@@ -551,10 +552,10 @@ int print_info(gnutls_session_t session, int verbose, int flags)
else {
size_t i;
- printf("- Channel binding 'tls-unique': ");
+ log_msg(stdout, "- Channel binding 'tls-unique': ");
for (i = 0; i < cb.size; i++)
- printf("%02x", cb.data[i]);
- printf("\n");
+ log_msg(stdout, "%02x", cb.data[i]);
+ log_msg(stdout, "\n");
gnutls_free(cb.data);
}
}
@@ -579,7 +580,7 @@ void print_cert_info2(gnutls_session_t session, int verbose, FILE *out, int prin
flag = GNUTLS_CRT_PRINT_COMPACT;
if (gnutls_certificate_client_get_request_status(session) != 0) {
- printf("- Server has requested a certificate.\n");
+ log_msg(stdout, "- Server has requested a certificate.\n");
print_crt_status = 1;
}
@@ -597,7 +598,7 @@ void print_cert_info_compact(gnutls_session_t session)
int verbose = 0;
if (gnutls_certificate_client_get_request_status(session) != 0) {
- printf("- Server has requested a certificate.\n");
+ log_msg(stdout, "- Server has requested a certificate.\n");
verbose = 1;
}
@@ -626,7 +627,7 @@ void print_list(const char *priorities, int verbose)
const unsigned int *list;
if (priorities != NULL) {
- printf("Cipher suites for %s\n", priorities);
+ log_msg(stdout, "Cipher suites for %s\n", priorities);
ret = gnutls_priority_init(&pcache, priorities, &err);
if (ret < 0) {
@@ -652,13 +653,13 @@ void print_list(const char *priorities, int verbose)
NULL, &version);
if (name != NULL)
- printf("%-50s\t0x%02x, 0x%02x\t%s\n",
+ log_msg(stdout, "%-50s\t0x%02x, 0x%02x\t%s\n",
name, (unsigned char) id[0],
(unsigned char) id[1],
gnutls_protocol_get_name(version));
}
- printf("\n");
+ log_msg(stdout, "\n");
#if 0
{
ret =
@@ -666,17 +667,17 @@ void print_list(const char *priorities, int verbose)
&list,
GNUTLS_CTYPE_CLIENT);
- printf("Certificate types: ");
+ log_msg(stdout, "Certificate types: ");
if (ret == 0)
- printf("none\n");
+ log_msg(stdout, "none\n");
for (i = 0; i < (unsigned) ret; i++) {
- printf("CTYPE-%s",
+ log_msg(stdout, "CTYPE-%s",
gnutls_certificate_type_get_name
(list[i]));
if (i + 1 != (unsigned) ret)
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
#endif
@@ -684,64 +685,64 @@ void print_list(const char *priorities, int verbose)
{
ret = gnutls_priority_protocol_list(pcache, &list);
- printf("Protocols: ");
+ log_msg(stdout, "Protocols: ");
if (ret == 0)
- printf("none\n");
+ log_msg(stdout, "none\n");
for (i = 0; i < (unsigned) ret; i++) {
- printf("VERS-%s",
+ log_msg(stdout, "VERS-%s",
gnutls_protocol_get_name(list[i]));
if (i + 1 != (unsigned) ret)
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
{
ret = gnutls_priority_cipher_list(pcache, &list);
- printf("Ciphers: ");
+ log_msg(stdout, "Ciphers: ");
if (ret == 0)
- printf("none\n");
+ log_msg(stdout, "none\n");
for (i = 0; i < (unsigned) ret; i++) {
- printf("%s",
+ log_msg(stdout, "%s",
gnutls_cipher_get_name(list[i]));
if (i + 1 != (unsigned) ret)
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
{
ret = gnutls_priority_mac_list(pcache, &list);
- printf("MACs: ");
+ log_msg(stdout, "MACs: ");
if (ret == 0)
- printf("none\n");
+ log_msg(stdout, "none\n");
for (i = 0; i < (unsigned) ret; i++) {
- printf("%s",
+ log_msg(stdout, "%s",
gnutls_mac_get_name(list[i]));
if (i + 1 != (unsigned) ret)
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
{
ret = gnutls_priority_kx_list(pcache, &list);
- printf("Key Exchange Algorithms: ");
+ log_msg(stdout, "Key Exchange Algorithms: ");
if (ret == 0)
- printf("none\n");
+ log_msg(stdout, "none\n");
for (i = 0; i < (unsigned) ret; i++) {
- printf("%s",
+ log_msg(stdout, "%s",
gnutls_kx_get_name(list[i]));
if (i + 1 != (unsigned) ret)
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
@@ -749,33 +750,33 @@ void print_list(const char *priorities, int verbose)
ret =
gnutls_priority_group_list(pcache, &list);
- printf("Groups: ");
+ log_msg(stdout, "Groups: ");
if (ret == 0)
- printf("none\n");
+ log_msg(stdout, "none\n");
for (i = 0; i < (unsigned) ret; i++) {
- printf("GROUP-%s",
+ log_msg(stdout, "GROUP-%s",
gnutls_group_get_name(list[i]));
if (i + 1 != (unsigned) ret)
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
{
ret = gnutls_priority_sign_list(pcache, &list);
- printf("PK-signatures: ");
+ log_msg(stdout, "PK-signatures: ");
if (ret == 0)
- printf("none\n");
+ log_msg(stdout, "none\n");
for (i = 0; i < (unsigned) ret; i++) {
- printf("SIGN-%s",
+ log_msg(stdout, "SIGN-%s",
gnutls_sign_algorithm_get_name(list
[i]));
if (i + 1 != (unsigned) ret)
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
@@ -783,99 +784,99 @@ void print_list(const char *priorities, int verbose)
return;
}
- printf("Cipher suites:\n");
+ log_msg(stdout, "Cipher suites:\n");
for (i = 0; (name = gnutls_cipher_suite_info
(i, id, &kx, &cipher, &mac, &version)); i++) {
- printf("%-50s\t0x%02x, 0x%02x\t%s\n",
+ log_msg(stdout, "%-50s\t0x%02x, 0x%02x\t%s\n",
name,
(unsigned char) id[0], (unsigned char) id[1],
gnutls_protocol_get_name(version));
if (verbose)
- printf
- ("\tKey exchange: %s\n\tCipher: %s\n\tMAC: %s\n\n",
+ log_msg
+ (stdout, "\tKey exchange: %s\n\tCipher: %s\n\tMAC: %s\n\n",
gnutls_kx_get_name(kx),
gnutls_cipher_get_name(cipher),
gnutls_mac_get_name(mac));
}
- printf("\n");
+ log_msg(stdout, "\n");
{
const gnutls_certificate_type_t *p =
gnutls_certificate_type_list();
- printf("Certificate types: ");
+ log_msg(stdout, "Certificate types: ");
for (; *p; p++) {
- printf("CTYPE-%s",
+ log_msg(stdout, "CTYPE-%s",
gnutls_certificate_type_get_name(*p));
if (*(p + 1))
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
{
const gnutls_protocol_t *p = gnutls_protocol_list();
- printf("Protocols: ");
+ log_msg(stdout, "Protocols: ");
for (; *p; p++) {
- printf("VERS-%s", gnutls_protocol_get_name(*p));
+ log_msg(stdout, "VERS-%s", gnutls_protocol_get_name(*p));
if (*(p + 1))
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
{
const gnutls_cipher_algorithm_t *p = gnutls_cipher_list();
- printf("Ciphers: ");
+ log_msg(stdout, "Ciphers: ");
for (; *p; p++) {
- printf("%s", gnutls_cipher_get_name(*p));
+ log_msg(stdout, "%s", gnutls_cipher_get_name(*p));
if (*(p + 1))
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
{
const gnutls_mac_algorithm_t *p = gnutls_mac_list();
- printf("MACs: ");
+ log_msg(stdout, "MACs: ");
for (; *p; p++) {
- printf("%s", gnutls_mac_get_name(*p));
+ log_msg(stdout, "%s", gnutls_mac_get_name(*p));
if (*(p + 1))
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
{
const gnutls_digest_algorithm_t *p = gnutls_digest_list();
- printf("Digests: ");
+ log_msg(stdout, "Digests: ");
for (; *p; p++) {
- printf("%s", gnutls_digest_get_name(*p));
+ log_msg(stdout, "%s", gnutls_digest_get_name(*p));
if (*(p + 1))
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
{
const gnutls_kx_algorithm_t *p = gnutls_kx_list();
- printf("Key exchange algorithms: ");
+ log_msg(stdout, "Key exchange algorithms: ");
for (; *p; p++) {
- printf("%s", gnutls_kx_get_name(*p));
+ log_msg(stdout, "%s", gnutls_kx_get_name(*p));
if (*(p + 1))
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
@@ -883,53 +884,53 @@ void print_list(const char *priorities, int verbose)
const gnutls_compression_method_t *p =
gnutls_compression_list();
- printf("Compression: ");
+ log_msg(stdout, "Compression: ");
for (; *p; p++) {
- printf("COMP-%s", gnutls_compression_get_name(*p));
+ log_msg(stdout, "COMP-%s", gnutls_compression_get_name(*p));
if (*(p + 1))
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
{
const gnutls_group_t *p = gnutls_group_list();
- printf("Groups: ");
+ log_msg(stdout, "Groups: ");
for (; *p; p++) {
- printf("GROUP-%s", gnutls_group_get_name(*p));
+ log_msg(stdout, "GROUP-%s", gnutls_group_get_name(*p));
if (*(p + 1))
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
{
const gnutls_pk_algorithm_t *p = gnutls_pk_list();
- printf("Public Key Systems: ");
+ log_msg(stdout, "Public Key Systems: ");
for (; *p; p++) {
- printf("%s", gnutls_pk_algorithm_get_name(*p));
+ log_msg(stdout, "%s", gnutls_pk_algorithm_get_name(*p));
if (*(p + 1))
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
{
const gnutls_sign_algorithm_t *p = gnutls_sign_list();
- printf("PK-signatures: ");
+ log_msg(stdout, "PK-signatures: ");
for (; *p; p++) {
- printf("SIGN-%s",
+ log_msg(stdout, "SIGN-%s",
gnutls_sign_algorithm_get_name(*p));
if (*(p + 1))
- printf(", ");
+ log_msg(stdout, ", ");
else
- printf("\n");
+ log_msg(stdout, "\n");
}
}
}
@@ -1069,16 +1070,16 @@ pin_callback(void *user, int attempt, const char *token_url,
if (flags & GNUTLS_PIN_FINAL_TRY) {
cache = 0;
- printf("*** This is the final try before locking!\n");
+ log_msg(stdout, "*** This is the final try before locking!\n");
}
if (flags & GNUTLS_PIN_COUNT_LOW) {
cache = 0;
- printf("*** Only few tries left before locking!\n");
+ log_msg(stdout, "*** Only few tries left before locking!\n");
}
if (flags & GNUTLS_PIN_WRONG) {
cache = 0;
- printf("*** Wrong PIN has been provided!\n");
+ log_msg(stdout, "*** Wrong PIN has been provided!\n");
}
if (cache > 0 && cached_url != NULL) {
@@ -1166,7 +1167,7 @@ token_callback(void *user, const char *label, const unsigned retry)
fprintf(stderr, "Could not find token %s\n", label);
return -1;
}
- printf("Please insert token '%s' in slot and press enter\n",
+ log_msg(stdout, "Please insert token '%s' in slot and press enter\n",
label);
if (fgets(buf, sizeof(buf), stdin) == NULL) {
fprintf(stderr, "error reading input\n");
@@ -1200,3 +1201,23 @@ void sockets_init(void)
signal(SIGPIPE, SIG_IGN);
#endif
}
+
+
+int log_msg(FILE *file, const char *message, ...)
+{
+ va_list args;
+ int rv;
+
+ va_start(args, message);
+
+ rv = vfprintf(logfile ? logfile : file, message, args);
+
+ va_end(args);
+
+ return rv;
+}
+
+void log_set(FILE *file)
+{
+ logfile = file;
+}
diff --git a/src/common.h b/src/common.h
index 2d2a69cc0f..1c2a0bc197 100644
--- a/src/common.h
+++ b/src/common.h
@@ -62,6 +62,9 @@ int print_info(gnutls_session_t state, int verbose, int flags);
void print_cert_info(gnutls_session_t, int flag, int print_cert);
void print_cert_info_compact(gnutls_session_t session);
+int log_msg(FILE *file, const char *message, ...) __attribute__((format(printf, 2, 3)));
+void log_set(FILE *file);
+
void print_cert_info2(gnutls_session_t, int flag, FILE *fp, int print_cert);
void print_list(const char *priorities, int verbose);
diff --git a/src/socket.c b/src/socket.c
index b65d585102..9ba784fa3a 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -37,6 +37,7 @@
#include <socket.h>
#include <c-ctype.h>
#include "sockets.h"
+#include "common.h"
#ifdef _WIN32
# undef endservent
@@ -226,7 +227,7 @@ socket_starttls(socket_st * socket)
if (strcasecmp(socket->app_proto, "smtp") == 0 || strcasecmp(socket->app_proto, "submission") == 0) {
if (socket->verbose)
- printf("Negotiating SMTP STARTTLS\n");
+ log_msg(stdout, "Negotiating SMTP STARTTLS\n");
wait_for_text(socket, "220 ", 4);
snprintf(buf, sizeof(buf), "EHLO %s\r\n", socket->hostname);
@@ -236,7 +237,7 @@ socket_starttls(socket_st * socket)
wait_for_text(socket, "220 ", 4);
} else if (strcasecmp(socket->app_proto, "imap") == 0 || strcasecmp(socket->app_proto, "imap2") == 0) {
if (socket->verbose)
- printf("Negotiating IMAP STARTTLS\n");
+ log_msg(stdout, "Negotiating IMAP STARTTLS\n");
send_line(socket, "a CAPABILITY\r\n");
wait_for_text(socket, "a OK", 4);
@@ -244,7 +245,7 @@ socket_starttls(socket_st * socket)
wait_for_text(socket, "a OK", 4);
} else if (strcasecmp(socket->app_proto, "xmpp") == 0) {
if (socket->verbose)
- printf("Negotiating XMPP STARTTLS\n");
+ log_msg(stdout, "Negotiating XMPP STARTTLS\n");
snprintf(buf, sizeof(buf), "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' to='%s' version='1.0'>\n", socket->hostname);
send_line(socket, buf);
@@ -253,13 +254,13 @@ socket_starttls(socket_st * socket)
wait_for_text(socket, "<proceed", 8);
} else if (strcasecmp(socket->app_proto, "ldap") == 0) {
if (socket->verbose)
- printf("Negotiating LDAP STARTTLS\n");
+ log_msg(stdout, "Negotiating LDAP STARTTLS\n");
#define LDAP_STR "\x30\x1d\x02\x01\x01\x77\x18\x80\x16\x31\x2e\x33\x2e\x36\x2e\x31\x2e\x34\x2e\x31\x2e\x31\x34\x36\x36\x2e\x32\x30\x30\x33\x37"
send(socket->fd, LDAP_STR, sizeof(LDAP_STR)-1, 0);
wait_for_text(socket, NULL, 0);
} else if (strcasecmp(socket->app_proto, "ftp") == 0 || strcasecmp(socket->app_proto, "ftps") == 0) {
if (socket->verbose)
- printf("Negotiating FTP STARTTLS\n");
+ log_msg(stdout, "Negotiating FTP STARTTLS\n");
send_line(socket, "FEAT\r\n");
wait_for_text(socket, "211 ", 4);
@@ -267,7 +268,7 @@ socket_starttls(socket_st * socket)
wait_for_text(socket, "234", 3);
} else if (strcasecmp(socket->app_proto, "lmtp") == 0) {
if (socket->verbose)
- printf("Negotiating LMTP STARTTLS\n");
+ log_msg(stdout, "Negotiating LMTP STARTTLS\n");
wait_for_text(socket, "220 ", 4);
snprintf(buf, sizeof(buf), "LHLO %s\r\n", socket->hostname);
@@ -277,28 +278,28 @@ socket_starttls(socket_st * socket)
wait_for_text(socket, "220 ", 4);
} else if (strcasecmp(socket->app_proto, "pop3") == 0) {
if (socket->verbose)
- printf("Negotiating POP3 STARTTLS\n");
+ log_msg(stdout, "Negotiating POP3 STARTTLS\n");
wait_for_text(socket, "+OK", 3);
send_line(socket, "STLS\r\n");
wait_for_text(socket, "+OK", 3);
} else if (strcasecmp(socket->app_proto, "nntp") == 0) {
if (socket->verbose)
- printf("Negotiating NNTP STARTTLS\n");
+ log_msg(stdout, "Negotiating NNTP STARTTLS\n");
wait_for_text(socket, "200 ", 4);
send_line(socket, "STARTTLS\r\n");
wait_for_text(socket, "382 ", 4);
} else if (strcasecmp(socket->app_proto, "sieve") == 0) {
if (socket->verbose)
- printf("Negotiating Sieve STARTTLS\n");
+ log_msg(stdout, "Negotiating Sieve STARTTLS\n");
wait_for_text(socket, "OK ", 3);
send_line(socket, "STARTTLS\r\n");
wait_for_text(socket, "OK ", 3);
} else if (strcasecmp(socket->app_proto, "postgres") == 0 || strcasecmp(socket->app_proto, "postgresql") == 0) {
if (socket->verbose)
- printf("Negotiating PostgreSQL STARTTLS\n");
+ log_msg(stdout, "Negotiating PostgreSQL STARTTLS\n");
#define POSTGRES_STR "\x00\x00\x00\x08\x04\xD2\x16\x2F"
send(socket->fd, POSTGRES_STR, sizeof(POSTGRES_STR)-1, 0);
@@ -494,7 +495,7 @@ socket_open2(socket_st * hd, const char *hostname, const char *service,
a_hostname = (char*)idna.data;
if (msg != NULL)
- printf("Resolving '%s:%s'...\n", a_hostname, service);
+ log_msg(stdout, "Resolving '%s:%s'...\n", a_hostname, service);
/* get server name */
memset(&hints, 0, sizeof(hints));
@@ -543,11 +544,11 @@ socket_open2(socket_st * hd, const char *hostname, const char *service,
hd->connect_addrlen = ptr->ai_addrlen;
if (msg)
- printf("%s '%s:%s' (TFO)...\n", msg, buffer, portname);
+ log_msg(stdout, "%s '%s:%s' (TFO)...\n", msg, buffer, portname);
} else {
if (msg)
- printf("%s '%s:%s'...\n", msg, buffer, portname);
+ log_msg(stdout, "%s '%s:%s'...\n", msg, buffer, portname);
if ((err = connect(sd, ptr->ai_addr, ptr->ai_addrlen)) < 0)
continue;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 34c697f3d4..8f778f57e5 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -467,7 +467,7 @@ dist_check_SCRIPTS += fastopen.sh pkgconfig.sh starttls.sh starttls-ftp.sh start
ocsp-tests/ocsp-test cipher-listings.sh sni-hostname.sh server-multi-keys.sh \
psktool.sh ocsp-tests/ocsp-load-chain gnutls-cli-save-data.sh gnutls-cli-debug.sh \
sni-resume.sh ocsp-tests/ocsptool cert-reencoding.sh pkcs7-cat.sh long-crl.sh \
- serv-udp.sh
+ serv-udp.sh logfile-option.sh
dist_check_SCRIPTS += gnutls-cli-self-signed.sh gnutls-cli-invalid-crl.sh
diff --git a/tests/logfile-option.sh b/tests/logfile-option.sh
new file mode 100755
index 0000000000..64fa232c8b
--- /dev/null
+++ b/tests/logfile-option.sh
@@ -0,0 +1,113 @@
+#!/bin/sh
+
+# Copyright (C) 2010-2016 Free Software Foundation, Inc.
+#
+# Author: Nikos Mavrogiannopoulos
+#
+# This file is part of GnuTLS.
+#
+# GnuTLS is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 3 of the License, or (at
+# your option) any later version.
+#
+# GnuTLS 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. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GnuTLS; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+srcdir="${srcdir:-.}"
+SERV="${SERV:-../src/gnutls-serv${EXEEXT}}"
+CLI="${CLI:-../src/gnutls-cli${EXEEXT}}"
+unset RETCODE
+
+if ! test -x "${SERV}"; then
+ exit 77
+fi
+
+if ! test -x "${CLI}"; then
+ exit 77
+fi
+
+if test "${WINDIR}" != ""; then
+ exit 77
+fi
+
+if ! test -z "${VALGRIND}"; then
+ VALGRIND="${LIBTOOL:-libtool} --mode=execute ${VALGRIND} --error-exitcode=15"
+fi
+
+
+SERV="${SERV} -q"
+
+. "${srcdir}/scripts/common.sh"
+
+echo "Checking whether logfile option works."
+
+KEY1=${srcdir}/../doc/credentials/x509/key-rsa.pem
+CERT1=${srcdir}/../doc/credentials/x509/cert-rsa.pem
+OCSP1=${srcdir}/ocsp-tests/response1.der
+PSK=${srcdir}/psk.passwd
+
+TMPFILE1=save-data1.$$.tmp
+TMPFILE2=save-data2.$$.tmp
+
+eval "${GETPORT}"
+launch_server $$ --echo --priority NORMAL:+ECDHE-PSK:+DHE-PSK:+PSK --pskpasswd=${PSK}
+PID=$!
+wait_server ${PID}
+
+${VALGRIND} "${CLI}" -p "${PORT}" 127.0.0.1 --priority NORMAL:+ECDHE-PSK:+DHE-PSK:+PSK --pskusername=jas --pskkey=9e32cf7786321a828ef7668f09fb35db </dev/null >${TMPFILE2}
+
+kill ${PID}
+wait
+
+if test -f ${TMPFILE1};then
+ echo "Logfile should not be created!"
+ exit 1
+fi
+if ! test -s ${TMPFILE2};then
+ echo "Stdout should not be empty!"
+ exit 1
+fi
+if grep -q "Handshake was completed" ${TMPFILE2};then
+ echo "Find the expected output!"
+else
+ echo "Cannot find the expected output!"
+ exit 1
+fi
+
+rm -f ${TMPFILE1} ${TMPFILE2}
+
+eval "${GETPORT}"
+launch_server $$ --echo --priority NORMAL:+ECDHE-PSK:+DHE-PSK:+PSK --pskpasswd=${PSK}
+PID=$!
+wait_server ${PID}
+
+${VALGRIND} "${CLI}" -p "${PORT}" 127.0.0.1 --logfile ${TMPFILE1} --priority NORMAL:+ECDHE-PSK:+DHE-PSK:+PSK --pskusername=jas --pskkey=9e32cf7786321a828ef7668f09fb35db </dev/null >${TMPFILE2}
+
+kill ${PID}
+wait
+
+if ! test -f ${TMPFILE1};then
+ echo "Logfile shoule be created!"
+ exit 1
+fi
+if test -s ${TMPFILE2};then
+ echo "Stdout should be empty!"
+ exit 1
+fi
+
+if grep -q "Handshake was completed" ${TMPFILE1}; then
+ echo "Found the expected output!"
+else
+ echo "Cannot find the expected output!"
+ exit 1
+fi
+rm -f ${TMPFILE1} ${TMPFILE2}
+
+exit 0