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