Testsuite: move test.again.dns and test.fail.dns handling to fakens
[exim.git] / test / src / client.c
CommitLineData
c55a77db
PH
1/* A little hacked up program that makes a TCP/IP call and reads a script to
2drive it, for testing Exim server code running as a daemon. It's got a bit
3messy with the addition of support for either OpenSSL or GnuTLS. The code for
2b4a568d
JH
4those was hacked out of Exim itself, then code for OpenSSL OCSP stapling was
5ripped from the openssl ocsp and s_client utilities. */
c55a77db
PH
6
7/* ANSI C standard includes */
8
9#include <ctype.h>
10#include <signal.h>
11#include <stdarg.h>
12#include <stddef.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <time.h>
17
18/* Unix includes */
19
20#include <errno.h>
21#include <dirent.h>
22#include <sys/types.h>
23
24#include <netinet/in_systm.h>
25#include <netinet/in.h>
26#include <netinet/ip.h>
27
28#include <netdb.h>
29#include <arpa/inet.h>
30#include <sys/time.h>
31#include <sys/resource.h>
32#include <sys/socket.h>
33#include <sys/stat.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <utime.h>
37
38#ifdef AF_INET6
39#define HAVE_IPV6 1
40#endif
41
42#ifndef S_ADDR_TYPE
43#define S_ADDR_TYPE u_long
44#endif
45
46typedef unsigned char uschar;
47
48#define CS (char *)
49#define US (unsigned char *)
50
51#define FALSE 0
52#define TRUE 1
53
54
55
56static int sigalrm_seen = 0;
57
58
59/* TLS support can be optionally included, either for OpenSSL or GnuTLS. The
60latter needs a whole pile of tables. */
c55a77db 61#ifdef HAVE_OPENSSL
348051ad
JH
62# define HAVE_TLS
63# include <openssl/crypto.h>
64# include <openssl/x509.h>
65# include <openssl/pem.h>
66# include <openssl/ssl.h>
67# include <openssl/err.h>
68# include <openssl/rand.h>
839a3b0d
JH
69
70# if OPENSSL_VERSION_NUMBER < 0x0090806fL && !defined(DISABLE_OCSP) && !defined(OPENSSL_NO_TLSEXT)
eca4debb
TL
71# warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile"
72# define DISABLE_OCSP
73# endif
6d68e1c7
TL
74# ifndef DISABLE_OCSP
75# include <openssl/ocsp.h>
76# endif
c55a77db
PH
77#endif
78
79
80#ifdef HAVE_GNUTLS
348051ad
JH
81# define HAVE_TLS
82# include <gnutls/gnutls.h>
83# include <gnutls/x509.h>
84# if GNUTLS_VERSION_NUMBER >= 0x030103
85# define HAVE_OCSP
86# include <gnutls/ocsp.h>
87# endif
c55a77db 88
348051ad 89# define DH_BITS 768
c55a77db
PH
90
91/* Local static variables for GNUTLS */
92
c55a77db
PH
93static gnutls_dh_params dh_params = NULL;
94
95static gnutls_certificate_credentials_t x509_cred = NULL;
96static gnutls_session tls_session = NULL;
97
98static int ssl_session_timeout = 200;
99
100/* Priorities for TLS algorithms to use. */
101
102static const int protocol_priority[16] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
103
104static const int kx_priority[16] = {
105 GNUTLS_KX_RSA,
106 GNUTLS_KX_DHE_DSS,
107 GNUTLS_KX_DHE_RSA,
c55a77db
PH
108 0 };
109
110static int default_cipher_priority[16] = {
111 GNUTLS_CIPHER_AES_256_CBC,
112 GNUTLS_CIPHER_AES_128_CBC,
113 GNUTLS_CIPHER_3DES_CBC,
114 GNUTLS_CIPHER_ARCFOUR_128,
115 0 };
116
117static const int mac_priority[16] = {
118 GNUTLS_MAC_SHA,
119 GNUTLS_MAC_MD5,
120 0 };
121
122static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 };
123static const int cert_type_priority[16] = { GNUTLS_CRT_X509, 0 };
124
348051ad 125#endif /*HAVE_GNUTLS*/
c55a77db
PH
126
127
128
2b4a568d
JH
129#ifdef HAVE_TLS
130char * ocsp_stapling = NULL;
131#endif
132
c55a77db
PH
133
134/*************************************************
135* SIGALRM handler - crash out *
136*************************************************/
137
138static void
139sigalrm_handler_crash(int sig)
140{
141sig = sig; /* Keep picky compilers happy */
142printf("\nClient timed out\n");
143exit(99);
144}
145
146
147/*************************************************
148* SIGALRM handler - set flag *
149*************************************************/
150
151static void
152sigalrm_handler_flag(int sig)
153{
154sig = sig; /* Keep picky compilers happy */
155sigalrm_seen = 1;
156}
157
158
159
160/****************************************************************************/
161/****************************************************************************/
162
163#ifdef HAVE_OPENSSL
f5d78688
JH
164
165X509_STORE *
166setup_verify(BIO *bp, char *CAfile, char *CApath)
167{
168 X509_STORE *store;
169 X509_LOOKUP *lookup;
170 if(!(store = X509_STORE_new())) goto end;
171 lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file());
172 if (lookup == NULL) goto end;
173 if (CAfile) {
174 if(!X509_LOOKUP_load_file(lookup,CAfile,X509_FILETYPE_PEM)) {
175 BIO_printf(bp, "Error loading file %s\n", CAfile);
176 goto end;
177 }
178 } else X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT);
179
180 lookup=X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
181 if (lookup == NULL) goto end;
182 if (CApath) {
183 if(!X509_LOOKUP_add_dir(lookup,CApath,X509_FILETYPE_PEM)) {
184 BIO_printf(bp, "Error loading directory %s\n", CApath);
185 goto end;
186 }
187 } else X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
188
189 ERR_clear_error();
190 return store;
191 end:
192 X509_STORE_free(store);
193 return NULL;
194}
195
196
6d68e1c7 197#ifndef DISABLE_OCSP
f5d78688
JH
198static int
199tls_client_stapling_cb(SSL *s, void *arg)
200{
201const unsigned char *p;
202int len;
203OCSP_RESPONSE *rsp;
204OCSP_BASICRESP *bs;
205char *CAfile = NULL;
206X509_STORE *store = NULL;
207int ret = 1;
208
209len = SSL_get_tlsext_status_ocsp_resp(s, &p);
210/*BIO_printf(arg, "OCSP response: ");*/
211if (!p)
212 {
213 BIO_printf(arg, "no response received\n");
214 return 1;
215 }
216if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
217 {
218 BIO_printf(arg, "response parse error\n");
219 BIO_dump_indent(arg, (char *)p, len, 4);
220 return 0;
221 }
222if(!(bs = OCSP_response_get1_basic(rsp)))
223 {
224 BIO_printf(arg, "error parsing response\n");
225 return 0;
226 }
227
228CAfile = ocsp_stapling;
229if(!(store = setup_verify(arg, CAfile, NULL)))
230 {
231 BIO_printf(arg, "error in cert setup\n");
232 return 0;
233 }
234
235/* No file of alternate certs, no options */
236if(OCSP_basic_verify(bs, NULL, store, 0) <= 0)
237 {
238 BIO_printf(arg, "Response Verify Failure\n");
239 ERR_print_errors(arg);
240 ret = 0;
241 }
242else
243 BIO_printf(arg, "Response verify OK\n");
244
245X509_STORE_free(store);
246return ret;
247}
6d68e1c7 248#endif
f5d78688
JH
249
250
c55a77db
PH
251/*************************************************
252* Start an OpenSSL TLS session *
253*************************************************/
254
2b4a568d
JH
255int
256tls_start(int sock, SSL **ssl, SSL_CTX *ctx)
c55a77db
PH
257{
258int rc;
57cde6e4 259static const unsigned char *sid_ctx = US"exim";
c55a77db
PH
260
261RAND_load_file("client.c", -1); /* Not *very* random! */
262
263*ssl = SSL_new (ctx);
57cde6e4 264SSL_set_session_id_context(*ssl, sid_ctx, strlen(CS sid_ctx));
c55a77db
PH
265SSL_set_fd (*ssl, sock);
266SSL_set_connect_state(*ssl);
267
6d68e1c7 268#ifndef DISABLE_OCSP
f5d78688
JH
269if (ocsp_stapling)
270 {
271 SSL_CTX_set_tlsext_status_cb(ctx, tls_client_stapling_cb);
272 SSL_CTX_set_tlsext_status_arg(ctx, BIO_new_fp(stdout, BIO_NOCLOSE));
273 SSL_set_tlsext_status_type(*ssl, TLSEXT_STATUSTYPE_ocsp);
274 }
6d68e1c7 275#endif
f5d78688 276
c55a77db
PH
277signal(SIGALRM, sigalrm_handler_flag);
278sigalrm_seen = 0;
279alarm(5);
280rc = SSL_connect (*ssl);
281alarm(0);
282
283if (sigalrm_seen)
284 {
285 printf("SSL_connect timed out\n");
286 return 0;
287 }
288
289if (rc <= 0)
290 {
291 ERR_print_errors_fp(stdout);
292 return 0;
293 }
294
295printf("SSL connection using %s\n", SSL_get_cipher (*ssl));
296return 1;
297}
298
299
300/*************************************************
301* SSL Information callback *
302*************************************************/
303
304static void
305info_callback(SSL *s, int where, int ret)
306{
307where = where;
308ret = ret;
309printf("SSL info: %s\n", SSL_state_string_long(s));
310}
311#endif
312
313
314/****************************************************************************/
315/****************************************************************************/
316
317
318#ifdef HAVE_GNUTLS
319/*************************************************
320* Handle GnuTLS error *
321*************************************************/
322
323/* Called from lots of places when errors occur before actually starting to do
324the TLS handshake, that is, while the session is still in clear.
325
326Argument:
327 prefix prefix text
328 err a GnuTLS error number, or 0 if local error
329
330Returns: doesn't - it dies
331*/
332
333static void
334gnutls_error(uschar *prefix, int err)
335{
bb727765 336fprintf(stderr, "GnuTLS connection error: %s:", prefix);
c55a77db
PH
337if (err != 0) fprintf(stderr, " %s", gnutls_strerror(err));
338fprintf(stderr, "\n");
9ff403f8 339exit(98);
c55a77db
PH
340}
341
342
343
344/*************************************************
bb727765 345* Setup up DH parameters *
c55a77db
PH
346*************************************************/
347
348/* For the test suite, the parameters should always be available in the spool
349directory. */
350
351static void
bb727765 352init_dh(void)
c55a77db
PH
353{
354int fd;
355int ret;
356gnutls_datum m;
357uschar filename[200];
358struct stat statbuf;
359
360/* Initialize the data structures for holding the parameters */
361
c55a77db
PH
362ret = gnutls_dh_params_init(&dh_params);
363if (ret < 0) gnutls_error(US"init dh_params", ret);
364
365/* Open the cache file for reading and if successful, read it and set up the
bb727765 366parameters. */
c55a77db
PH
367
368fd = open("aux-fixed/gnutls-params", O_RDONLY, 0);
369if (fd < 0)
370 {
371 fprintf(stderr, "Failed to open spool/gnutls-params: %s\n", strerror(errno));
9ff403f8 372 exit(97);
c55a77db
PH
373 }
374
375if (fstat(fd, &statbuf) < 0)
376 {
377 (void)close(fd);
378 return gnutls_error(US"TLS cache stat failed", 0);
379 }
380
381m.size = statbuf.st_size;
382m.data = malloc(m.size);
383if (m.data == NULL)
384 return gnutls_error(US"memory allocation failed", 0);
385if (read(fd, m.data, m.size) != m.size)
386 return gnutls_error(US"TLS cache read failed", 0);
387(void)close(fd);
388
c55a77db
PH
389ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM);
390if (ret < 0) return gnutls_error(US"DH params import", ret);
391free(m.data);
392}
393
394
395
396
397/*************************************************
398* Initialize for GnuTLS *
399*************************************************/
400
401/*
402Arguments:
403 certificate certificate file
404 privatekey private key file
405*/
406
407static void
408tls_init(uschar *certificate, uschar *privatekey)
409{
410int rc;
411
412rc = gnutls_global_init();
413if (rc < 0) gnutls_error(US"gnutls_global_init", rc);
414
bb727765 415/* Read D-H parameters from the cache file. */
c55a77db 416
bb727765 417init_dh();
c55a77db
PH
418
419/* Create the credentials structure */
420
421rc = gnutls_certificate_allocate_credentials(&x509_cred);
422if (rc < 0) gnutls_error(US"certificate_allocate_credentials", rc);
423
424/* Set the certificate and private keys */
425
426if (certificate != NULL)
427 {
428 rc = gnutls_certificate_set_x509_key_file(x509_cred, CS certificate,
429 CS privatekey, GNUTLS_X509_FMT_PEM);
430 if (rc < 0) gnutls_error("gnutls_certificate", rc);
431 }
432
433/* Associate the parameters with the x509 credentials structure. */
434
435gnutls_certificate_set_dh_params(x509_cred, dh_params);
2b4a568d
JH
436
437/* set the CA info for server-cert verify */
438if (ocsp_stapling)
439 gnutls_certificate_set_x509_trust_file(x509_cred, ocsp_stapling,
440 GNUTLS_X509_FMT_PEM);
c55a77db
PH
441}
442
443
444
445/*************************************************
446* Initialize a single GNUTLS session *
447*************************************************/
448
449static gnutls_session
450tls_session_init(void)
451{
452gnutls_session session;
453
454gnutls_init(&session, GNUTLS_CLIENT);
455
456gnutls_cipher_set_priority(session, default_cipher_priority);
457gnutls_compression_set_priority(session, comp_priority);
458gnutls_kx_set_priority(session, kx_priority);
459gnutls_protocol_set_priority(session, protocol_priority);
460gnutls_mac_set_priority(session, mac_priority);
461
462gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
463
464gnutls_dh_set_prime_bits(session, DH_BITS);
465gnutls_db_set_cache_expiration(session, ssl_session_timeout);
466
467return session;
468}
469#endif
470
471
472/****************************************************************************/
473/****************************************************************************/
474
475
476
477
478/*************************************************
479* Main Program *
480*************************************************/
481
4fe99a6c 482const char * const HELP_MESSAGE = "\n\
972e83f5
HSHR
483Usage: client\n"
484#ifdef HAVE_TLS
485"\
486 [-tls-on-connect]\n\
487 [-ocsp]\n"
488#endif
489"\
490 [-tn] n seconds timeout\n\
4fe99a6c
PP
491 <IP address>\n\
492 <port>\n\
493 [<outgoing interface>]\n\
494 [<cert file>]\n\
495 [<key file>]\n\
496\n";
c55a77db
PH
497
498int main(int argc, char **argv)
499{
500struct sockaddr *s_ptr;
501struct sockaddr_in s_in4;
502char *interface = NULL;
503char *address = NULL;
504char *certfile = NULL;
505char *keyfile = NULL;
1ec3f27d 506char *end = NULL;
c55a77db
PH
507int argi = 1;
508int host_af, port, s_len, rc, sock, save_errno;
d528a389 509int timeout = 5;
c55a77db
PH
510int tls_active = 0;
511int sent_starttls = 0;
512int tls_on_connect = 0;
1ec3f27d 513long tmplong;
c55a77db
PH
514
515#if HAVE_IPV6
516struct sockaddr_in6 s_in6;
517#endif
518
519#ifdef HAVE_OPENSSL
520SSL_CTX* ctx;
521SSL* ssl;
522#endif
523
524unsigned char outbuffer[10240];
525unsigned char inbuffer[10240];
526unsigned char *inptr = inbuffer;
527
528*inptr = 0; /* Buffer empty */
529
530/* Options */
531
532while (argc >= argi + 1 && argv[argi][0] == '-')
533 {
4fe99a6c
PP
534 if (strcmp(argv[argi], "-help") == 0 ||
535 strcmp(argv[argi], "--help") == 0 ||
536 strcmp(argv[argi], "-h") == 0)
537 {
dd4c8678 538 puts(HELP_MESSAGE);
4fe99a6c
PP
539 exit(0);
540 }
c55a77db
PH
541 if (strcmp(argv[argi], "-tls-on-connect") == 0)
542 {
543 tls_on_connect = 1;
544 argi++;
545 }
2b4a568d 546#ifdef HAVE_TLS
f5d78688
JH
547 else if (strcmp(argv[argi], "-ocsp") == 0)
548 {
549 if (argc < ++argi + 1)
550 {
551 fprintf(stderr, "Missing required certificate file for ocsp option\n");
9ff403f8 552 exit(96);
f5d78688
JH
553 }
554 ocsp_stapling = argv[argi++];
555 }
2b4a568d 556
f5d78688 557#endif
c55a77db
PH
558 else if (argv[argi][1] == 't' && isdigit(argv[argi][2]))
559 {
1ec3f27d
PP
560 tmplong = strtol(argv[argi]+2, &end, 10);
561 if (end == argv[argi]+2 || *end)
562 {
563 fprintf(stderr, "Failed to parse seconds from option <%s>\n",
564 argv[argi]);
9ff403f8 565 exit(95);
1ec3f27d
PP
566 }
567 if (tmplong > 10000L)
568 {
dd4c8678 569 fprintf(stderr, "Unreasonably long wait of %ld seconds requested\n",
1ec3f27d 570 tmplong);
9ff403f8 571 exit(94);
1ec3f27d
PP
572 }
573 if (tmplong < 0L)
574 {
dd4c8678 575 fprintf(stderr, "Timeout must not be negative (%ld)\n", tmplong);
9ff403f8 576 exit(93);
1ec3f27d
PP
577 }
578 timeout = (int) tmplong;
c55a77db
PH
579 argi++;
580 }
581 else
582 {
1ec3f27d 583 fprintf(stderr, "Unrecognized option %s\n", argv[argi]);
9ff403f8 584 exit(92);
c55a77db
PH
585 }
586 }
587
588/* Mandatory 1st arg is IP address */
589
590if (argc < argi+1)
591 {
1ec3f27d 592 fprintf(stderr, "No IP address given\n");
9ff403f8 593 exit(91);
c55a77db
PH
594 }
595
596address = argv[argi++];
597host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET;
598
599/* Mandatory 2nd arg is port */
600
601if (argc < argi+1)
602 {
1ec3f27d 603 fprintf(stderr, "No port number given\n");
9ff403f8 604 exit(90);
c55a77db
PH
605 }
606
607port = atoi(argv[argi++]);
608
609/* Optional next arg is interface */
610
611if (argc > argi &&
612 (isdigit((unsigned char)argv[argi][0]) || argv[argi][0] == ':'))
613 interface = argv[argi++];
614
615/* Any more arguments are the name of a certificate file and key file */
616
617if (argc > argi) certfile = argv[argi++];
618if (argc > argi) keyfile = argv[argi++];
619
620
621#if HAVE_IPV6
622/* For an IPv6 address, use an IPv6 sockaddr structure. */
623
624if (host_af == AF_INET6)
625 {
626 s_ptr = (struct sockaddr *)&s_in6;
627 s_len = sizeof(s_in6);
628 }
629else
630#endif
631
632/* For an IPv4 address, use an IPv4 sockaddr structure,
633even on an IPv6 system. */
634
635 {
636 s_ptr = (struct sockaddr *)&s_in4;
637 s_len = sizeof(s_in4);
638 }
639
640printf("Connecting to %s port %d ... ", address, port);
641
642sock = socket(host_af, SOCK_STREAM, 0);
643if (sock < 0)
644 {
645 printf("socket creation failed: %s\n", strerror(errno));
9ff403f8 646 exit(89);
c55a77db
PH
647 }
648
649/* Bind to a specific interface if requested. On an IPv6 system, this has
650to be of the same family as the address we are calling. On an IPv4 system the
651test is redundant, but it keeps the code tidier. */
652
653if (interface != NULL)
654 {
655 int interface_af = (strchr(interface, ':') != NULL)? AF_INET6 : AF_INET;
656
657 if (interface_af == host_af)
658 {
659 #if HAVE_IPV6
660
661 /* Set up for IPv6 binding */
662
663 if (host_af == AF_INET6)
664 {
665 memset(&s_in6, 0, sizeof(s_in6));
666 s_in6.sin6_family = AF_INET6;
667 s_in6.sin6_port = 0;
668 if (inet_pton(AF_INET6, interface, &s_in6.sin6_addr) != 1)
669 {
670 printf("Unable to parse \"%s\"", interface);
9ff403f8 671 exit(88);
c55a77db
PH
672 }
673 }
674 else
675 #endif
676
677 /* Set up for IPv4 binding */
678
679 {
680 memset(&s_in4, 0, sizeof(s_in4));
681 s_in4.sin_family = AF_INET;
682 s_in4.sin_port = 0;
683 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(interface);
684 }
685
686 /* Bind */
687
688 if (bind(sock, s_ptr, s_len) < 0)
689 {
690 printf("Unable to bind outgoing SMTP call to %s: %s",
691 interface, strerror(errno));
9ff403f8 692 exit(87);
c55a77db
PH
693 }
694 }
695 }
696
697/* Set up a remote IPv6 address */
698
699#if HAVE_IPV6
700if (host_af == AF_INET6)
701 {
702 memset(&s_in6, 0, sizeof(s_in6));
703 s_in6.sin6_family = AF_INET6;
704 s_in6.sin6_port = htons(port);
705 if (inet_pton(host_af, address, &s_in6.sin6_addr) != 1)
706 {
707 printf("Unable to parse \"%s\"", address);
9ff403f8 708 exit(86);
c55a77db
PH
709 }
710 }
711else
712#endif
713
714/* Set up a remote IPv4 address */
715
716 {
717 memset(&s_in4, 0, sizeof(s_in4));
718 s_in4.sin_family = AF_INET;
719 s_in4.sin_port = htons(port);
720 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(address);
721 }
722
723/* SIGALRM handler crashes out */
724
725signal(SIGALRM, sigalrm_handler_crash);
726alarm(timeout);
727rc = connect(sock, s_ptr, s_len);
728save_errno = errno;
729alarm(0);
730
731/* A failure whose error code is "Interrupted system call" is in fact
732an externally applied timeout if the signal handler has been run. */
733
734if (rc < 0)
735 {
736 close(sock);
41fdef91 737 printf("connect failed: %s\n", strerror(save_errno));
9ff403f8 738 exit(85);
c55a77db
PH
739 }
740
741printf("connected\n");
742
743
744/* --------------- Set up for OpenSSL --------------- */
745
746#ifdef HAVE_OPENSSL
747SSL_library_init();
748SSL_load_error_strings();
749
750ctx = SSL_CTX_new(SSLv23_method());
751if (ctx == NULL)
752 {
753 printf ("SSL_CTX_new failed\n");
9ff403f8 754 exit(84);
c55a77db
PH
755 }
756
757if (certfile != NULL)
758 {
759 if (!SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM))
760 {
761 printf("SSL_CTX_use_certificate_file failed\n");
9ff403f8 762 exit(83);
c55a77db
PH
763 }
764 printf("Certificate file = %s\n", certfile);
765 }
766
767if (keyfile != NULL)
768 {
769 if (!SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))
770 {
771 printf("SSL_CTX_use_PrivateKey_file failed\n");
9ff403f8 772 exit(82);
c55a77db
PH
773 }
774 printf("Key file = %s\n", keyfile);
775 }
776
777SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
778SSL_CTX_set_timeout(ctx, 200);
779SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
780#endif
781
782
783/* --------------- Set up for GnuTLS --------------- */
784
785#ifdef HAVE_GNUTLS
786if (certfile != NULL) printf("Certificate file = %s\n", certfile);
787if (keyfile != NULL) printf("Key file = %s\n", keyfile);
788tls_init(certfile, keyfile);
789tls_session = tls_session_init();
348051ad 790#ifdef HAVE_OCSP
2b4a568d
JH
791if (ocsp_stapling)
792 gnutls_ocsp_status_request_enable_client(tls_session, NULL, 0, NULL);
348051ad 793#endif
c55a77db
PH
794gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)sock);
795
796/* When the server asks for a certificate and the client does not have one,
797there is a SIGPIPE error in the gnutls_handshake() function for some reason
798that is not understood. As luck would have it, this has never hit Exim itself
799because it ignores SIGPIPE errors. Doing the same here allows it all to work as
800one wants. */
801
802signal(SIGPIPE, SIG_IGN);
803#endif
804
805/* ---------------------------------------------- */
806
807
808/* Start TLS session if configured to do so without STARTTLS */
809
810#ifdef HAVE_TLS
811if (tls_on_connect)
812 {
813 printf("Attempting to start TLS\n");
814
815 #ifdef HAVE_OPENSSL
816 tls_active = tls_start(sock, &ssl, ctx);
817 #endif
818
819 #ifdef HAVE_GNUTLS
820 sigalrm_seen = FALSE;
821 alarm(timeout);
822 tls_active = gnutls_handshake(tls_session) >= 0;
823 alarm(0);
824 #endif
825
826 if (!tls_active)
827 printf("Failed to start TLS\n");
348051ad 828 #if defined(HAVE_GNUTLS) && defined(HAVE_OCSP)
2b4a568d
JH
829 else if ( ocsp_stapling
830 && gnutls_ocsp_status_request_is_checked(tls_session, 0) == 0)
831 printf("Failed to verify certificate status\n");
832 #endif
c55a77db
PH
833 else
834 printf("Succeeded in starting TLS\n");
835 }
836#endif
837
a719fce4 838while (fgets(CS outbuffer, sizeof(outbuffer), stdin) != NULL)
c55a77db 839 {
a719fce4 840 int n = (int)strlen(CS outbuffer);
c55a77db
PH
841 while (n > 0 && isspace(outbuffer[n-1])) n--;
842 outbuffer[n] = 0;
843
844 /* Expect incoming */
845
a719fce4 846 if (strncmp(CS outbuffer, "??? ", 4) == 0)
c55a77db
PH
847 {
848 unsigned char *lineptr;
849 printf("%s\n", outbuffer);
850
851 if (*inptr == 0) /* Refill input buffer */
852 {
853 if (tls_active)
854 {
855 #ifdef HAVE_OPENSSL
856 rc = SSL_read (ssl, inbuffer, sizeof(inbuffer) - 1);
857 #endif
858 #ifdef HAVE_GNUTLS
859 rc = gnutls_record_recv(tls_session, CS inbuffer, sizeof(inbuffer) - 1);
860 #endif
861 }
862 else
863 {
864 alarm(timeout);
865 rc = read(sock, inbuffer, sizeof(inbuffer));
866 alarm(0);
867 }
868
869 if (rc < 0)
870 {
871 printf("Read error %s\n", strerror(errno));
9ff403f8 872 exit(81) ;
c55a77db
PH
873 }
874 else if (rc == 0)
875 {
876 printf("Unexpected EOF read\n");
877 close(sock);
9ff403f8 878 exit(80);
c55a77db
PH
879 }
880 else
881 {
882 inbuffer[rc] = 0;
883 inptr = inbuffer;
884 }
885 }
886
887 lineptr = inptr;
888 while (*inptr != 0 && *inptr != '\r' && *inptr != '\n') inptr++;
889 if (*inptr != 0)
890 {
891 *inptr++ = 0;
892 if (*inptr == '\n') inptr++;
893 }
894
895 printf("<<< %s\n", lineptr);
a719fce4 896 if (strncmp(CS lineptr, CS outbuffer + 4, (int)strlen(CS outbuffer) - 4) != 0)
c55a77db
PH
897 {
898 printf("\n******** Input mismatch ********\n");
9ff403f8 899 exit(79);
c55a77db
PH
900 }
901
902 #ifdef HAVE_TLS
903 if (sent_starttls)
904 {
905 if (lineptr[0] == '2')
906 {
2b4a568d
JH
907int rc;
908 unsigned int verify;
909
c55a77db
PH
910 printf("Attempting to start TLS\n");
911 fflush(stdout);
912
913 #ifdef HAVE_OPENSSL
914 tls_active = tls_start(sock, &ssl, ctx);
915 #endif
916
917 #ifdef HAVE_GNUTLS
918 sigalrm_seen = FALSE;
919 alarm(timeout);
920 tls_active = gnutls_handshake(tls_session) >= 0;
921 alarm(0);
922 #endif
923
924 if (!tls_active)
925 {
926 printf("Failed to start TLS\n");
927 fflush(stdout);
928 }
2b4a568d
JH
929 #ifdef HAVE_GNUTLS
930 else if (ocsp_stapling)
931 {
932 if ((rc= gnutls_certificate_verify_peers2(tls_session, &verify)) < 0)
933 {
934 printf("Failed to verify certificate: %s\n", gnutls_strerror(rc));
935 fflush(stdout);
936 }
937 else if (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED))
938 {
939 printf("Bad certificate\n");
940 fflush(stdout);
941 }
348051ad 942 #ifdef HAVE_OCSP
2b4a568d
JH
943 else if (gnutls_ocsp_status_request_is_checked(tls_session, 0) == 0)
944 {
945 printf("Failed to verify certificate status\n");
946 {
947 gnutls_datum_t stapling;
948 gnutls_ocsp_resp_t resp;
949 gnutls_datum_t printed;
950 if ( (rc= gnutls_ocsp_status_request_get(tls_session, &stapling)) == 0
951 && (rc= gnutls_ocsp_resp_init(&resp)) == 0
952 && (rc= gnutls_ocsp_resp_import(resp, &stapling)) == 0
953 && (rc= gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &printed)) == 0
954 )
955 {
956 fprintf(stderr, "%.4096s", printed.data);
957 gnutls_free(printed.data);
958 }
959 else
960 (void) fprintf(stderr,"ocsp decode: %s", gnutls_strerror(rc));
961 }
962 fflush(stdout);
963 }
348051ad 964 #endif
2b4a568d
JH
965 }
966 #endif
c55a77db
PH
967 else
968 printf("Succeeded in starting TLS\n");
969 }
970 else printf("Abandoning TLS start attempt\n");
971 }
972 sent_starttls = 0;
973 #endif
974 }
975
976 /* Wait for a bit before proceeding */
977
a719fce4 978 else if (strncmp(CS outbuffer, "+++ ", 4) == 0)
c55a77db
PH
979 {
980 printf("%s\n", outbuffer);
a719fce4 981 sleep(atoi(CS outbuffer + 4));
c55a77db
PH
982 }
983
984 /* Send outgoing, but barf if unconsumed incoming */
985
986 else
987 {
988 unsigned char *escape;
989
990 if (*inptr != 0)
991 {
992 printf("Unconsumed input: %s", inptr);
993 printf(" About to send: %s\n", outbuffer);
9ff403f8 994 exit(78);
c55a77db
PH
995 }
996
997 #ifdef HAVE_TLS
998
999 /* Shutdown TLS */
1000
e265af1f
JH
1001 if (strcmp(CS outbuffer, "stoptls") == 0 ||
1002 strcmp(CS outbuffer, "STOPTLS") == 0)
c55a77db
PH
1003 {
1004 if (!tls_active)
1005 {
1006 printf("STOPTLS read when TLS not active\n");
9ff403f8 1007 exit(77);
c55a77db
PH
1008 }
1009 printf("Shutting down TLS encryption\n");
1010
1011 #ifdef HAVE_OPENSSL
1012 SSL_shutdown(ssl);
1013 SSL_free(ssl);
1014 #endif
1015
1016 #ifdef HAVE_GNUTLS
1017 gnutls_bye(tls_session, GNUTLS_SHUT_WR);
1018 gnutls_deinit(tls_session);
1019 tls_session = NULL;
1020 gnutls_global_deinit();
1021 #endif
1022
1023 tls_active = 0;
1024 continue;
1025 }
1026
1027 /* Remember that we sent STARTTLS */
1028
e265af1f
JH
1029 sent_starttls = (strcmp(CS outbuffer, "starttls") == 0 ||
1030 strcmp(CS outbuffer, "STARTTLS") == 0);
c55a77db
PH
1031
1032 /* Fudge: if the command is "starttls_wait", we send the starttls bit,
1033 but we haven't set the flag, so that there is no negotiation. This is for
1034 testing the server's timeout. */
1035
e265af1f 1036 if (strcmp(CS outbuffer, "starttls_wait") == 0)
c55a77db
PH
1037 {
1038 outbuffer[8] = 0;
1039 n = 8;
1040 }
1041 #endif
1042
1043 printf(">>> %s\n", outbuffer);
a719fce4 1044 strcpy(CS outbuffer + n, "\r\n");
c55a77db
PH
1045
1046 /* Turn "\n" and "\r" into the relevant characters. This is a hack. */
1047
a719fce4 1048 while ((escape = US strstr(CS outbuffer, "\\r")) != NULL)
c55a77db
PH
1049 {
1050 *escape = '\r';
1051 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
1052 n--;
1053 }
1054
e265af1f 1055 while ((escape = US strstr(CS outbuffer, "\\n")) != NULL)
c55a77db
PH
1056 {
1057 *escape = '\n';
1058 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
1059 n--;
1060 }
1061
1062 /* OK, do it */
1063
1064 alarm(timeout);
1065 if (tls_active)
1066 {
1067 #ifdef HAVE_OPENSSL
1068 rc = SSL_write (ssl, outbuffer, n + 2);
1069 #endif
1070 #ifdef HAVE_GNUTLS
1071 rc = gnutls_record_send(tls_session, CS outbuffer, n + 2);
1072 if (rc < 0)
1073 {
1074 printf("GnuTLS write error: %s\n", gnutls_strerror(rc));
9ff403f8 1075 exit(76);
c55a77db
PH
1076 }
1077 #endif
1078 }
1079 else
1080 {
1081 rc = write(sock, outbuffer, n + 2);
1082 }
1083 alarm(0);
1084
1085 if (rc < 0)
1086 {
1087 printf("Write error: %s\n", strerror(errno));
9ff403f8 1088 exit(75);
c55a77db
PH
1089 }
1090 }
1091 }
1092
1093printf("End of script\n");
1094close(sock);
1095
1096exit(0);
1097}
1098
1099/* End of client.c */