Testsuite: try to trace intermittent callout fails
[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>
5ddc9771 27#include <netinet/tcp.h>
c55a77db
PH
28
29#include <netdb.h>
30#include <arpa/inet.h>
31#include <sys/time.h>
32#include <sys/resource.h>
33#include <sys/socket.h>
34#include <sys/stat.h>
35#include <fcntl.h>
36#include <unistd.h>
37#include <utime.h>
38
90788405
JH
39/* Set to TRUE to enable debug output */
40#define DEBUG if (FALSE)
41
c55a77db
PH
42#ifdef AF_INET6
43#define HAVE_IPV6 1
44#endif
45
46#ifndef S_ADDR_TYPE
47#define S_ADDR_TYPE u_long
48#endif
49
50typedef unsigned char uschar;
51
52#define CS (char *)
53#define US (unsigned char *)
54
55#define FALSE 0
56#define TRUE 1
57
58
59
60static int sigalrm_seen = 0;
61
62
63/* TLS support can be optionally included, either for OpenSSL or GnuTLS. The
64latter needs a whole pile of tables. */
c55a77db 65#ifdef HAVE_OPENSSL
348051ad
JH
66# define HAVE_TLS
67# include <openssl/crypto.h>
68# include <openssl/x509.h>
69# include <openssl/pem.h>
70# include <openssl/ssl.h>
71# include <openssl/err.h>
72# include <openssl/rand.h>
839a3b0d
JH
73
74# if OPENSSL_VERSION_NUMBER < 0x0090806fL && !defined(DISABLE_OCSP) && !defined(OPENSSL_NO_TLSEXT)
eca4debb
TL
75# warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile"
76# define DISABLE_OCSP
77# endif
6d68e1c7
TL
78# ifndef DISABLE_OCSP
79# include <openssl/ocsp.h>
80# endif
c55a77db
PH
81#endif
82
83
84#ifdef HAVE_GNUTLS
348051ad
JH
85# define HAVE_TLS
86# include <gnutls/gnutls.h>
87# include <gnutls/x509.h>
88# if GNUTLS_VERSION_NUMBER >= 0x030103
89# define HAVE_OCSP
90# include <gnutls/ocsp.h>
5597be31
JH
91# endif
92# ifndef GNUTLS_NO_EXTENSIONS
93# define GNUTLS_NO_EXTENSIONS 0
348051ad 94# endif
c55a77db 95
348051ad 96# define DH_BITS 768
c55a77db
PH
97
98/* Local static variables for GNUTLS */
99
fc4fcc34 100static gnutls_dh_params_t dh_params = NULL;
c55a77db
PH
101
102static gnutls_certificate_credentials_t x509_cred = NULL;
fc4fcc34 103static gnutls_session_t tls_session = NULL;
c55a77db
PH
104
105static int ssl_session_timeout = 200;
106
107/* Priorities for TLS algorithms to use. */
108
ba86e143 109# if GNUTLS_VERSION_NUMBER < 0x030400
c55a77db
PH
110static const int protocol_priority[16] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
111
112static const int kx_priority[16] = {
113 GNUTLS_KX_RSA,
114 GNUTLS_KX_DHE_DSS,
115 GNUTLS_KX_DHE_RSA,
c55a77db
PH
116 0 };
117
118static int default_cipher_priority[16] = {
119 GNUTLS_CIPHER_AES_256_CBC,
120 GNUTLS_CIPHER_AES_128_CBC,
121 GNUTLS_CIPHER_3DES_CBC,
122 GNUTLS_CIPHER_ARCFOUR_128,
123 0 };
124
125static const int mac_priority[16] = {
126 GNUTLS_MAC_SHA,
127 GNUTLS_MAC_MD5,
128 0 };
129
130static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 };
ba86e143 131# endif
c55a77db 132
348051ad 133#endif /*HAVE_GNUTLS*/
c55a77db
PH
134
135
136
2b4a568d
JH
137#ifdef HAVE_TLS
138char * ocsp_stapling = NULL;
ba86e143 139char * pri_string = NULL;
2b4a568d
JH
140#endif
141
c55a77db
PH
142
143/*************************************************
144* SIGALRM handler - crash out *
145*************************************************/
146
147static void
148sigalrm_handler_crash(int sig)
149{
150sig = sig; /* Keep picky compilers happy */
151printf("\nClient timed out\n");
152exit(99);
153}
154
155
156/*************************************************
157* SIGALRM handler - set flag *
158*************************************************/
159
160static void
161sigalrm_handler_flag(int sig)
162{
163sig = sig; /* Keep picky compilers happy */
164sigalrm_seen = 1;
165}
166
167
168
169/****************************************************************************/
170/****************************************************************************/
171
172#ifdef HAVE_OPENSSL
3b3634d0 173# ifndef DISABLE_OCSP
f5d78688 174
ee5b1e28 175static STACK_OF(X509) *
3b3634d0 176chain_from_pem_file(const uschar * file)
ee5b1e28 177{
3b3634d0
JH
178BIO * bp;
179X509 * x;
180STACK_OF(X509) * sk;
ee5b1e28 181
3b3634d0
JH
182if (!(sk = sk_X509_new_null())) return NULL;
183if (!(bp = BIO_new_file(CS file, "r"))) return NULL;
184while ((x = PEM_read_bio_X509(bp, NULL, 0, NULL)))
185 sk_X509_push(sk, x);
186BIO_free(bp);
ee5b1e28
JH
187return sk;
188}
189
3b3634d0
JH
190
191
ee5b1e28
JH
192static void
193cert_stack_free(STACK_OF(X509) * sk)
194{
195while (sk_X509_num(sk) > 0) (void) sk_X509_pop(sk);
196sk_X509_free(sk);
197}
198
199
f5d78688
JH
200static int
201tls_client_stapling_cb(SSL *s, void *arg)
202{
203const unsigned char *p;
204int len;
205OCSP_RESPONSE *rsp;
206OCSP_BASICRESP *bs;
ee5b1e28 207STACK_OF(X509) * sk;
f5d78688
JH
208int ret = 1;
209
210len = SSL_get_tlsext_status_ocsp_resp(s, &p);
211/*BIO_printf(arg, "OCSP response: ");*/
212if (!p)
213 {
214 BIO_printf(arg, "no response received\n");
215 return 1;
216 }
217if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
218 {
219 BIO_printf(arg, "response parse error\n");
220 BIO_dump_indent(arg, (char *)p, len, 4);
221 return 0;
222 }
223if(!(bs = OCSP_response_get1_basic(rsp)))
224 {
225 BIO_printf(arg, "error parsing response\n");
226 return 0;
227 }
228
ee5b1e28 229
f1a49684 230if (!(sk = chain_from_pem_file((const uschar *)ocsp_stapling)))
f5d78688
JH
231 {
232 BIO_printf(arg, "error in cert setup\n");
233 return 0;
234 }
235
ee5b1e28
JH
236/* OCSP_basic_verify takes a "store" arg, but does not
237use it for the chain verification, which is all we do
238when OCSP_NOVERIFY is set. The content from the wire
239(in "bs") and a cert-stack "sk" are all that is used. */
240
241if(OCSP_basic_verify(bs, sk, NULL, OCSP_NOVERIFY) <= 0)
f5d78688
JH
242 {
243 BIO_printf(arg, "Response Verify Failure\n");
244 ERR_print_errors(arg);
245 ret = 0;
246 }
247else
248 BIO_printf(arg, "Response verify OK\n");
249
ee5b1e28 250cert_stack_free(sk);
f5d78688
JH
251return ret;
252}
3b3634d0 253# endif /*DISABLE_OCSP*/
f5d78688
JH
254
255
c55a77db
PH
256/*************************************************
257* Start an OpenSSL TLS session *
258*************************************************/
259
2b4a568d
JH
260int
261tls_start(int sock, SSL **ssl, SSL_CTX *ctx)
c55a77db
PH
262{
263int rc;
57cde6e4 264static const unsigned char *sid_ctx = US"exim";
c55a77db
PH
265
266RAND_load_file("client.c", -1); /* Not *very* random! */
267
268*ssl = SSL_new (ctx);
57cde6e4 269SSL_set_session_id_context(*ssl, sid_ctx, strlen(CS sid_ctx));
c55a77db
PH
270SSL_set_fd (*ssl, sock);
271SSL_set_connect_state(*ssl);
272
6d68e1c7 273#ifndef DISABLE_OCSP
f5d78688
JH
274if (ocsp_stapling)
275 {
276 SSL_CTX_set_tlsext_status_cb(ctx, tls_client_stapling_cb);
277 SSL_CTX_set_tlsext_status_arg(ctx, BIO_new_fp(stdout, BIO_NOCLOSE));
278 SSL_set_tlsext_status_type(*ssl, TLSEXT_STATUSTYPE_ocsp);
279 }
6d68e1c7 280#endif
f5d78688 281
c55a77db
PH
282signal(SIGALRM, sigalrm_handler_flag);
283sigalrm_seen = 0;
284alarm(5);
285rc = SSL_connect (*ssl);
286alarm(0);
287
288if (sigalrm_seen)
289 {
290 printf("SSL_connect timed out\n");
291 return 0;
292 }
293
294if (rc <= 0)
295 {
296 ERR_print_errors_fp(stdout);
297 return 0;
298 }
299
300printf("SSL connection using %s\n", SSL_get_cipher (*ssl));
301return 1;
302}
303
304
305/*************************************************
306* SSL Information callback *
307*************************************************/
308
309static void
310info_callback(SSL *s, int where, int ret)
311{
312where = where;
313ret = ret;
314printf("SSL info: %s\n", SSL_state_string_long(s));
315}
316#endif
317
318
319/****************************************************************************/
320/****************************************************************************/
321
322
323#ifdef HAVE_GNUTLS
324/*************************************************
325* Handle GnuTLS error *
326*************************************************/
327
328/* Called from lots of places when errors occur before actually starting to do
329the TLS handshake, that is, while the session is still in clear.
330
331Argument:
332 prefix prefix text
333 err a GnuTLS error number, or 0 if local error
334
335Returns: doesn't - it dies
336*/
337
338static void
339gnutls_error(uschar *prefix, int err)
340{
bb727765 341fprintf(stderr, "GnuTLS connection error: %s:", prefix);
c55a77db
PH
342if (err != 0) fprintf(stderr, " %s", gnutls_strerror(err));
343fprintf(stderr, "\n");
9ff403f8 344exit(98);
c55a77db
PH
345}
346
347
348
349/*************************************************
bb727765 350* Setup up DH parameters *
c55a77db
PH
351*************************************************/
352
353/* For the test suite, the parameters should always be available in the spool
354directory. */
355
356static void
bb727765 357init_dh(void)
c55a77db
PH
358{
359int fd;
360int ret;
fc4fcc34 361gnutls_datum_t m;
c55a77db
PH
362uschar filename[200];
363struct stat statbuf;
364
365/* Initialize the data structures for holding the parameters */
366
c55a77db
PH
367ret = gnutls_dh_params_init(&dh_params);
368if (ret < 0) gnutls_error(US"init dh_params", ret);
369
370/* Open the cache file for reading and if successful, read it and set up the
bb727765 371parameters. */
c55a77db
PH
372
373fd = open("aux-fixed/gnutls-params", O_RDONLY, 0);
374if (fd < 0)
375 {
376 fprintf(stderr, "Failed to open spool/gnutls-params: %s\n", strerror(errno));
9ff403f8 377 exit(97);
c55a77db
PH
378 }
379
380if (fstat(fd, &statbuf) < 0)
381 {
382 (void)close(fd);
383 return gnutls_error(US"TLS cache stat failed", 0);
384 }
385
386m.size = statbuf.st_size;
387m.data = malloc(m.size);
388if (m.data == NULL)
389 return gnutls_error(US"memory allocation failed", 0);
390if (read(fd, m.data, m.size) != m.size)
391 return gnutls_error(US"TLS cache read failed", 0);
392(void)close(fd);
393
c55a77db
PH
394ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM);
395if (ret < 0) return gnutls_error(US"DH params import", ret);
396free(m.data);
397}
398
399
400
401
402/*************************************************
403* Initialize for GnuTLS *
404*************************************************/
405
406/*
407Arguments:
408 certificate certificate file
409 privatekey private key file
410*/
411
412static void
413tls_init(uschar *certificate, uschar *privatekey)
414{
415int rc;
416
417rc = gnutls_global_init();
418if (rc < 0) gnutls_error(US"gnutls_global_init", rc);
419
bb727765 420/* Read D-H parameters from the cache file. */
c55a77db 421
bb727765 422init_dh();
c55a77db
PH
423
424/* Create the credentials structure */
425
426rc = gnutls_certificate_allocate_credentials(&x509_cred);
427if (rc < 0) gnutls_error(US"certificate_allocate_credentials", rc);
428
429/* Set the certificate and private keys */
430
431if (certificate != NULL)
432 {
433 rc = gnutls_certificate_set_x509_key_file(x509_cred, CS certificate,
434 CS privatekey, GNUTLS_X509_FMT_PEM);
059f2ace 435 if (rc < 0) gnutls_error(US"gnutls_certificate", rc);
c55a77db
PH
436 }
437
438/* Associate the parameters with the x509 credentials structure. */
439
440gnutls_certificate_set_dh_params(x509_cred, dh_params);
2b4a568d
JH
441
442/* set the CA info for server-cert verify */
443if (ocsp_stapling)
444 gnutls_certificate_set_x509_trust_file(x509_cred, ocsp_stapling,
445 GNUTLS_X509_FMT_PEM);
c55a77db
PH
446}
447
448
449
450/*************************************************
451* Initialize a single GNUTLS session *
452*************************************************/
453
fc4fcc34 454static gnutls_session_t
c55a77db
PH
455tls_session_init(void)
456{
fc4fcc34 457gnutls_session_t session;
c55a77db 458
0886a95e 459gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_NO_EXTENSIONS);
c55a77db 460
ba86e143 461# if GNUTLS_VERSION_NUMBER < 0x030400
c55a77db
PH
462gnutls_cipher_set_priority(session, default_cipher_priority);
463gnutls_compression_set_priority(session, comp_priority);
464gnutls_kx_set_priority(session, kx_priority);
465gnutls_protocol_set_priority(session, protocol_priority);
466gnutls_mac_set_priority(session, mac_priority);
467
468gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
ba86e143
JH
469# else
470if (pri_string)
471 {
472 gnutls_priority_t priority_cache;
473 const char * errpos;
474
475 gnutls_priority_init(&priority_cache, pri_string, &errpos);
476 gnutls_priority_set(session, priority_cache);
477 }
478else
479 gnutls_set_default_priority(session);
fc4fcc34 480gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
ba86e143 481# endif
c55a77db
PH
482
483gnutls_dh_set_prime_bits(session, DH_BITS);
484gnutls_db_set_cache_expiration(session, ssl_session_timeout);
485
486return session;
487}
488#endif
489
490
491/****************************************************************************/
5d036699
JH
492/* Turn "\n" and "\r" into the relevant characters. This is a hack. */
493
494static int
495unescape_buf(unsigned char * buf, int len)
496{
497unsigned char * s;
498unsigned char c, t;
499unsigned shift;
500
501for (s = buf; s < buf+len; s++) if (*s == '\\')
502 {
503 switch (s[1])
504 {
505 default: c = s[1]; shift = 1; break;
506 case 'n': c = '\n'; shift = 1; break;
507 case 'r': c = '\r'; shift = 1; break;
508 case 'x':
509 t = s[2];
510 if (t >= 'A' && t <= 'F') t -= 'A'-'9'-1;
511 else if (t >= 'a' && t <= 'f') t -= 'a'-'9'-1;
512 t -= '0';
513 c = (t<<4) & 0xf0;
514 t = s[3];
515 if (t >= 'A' && t <= 'F') t -= 'A'-'9'-1;
516 else if (t >= 'a' && t <= 'f') t -= 'a'-'9'-1;
517 t -= '0';
518 c |= t & 0xf;
519 shift = 3;
520 break;
521 }
522 *s = c;
523 memmove(s+1, s+shift+1, len-shift);
524 len -= shift;
525 }
526return len;
527}
528
529
c55a77db 530/****************************************************************************/
7bbb3621
JH
531typedef struct {
532 int sock;
533 int tls_active;
534#ifdef HAVE_OPENSSL
535 SSL_CTX * ctx;
536 SSL * ssl;
537#endif
538 int sent_starttls;
539} srv_ctx;
540
541static void
542do_file(srv_ctx * srv, FILE * f, int timeout,
543 unsigned char * inbuffer, unsigned bsiz, unsigned char * inptr)
544{
b9df1829 545unsigned char outbuffer[1024 * 20];
7bbb3621
JH
546
547while (fgets(CS outbuffer, sizeof(outbuffer), f) != NULL)
548 {
549 int n = (int)strlen(CS outbuffer);
550 int crlf = 1;
551 int rc;
552
553 /* Strip trailing newline */
554 if (outbuffer[n-1] == '\n') outbuffer[--n] = 0;
555
556 /* Expect incoming */
557
558 if ( strncmp(CS outbuffer, "???", 3) == 0
ce80533b 559 && (outbuffer[3] == ' ' || outbuffer[3] == '*' || outbuffer[3] == '?')
7bbb3621
JH
560 )
561 {
562 unsigned char *lineptr;
563 unsigned exp_eof = outbuffer[3] == '*';
ce80533b 564 unsigned resp_optional = outbuffer[3] == '?';
7bbb3621
JH
565
566 printf("%s\n", outbuffer);
567 n = unescape_buf(outbuffer, n);
568
ce80533b 569nextinput:
7bbb3621
JH
570 if (*inptr == 0) /* Refill input buffer */
571 {
ce80533b 572 alarm(timeout);
7bbb3621
JH
573 if (srv->tls_active)
574 {
5dcadbf4
JH
575#ifdef HAVE_OPENSSL
576 int error;
90788405 577 DEBUG { printf("call SSL_read\n"); fflush(stdout); }
ce80533b 578 rc = SSL_read(srv->ssl, inbuffer, bsiz - 1);
90788405 579 DEBUG { printf("SSL_read: %d\n", rc); fflush(stdout); }
5dcadbf4
JH
580 if (rc <= 0)
581 switch (error = SSL_get_error(srv->ssl, rc))
582 {
583 case SSL_ERROR_ZERO_RETURN:
584 break;
585 case SSL_ERROR_SYSCALL:
fd3cf789 586 printf("%s\n", ERR_error_string(ERR_get_error(), NULL));
5dcadbf4 587 rc = -1;
fd3cf789 588 break;
5dcadbf4 589 case SSL_ERROR_SSL:
fd3cf789 590 printf("%s\nTLS terminated\n", ERR_error_string(ERR_get_error(), NULL));
5dcadbf4
JH
591 SSL_shutdown(srv->ssl);
592 SSL_free(srv->ssl);
593 srv->tls_active = FALSE;
fd3cf789
JH
594 { /* OpenSSL leaves it in restartsys mode */
595 struct sigaction act = {.sa_handler = sigalrm_handler_flag, .sa_flags = 0};
596 sigalrm_seen = 1;
597 sigaction(SIGALRM, &act, NULL);
598 }
599 *inptr = 0;
90788405 600 DEBUG { printf("go round\n"); fflush(stdout); }
5dcadbf4
JH
601 goto nextinput;
602 default:
603 printf("SSL error code %d\n", error);
604 }
5dcadbf4
JH
605#endif
606#ifdef HAVE_GNUTLS
b2ba9267
JH
607 retry1:
608 DEBUG { printf("call gnutls_record_recv\n"); fflush(stdout); }
7bbb3621 609 rc = gnutls_record_recv(tls_session, CS inbuffer, bsiz - 1);
b2ba9267
JH
610 if (rc < 0)
611 {
612 DEBUG { printf("gnutls_record_recv: %s\n", gnutls_strerror(rc)); fflush(stdout); }
613 if (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN)
614 goto retry1;
615 printf("%s\n", gnutls_strerror(rc));
616 srv->tls_active = FALSE;
617 *inptr = 0;
618 DEBUG { printf("go round\n"); fflush(stdout); }
619 goto nextinput;
620 }
621 DEBUG { printf("gnutls_record_recv: %d\n", rc); fflush(stdout); }
5dcadbf4 622#endif
7bbb3621
JH
623 }
624 else
90788405
JH
625 {
626 DEBUG { printf("call read\n"); fflush(stdout); }
627 rc = read(srv->sock, inbuffer, bsiz);
628 DEBUG { printf("read: %d\n", rc); fflush(stdout); }
629 }
ce80533b 630 alarm(0);
7bbb3621
JH
631
632 if (rc < 0)
633 {
fd3cf789
JH
634 if (errno == EINTR && sigalrm_seen && resp_optional)
635 continue; /* next scriptline */
b2ba9267 636 printf("Read error: %s\n", strerror(errno));
7bbb3621
JH
637 exit(81);
638 }
639 else if (rc == 0)
640 if (exp_eof)
641 {
642 printf("Expected EOF read\n");
643 continue;
644 }
5dcadbf4
JH
645 else if (resp_optional)
646 continue; /* next scriptline */
7bbb3621
JH
647 else
648 {
649 printf("Unexpected EOF read\n");
650 close(srv->sock);
651 exit(80);
652 }
653 else if (exp_eof)
654 {
655 printf("Expected EOF not read\n");
656 close(srv->sock);
657 exit(74);
658 }
659 else
660 {
661 inbuffer[rc] = 0;
662 inptr = inbuffer;
663 }
664 }
90788405 665 DEBUG { printf("read: '%s'\n", inptr); fflush(stdout); }
7bbb3621
JH
666
667 lineptr = inptr;
668 while (*inptr != 0 && *inptr != '\r' && *inptr != '\n') inptr++;
669 if (*inptr != 0)
670 {
671 *inptr++ = 0;
672 if (*inptr == '\n') inptr++;
673 }
674
7bbb3621 675 if (strncmp(CS lineptr, CS outbuffer + 4, n - 4) != 0)
ce80533b
JH
676 if (resp_optional)
677 {
678 inptr = lineptr; /* consume scriptline, not inputline */
679 continue;
680 }
681 else
682 {
683 printf("<<< %s\n", lineptr);
684 printf("\n******** Input mismatch ********\n");
685 exit(79);
686 }
687
90788405
JH
688 /* Input matched script. Output the inputline, unless optional */
689 DEBUG { printf("read matched\n"); fflush(stdout); }
690
691 if (!resp_optional)
692 printf("<<< %s\n", lineptr);
693 else
ce80533b 694
90788405
JH
695 /* If there is further input after this line, consume inputline but not
696 scriptline in case there are several matching. Nonmatches are dealt with
697 above. */
ce80533b 698
90788405
JH
699 if (*inptr != 0)
700 goto nextinput;
7bbb3621
JH
701
702 #ifdef HAVE_TLS
703 if (srv->sent_starttls)
704 {
705 if (lineptr[0] == '2')
706 {
7bbb3621
JH
707 unsigned int verify;
708
709 printf("Attempting to start TLS\n");
710 fflush(stdout);
711
712 #ifdef HAVE_OPENSSL
713 srv->tls_active = tls_start(srv->sock, &srv->ssl, srv->ctx);
714 #endif
715
716 #ifdef HAVE_GNUTLS
717 {
718 int rc;
b2ba9267
JH
719 fd_set rfd;
720 struct timeval tv = { 0, 2000 };
721
7bbb3621
JH
722 sigalrm_seen = FALSE;
723 alarm(timeout);
724 do {
725 rc = gnutls_handshake(tls_session);
726 } while (rc < 0 && gnutls_error_is_fatal(rc) == 0);
727 srv->tls_active = rc >= 0;
728 alarm(0);
729
730 if (!srv->tls_active) printf("%s\n", gnutls_strerror(rc));
b2ba9267
JH
731
732 /* look for an error on the TLS conn */
733 FD_ZERO(&rfd);
734 FD_SET(srv->sock, &rfd);
735 if (select(srv->sock+1, &rfd, NULL, NULL, &tv) > 0)
736 {
737 retry2:
738 DEBUG { printf("call gnutls_record_recv\n"); fflush(stdout); }
739 rc = gnutls_record_recv(tls_session, CS inbuffer, bsiz - 1);
740 if (rc < 0)
741 {
742 DEBUG { printf("gnutls_record_recv: %s\n", gnutls_strerror(rc)); fflush(stdout); }
743 if (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN)
744 goto retry2;
745 printf("%s\n", gnutls_strerror(rc));
746 srv->tls_active = FALSE;
747 }
748 DEBUG { printf("gnutls_record_recv: %d\n", rc); fflush(stdout); }
749 }
7bbb3621
JH
750 }
751 #endif
752
753 if (!srv->tls_active)
754 {
755 printf("Failed to start TLS\n");
756 fflush(stdout);
757 }
758 #ifdef HAVE_GNUTLS
759 else if (ocsp_stapling)
760 {
761 if ((rc= gnutls_certificate_verify_peers2(tls_session, &verify)) < 0)
762 {
763 printf("Failed to verify certificate: %s\n", gnutls_strerror(rc));
764 fflush(stdout);
765 }
766 else if (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED))
767 {
768 printf("Bad certificate\n");
769 fflush(stdout);
770 }
771 #ifdef HAVE_OCSP
772 else if (gnutls_ocsp_status_request_is_checked(tls_session, 0) == 0)
773 {
774 printf("Failed to verify certificate status\n");
775 {
776 gnutls_datum_t stapling;
777 gnutls_ocsp_resp_t resp;
778 gnutls_datum_t printed;
779 if ( (rc= gnutls_ocsp_status_request_get(tls_session, &stapling)) == 0
780 && (rc= gnutls_ocsp_resp_init(&resp)) == 0
781 && (rc= gnutls_ocsp_resp_import(resp, &stapling)) == 0
782 && (rc= gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &printed)) == 0
783 )
784 {
785 fprintf(stderr, "%.4096s", printed.data);
786 gnutls_free(printed.data);
787 }
788 else
789 (void) fprintf(stderr,"ocsp decode: %s", gnutls_strerror(rc));
790 }
791 fflush(stdout);
792 }
793 #endif
794 }
795 #endif
796 else
797 printf("Succeeded in starting TLS\n");
798 }
799 else printf("Abandoning TLS start attempt\n");
800 }
801 srv->sent_starttls = 0;
802 #endif
803 }
804
805 /* Wait for a bit before proceeding */
806
807 else if (strncmp(CS outbuffer, "+++ ", 4) == 0)
808 {
809 printf("%s\n", outbuffer);
810 sleep(atoi(CS outbuffer + 4));
811 }
812
813 /* Stack new input file */
814
815 else if (strncmp(CS outbuffer, "<<< ", 4) == 0)
816 {
817 FILE * new_f;
f1a49684 818 if (!(new_f = fopen((const char *)outbuffer+4 , "r")))
7bbb3621 819 {
768e8b5b 820 printf("Unable to open '%s': %s", inptr, strerror(errno));
7bbb3621
JH
821 exit(74);
822 }
823 do_file(srv, new_f, timeout, inbuffer, bsiz, inptr);
824 }
825
826
827 /* Send line outgoing, but barf if unconsumed incoming */
828
829 else
830 {
831 unsigned char * out = outbuffer;
832
833 if (strncmp(CS outbuffer, ">>> ", 4) == 0)
834 {
835 crlf = 0;
836 out += 4;
837 n -= 4;
838 }
839
840 if (*inptr != 0)
841 {
842 printf("Unconsumed input: %s", inptr);
843 printf(" About to send: %s\n", out);
844 exit(78);
845 }
846
847 #ifdef HAVE_TLS
848
849 /* Shutdown TLS */
850
851 if (strcmp(CS out, "stoptls") == 0 ||
852 strcmp(CS out, "STOPTLS") == 0)
853 {
854 if (!srv->tls_active)
855 {
856 printf("STOPTLS read when TLS not active\n");
857 exit(77);
858 }
859 printf("Shutting down TLS encryption\n");
860
861 #ifdef HAVE_OPENSSL
862 SSL_shutdown(srv->ssl);
863 SSL_free(srv->ssl);
864 #endif
865
866 #ifdef HAVE_GNUTLS
867 gnutls_bye(tls_session, GNUTLS_SHUT_WR);
868 gnutls_deinit(tls_session);
869 tls_session = NULL;
870 gnutls_global_deinit();
871 #endif
872
873 srv->tls_active = 0;
874 continue;
875 }
876
877 /* Remember that we sent STARTTLS */
878
879 srv->sent_starttls = (strcmp(CS out, "starttls") == 0 ||
880 strcmp(CS out, "STARTTLS") == 0);
881
882 /* Fudge: if the command is "starttls_wait", we send the starttls bit,
883 but we haven't set the flag, so that there is no negotiation. This is for
884 testing the server's timeout. */
885
886 if (strcmp(CS out, "starttls_wait") == 0)
887 {
888 out[8] = 0;
889 n = 8;
890 }
891 #endif
892
893 printf(">>> %s\n", out);
894 if (crlf)
895 {
896 strcpy(CS out + n, "\r\n");
897 n += 2;
898 }
899
900 n = unescape_buf(out, n);
901
902 /* OK, do it */
903
904 alarm(timeout);
905 if (srv->tls_active)
906 {
907 #ifdef HAVE_OPENSSL
908 rc = SSL_write (srv->ssl, out, n);
909 #endif
910 #ifdef HAVE_GNUTLS
911 if ((rc = gnutls_record_send(tls_session, CS out, n)) < 0)
912 {
913 printf("GnuTLS write error: %s\n", gnutls_strerror(rc));
914 exit(76);
915 }
916 #endif
917 }
918 else
919 rc = write(srv->sock, out, n);
920 alarm(0);
921
922 if (rc < 0)
923 {
924 printf("Write error: %s\n", strerror(errno));
925 exit(75);
926 }
927 }
928 }
929}
c55a77db
PH
930
931
932
933
934/*************************************************
935* Main Program *
936*************************************************/
937
4fe99a6c 938const char * const HELP_MESSAGE = "\n\
972e83f5
HSHR
939Usage: client\n"
940#ifdef HAVE_TLS
941"\
942 [-tls-on-connect]\n\
943 [-ocsp]\n"
ba86e143
JH
944# ifdef HAVE_GNUTLS
945"\
946 [-p priority-string]\n"
947# endif
972e83f5
HSHR
948#endif
949"\
950 [-tn] n seconds timeout\n\
4fe99a6c
PP
951 <IP address>\n\
952 <port>\n\
953 [<outgoing interface>]\n\
954 [<cert file>]\n\
955 [<key file>]\n\
956\n";
c55a77db 957
5d036699
JH
958int
959main(int argc, char **argv)
c55a77db
PH
960{
961struct sockaddr *s_ptr;
962struct sockaddr_in s_in4;
963char *interface = NULL;
964char *address = NULL;
965char *certfile = NULL;
966char *keyfile = NULL;
1ec3f27d 967char *end = NULL;
c55a77db 968int argi = 1;
7bbb3621 969int host_af, port, s_len, rc, save_errno;
d528a389 970int timeout = 5;
c55a77db 971int tls_on_connect = 0;
1ec3f27d 972long tmplong;
c55a77db
PH
973
974#if HAVE_IPV6
975struct sockaddr_in6 s_in6;
976#endif
977
7bbb3621 978srv_ctx srv;
c55a77db 979
c55a77db
PH
980unsigned char inbuffer[10240];
981unsigned char *inptr = inbuffer;
982
983*inptr = 0; /* Buffer empty */
7bbb3621
JH
984srv.tls_active = 0;
985srv.sent_starttls = 0;
c55a77db
PH
986
987/* Options */
988
989while (argc >= argi + 1 && argv[argi][0] == '-')
990 {
4fe99a6c
PP
991 if (strcmp(argv[argi], "-help") == 0 ||
992 strcmp(argv[argi], "--help") == 0 ||
993 strcmp(argv[argi], "-h") == 0)
994 {
dd4c8678 995 puts(HELP_MESSAGE);
4fe99a6c
PP
996 exit(0);
997 }
c55a77db
PH
998 if (strcmp(argv[argi], "-tls-on-connect") == 0)
999 {
1000 tls_on_connect = 1;
1001 argi++;
1002 }
2b4a568d 1003#ifdef HAVE_TLS
f5d78688
JH
1004 else if (strcmp(argv[argi], "-ocsp") == 0)
1005 {
1006 if (argc < ++argi + 1)
1007 {
1008 fprintf(stderr, "Missing required certificate file for ocsp option\n");
9ff403f8 1009 exit(96);
f5d78688
JH
1010 }
1011 ocsp_stapling = argv[argi++];
1012 }
ba86e143
JH
1013# ifdef HAVE_GNUTLS
1014 else if (strcmp(argv[argi], "-p") == 0)
1015 {
1016 if (argc < ++argi + 1)
1017 {
1018 fprintf(stderr, "Missing priority string\n");
1019 exit(96);
1020 }
1021 pri_string = argv[argi++];
1022 }
1023#endif
2b4a568d 1024
f5d78688 1025#endif
c55a77db
PH
1026 else if (argv[argi][1] == 't' && isdigit(argv[argi][2]))
1027 {
1ec3f27d
PP
1028 tmplong = strtol(argv[argi]+2, &end, 10);
1029 if (end == argv[argi]+2 || *end)
1030 {
1031 fprintf(stderr, "Failed to parse seconds from option <%s>\n",
1032 argv[argi]);
9ff403f8 1033 exit(95);
1ec3f27d
PP
1034 }
1035 if (tmplong > 10000L)
1036 {
dd4c8678 1037 fprintf(stderr, "Unreasonably long wait of %ld seconds requested\n",
1ec3f27d 1038 tmplong);
9ff403f8 1039 exit(94);
1ec3f27d
PP
1040 }
1041 if (tmplong < 0L)
1042 {
dd4c8678 1043 fprintf(stderr, "Timeout must not be negative (%ld)\n", tmplong);
9ff403f8 1044 exit(93);
1ec3f27d
PP
1045 }
1046 timeout = (int) tmplong;
c55a77db
PH
1047 argi++;
1048 }
1049 else
1050 {
1ec3f27d 1051 fprintf(stderr, "Unrecognized option %s\n", argv[argi]);
9ff403f8 1052 exit(92);
c55a77db
PH
1053 }
1054 }
1055
1056/* Mandatory 1st arg is IP address */
1057
1058if (argc < argi+1)
1059 {
1ec3f27d 1060 fprintf(stderr, "No IP address given\n");
9ff403f8 1061 exit(91);
c55a77db
PH
1062 }
1063
1064address = argv[argi++];
1065host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET;
1066
1067/* Mandatory 2nd arg is port */
1068
1069if (argc < argi+1)
1070 {
1ec3f27d 1071 fprintf(stderr, "No port number given\n");
9ff403f8 1072 exit(90);
c55a77db
PH
1073 }
1074
1075port = atoi(argv[argi++]);
1076
1077/* Optional next arg is interface */
1078
1079if (argc > argi &&
1080 (isdigit((unsigned char)argv[argi][0]) || argv[argi][0] == ':'))
1081 interface = argv[argi++];
1082
1083/* Any more arguments are the name of a certificate file and key file */
1084
1085if (argc > argi) certfile = argv[argi++];
1086if (argc > argi) keyfile = argv[argi++];
1087
1088
1089#if HAVE_IPV6
1090/* For an IPv6 address, use an IPv6 sockaddr structure. */
1091
1092if (host_af == AF_INET6)
1093 {
1094 s_ptr = (struct sockaddr *)&s_in6;
1095 s_len = sizeof(s_in6);
1096 }
1097else
1098#endif
1099
1100/* For an IPv4 address, use an IPv4 sockaddr structure,
1101even on an IPv6 system. */
1102
1103 {
1104 s_ptr = (struct sockaddr *)&s_in4;
1105 s_len = sizeof(s_in4);
1106 }
1107
1108printf("Connecting to %s port %d ... ", address, port);
1109
7bbb3621
JH
1110srv.sock = socket(host_af, SOCK_STREAM, 0);
1111if (srv.sock < 0)
c55a77db
PH
1112 {
1113 printf("socket creation failed: %s\n", strerror(errno));
9ff403f8 1114 exit(89);
c55a77db
PH
1115 }
1116
1117/* Bind to a specific interface if requested. On an IPv6 system, this has
1118to be of the same family as the address we are calling. On an IPv4 system the
1119test is redundant, but it keeps the code tidier. */
1120
1121if (interface != NULL)
1122 {
1123 int interface_af = (strchr(interface, ':') != NULL)? AF_INET6 : AF_INET;
1124
1125 if (interface_af == host_af)
1126 {
1127 #if HAVE_IPV6
1128
1129 /* Set up for IPv6 binding */
1130
1131 if (host_af == AF_INET6)
1132 {
1133 memset(&s_in6, 0, sizeof(s_in6));
1134 s_in6.sin6_family = AF_INET6;
1135 s_in6.sin6_port = 0;
1136 if (inet_pton(AF_INET6, interface, &s_in6.sin6_addr) != 1)
1137 {
1138 printf("Unable to parse \"%s\"", interface);
9ff403f8 1139 exit(88);
c55a77db
PH
1140 }
1141 }
1142 else
1143 #endif
1144
1145 /* Set up for IPv4 binding */
1146
1147 {
1148 memset(&s_in4, 0, sizeof(s_in4));
1149 s_in4.sin_family = AF_INET;
1150 s_in4.sin_port = 0;
1151 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(interface);
1152 }
1153
1154 /* Bind */
1155
7bbb3621 1156 if (bind(srv.sock, s_ptr, s_len) < 0)
c55a77db
PH
1157 {
1158 printf("Unable to bind outgoing SMTP call to %s: %s",
1159 interface, strerror(errno));
9ff403f8 1160 exit(87);
c55a77db
PH
1161 }
1162 }
1163 }
1164
1165/* Set up a remote IPv6 address */
1166
1167#if HAVE_IPV6
1168if (host_af == AF_INET6)
1169 {
1170 memset(&s_in6, 0, sizeof(s_in6));
1171 s_in6.sin6_family = AF_INET6;
1172 s_in6.sin6_port = htons(port);
1173 if (inet_pton(host_af, address, &s_in6.sin6_addr) != 1)
1174 {
1175 printf("Unable to parse \"%s\"", address);
9ff403f8 1176 exit(86);
c55a77db
PH
1177 }
1178 }
1179else
1180#endif
1181
1182/* Set up a remote IPv4 address */
1183
1184 {
1185 memset(&s_in4, 0, sizeof(s_in4));
1186 s_in4.sin_family = AF_INET;
1187 s_in4.sin_port = htons(port);
1188 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(address);
1189 }
1190
1191/* SIGALRM handler crashes out */
1192
1193signal(SIGALRM, sigalrm_handler_crash);
1194alarm(timeout);
7bbb3621 1195rc = connect(srv.sock, s_ptr, s_len);
c55a77db
PH
1196save_errno = errno;
1197alarm(0);
1198
1199/* A failure whose error code is "Interrupted system call" is in fact
1200an externally applied timeout if the signal handler has been run. */
1201
1202if (rc < 0)
1203 {
7bbb3621 1204 close(srv.sock);
41fdef91 1205 printf("connect failed: %s\n", strerror(save_errno));
9ff403f8 1206 exit(85);
c55a77db
PH
1207 }
1208
1209printf("connected\n");
1210
1211
1212/* --------------- Set up for OpenSSL --------------- */
1213
1214#ifdef HAVE_OPENSSL
1215SSL_library_init();
1216SSL_load_error_strings();
1217
7bbb3621 1218if (!(srv.ctx = SSL_CTX_new(SSLv23_method())))
c55a77db
PH
1219 {
1220 printf ("SSL_CTX_new failed\n");
9ff403f8 1221 exit(84);
c55a77db
PH
1222 }
1223
7bbb3621 1224if (certfile)
c55a77db 1225 {
7bbb3621 1226 if (!SSL_CTX_use_certificate_file(srv.ctx, certfile, SSL_FILETYPE_PEM))
c55a77db
PH
1227 {
1228 printf("SSL_CTX_use_certificate_file failed\n");
9ff403f8 1229 exit(83);
c55a77db
PH
1230 }
1231 printf("Certificate file = %s\n", certfile);
1232 }
1233
7bbb3621 1234if (keyfile)
c55a77db 1235 {
7bbb3621 1236 if (!SSL_CTX_use_PrivateKey_file(srv.ctx, keyfile, SSL_FILETYPE_PEM))
c55a77db
PH
1237 {
1238 printf("SSL_CTX_use_PrivateKey_file failed\n");
9ff403f8 1239 exit(82);
c55a77db
PH
1240 }
1241 printf("Key file = %s\n", keyfile);
1242 }
1243
7bbb3621
JH
1244SSL_CTX_set_session_cache_mode(srv.ctx, SSL_SESS_CACHE_BOTH);
1245SSL_CTX_set_timeout(srv.ctx, 200);
1246SSL_CTX_set_info_callback(srv.ctx, (void (*)())info_callback);
c55a77db
PH
1247#endif
1248
1249
1250/* --------------- Set up for GnuTLS --------------- */
1251
1252#ifdef HAVE_GNUTLS
1253if (certfile != NULL) printf("Certificate file = %s\n", certfile);
1254if (keyfile != NULL) printf("Key file = %s\n", keyfile);
059f2ace 1255tls_init(US certfile, US keyfile);
c55a77db 1256tls_session = tls_session_init();
348051ad 1257#ifdef HAVE_OCSP
2b4a568d
JH
1258if (ocsp_stapling)
1259 gnutls_ocsp_status_request_enable_client(tls_session, NULL, 0, NULL);
348051ad 1260#endif
7bbb3621 1261gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr_t)(intptr_t)srv.sock);
c55a77db
PH
1262
1263/* When the server asks for a certificate and the client does not have one,
1264there is a SIGPIPE error in the gnutls_handshake() function for some reason
1265that is not understood. As luck would have it, this has never hit Exim itself
1266because it ignores SIGPIPE errors. Doing the same here allows it all to work as
1267one wants. */
1268
1269signal(SIGPIPE, SIG_IGN);
1270#endif
1271
1272/* ---------------------------------------------- */
1273
1274
1275/* Start TLS session if configured to do so without STARTTLS */
1276
1277#ifdef HAVE_TLS
1278if (tls_on_connect)
1279 {
1280 printf("Attempting to start TLS\n");
1281
fc4fcc34 1282#ifdef HAVE_OPENSSL
7bbb3621 1283 srv.tls_active = tls_start(srv.sock, &srv.ssl, srv.ctx);
fc4fcc34 1284#endif
c55a77db 1285
fc4fcc34
JH
1286#ifdef HAVE_GNUTLS
1287 {
1288 int rc;
c55a77db
PH
1289 sigalrm_seen = FALSE;
1290 alarm(timeout);
fc4fcc34
JH
1291 do {
1292 rc = gnutls_handshake(tls_session);
1293 } while (rc < 0 && gnutls_error_is_fatal(rc) == 0);
7bbb3621 1294 srv.tls_active = rc >= 0;
c55a77db 1295 alarm(0);
fc4fcc34 1296
7bbb3621 1297 if (!srv.tls_active) printf("%s\n", gnutls_strerror(rc));
fc4fcc34
JH
1298 }
1299#endif
c55a77db 1300
7bbb3621 1301 if (!srv.tls_active)
c55a77db 1302 printf("Failed to start TLS\n");
fc4fcc34 1303#if defined(HAVE_GNUTLS) && defined(HAVE_OCSP)
2b4a568d
JH
1304 else if ( ocsp_stapling
1305 && gnutls_ocsp_status_request_is_checked(tls_session, 0) == 0)
1306 printf("Failed to verify certificate status\n");
fc4fcc34 1307#endif
c55a77db
PH
1308 else
1309 printf("Succeeded in starting TLS\n");
1310 }
1311#endif
1312
7bbb3621 1313do_file(&srv, stdin, timeout, inbuffer, sizeof(inbuffer), inptr);
c55a77db
PH
1314
1315printf("End of script\n");
7bbb3621 1316shutdown(srv.sock, SHUT_WR);
ce80533b
JH
1317if (fcntl(srv.sock, F_SETFL, O_NONBLOCK) == 0)
1318 while (read(srv.sock, inbuffer, sizeof(inbuffer)) > 0) ;
7bbb3621 1319close(srv.sock);
c55a77db
PH
1320
1321exit(0);
1322}
1323
1324/* End of client.c */