Testsuite: sys_errlist[] not supported on all platforms
[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 517/****************************************************************************/
7bbb3621
JH
518typedef struct {
519 int sock;
520 int tls_active;
521#ifdef HAVE_OPENSSL
522 SSL_CTX * ctx;
523 SSL * ssl;
524#endif
525 int sent_starttls;
526} srv_ctx;
527
528static void
529do_file(srv_ctx * srv, FILE * f, int timeout,
530 unsigned char * inbuffer, unsigned bsiz, unsigned char * inptr)
531{
b9df1829 532unsigned char outbuffer[1024 * 20];
7bbb3621
JH
533
534while (fgets(CS outbuffer, sizeof(outbuffer), f) != NULL)
535 {
536 int n = (int)strlen(CS outbuffer);
537 int crlf = 1;
538 int rc;
539
540 /* Strip trailing newline */
541 if (outbuffer[n-1] == '\n') outbuffer[--n] = 0;
542
543 /* Expect incoming */
544
545 if ( strncmp(CS outbuffer, "???", 3) == 0
546 && (outbuffer[3] == ' ' || outbuffer[3] == '*')
547 )
548 {
549 unsigned char *lineptr;
550 unsigned exp_eof = outbuffer[3] == '*';
551
552 printf("%s\n", outbuffer);
553 n = unescape_buf(outbuffer, n);
554
555 if (*inptr == 0) /* Refill input buffer */
556 {
557 if (srv->tls_active)
558 {
559 #ifdef HAVE_OPENSSL
560 rc = SSL_read (srv->ssl, inbuffer, bsiz - 1);
561 #endif
562 #ifdef HAVE_GNUTLS
563 rc = gnutls_record_recv(tls_session, CS inbuffer, bsiz - 1);
564 #endif
565 }
566 else
567 {
568 alarm(timeout);
569 rc = read(srv->sock, inbuffer, bsiz);
570 alarm(0);
571 }
572
573 if (rc < 0)
574 {
575 printf("Read error %s\n", strerror(errno));
576 exit(81);
577 }
578 else if (rc == 0)
579 if (exp_eof)
580 {
581 printf("Expected EOF read\n");
582 continue;
583 }
584 else
585 {
586 printf("Unexpected EOF read\n");
587 close(srv->sock);
588 exit(80);
589 }
590 else if (exp_eof)
591 {
592 printf("Expected EOF not read\n");
593 close(srv->sock);
594 exit(74);
595 }
596 else
597 {
598 inbuffer[rc] = 0;
599 inptr = inbuffer;
600 }
601 }
602
603 lineptr = inptr;
604 while (*inptr != 0 && *inptr != '\r' && *inptr != '\n') inptr++;
605 if (*inptr != 0)
606 {
607 *inptr++ = 0;
608 if (*inptr == '\n') inptr++;
609 }
610
611 printf("<<< %s\n", lineptr);
612 if (strncmp(CS lineptr, CS outbuffer + 4, n - 4) != 0)
613 {
614 printf("\n******** Input mismatch ********\n");
615 exit(79);
616 }
617
618 #ifdef HAVE_TLS
619 if (srv->sent_starttls)
620 {
621 if (lineptr[0] == '2')
622 {
623int rc;
624 unsigned int verify;
625
626 printf("Attempting to start TLS\n");
627 fflush(stdout);
628
629 #ifdef HAVE_OPENSSL
630 srv->tls_active = tls_start(srv->sock, &srv->ssl, srv->ctx);
631 #endif
632
633 #ifdef HAVE_GNUTLS
634 {
635 int rc;
636 sigalrm_seen = FALSE;
637 alarm(timeout);
638 do {
639 rc = gnutls_handshake(tls_session);
640 } while (rc < 0 && gnutls_error_is_fatal(rc) == 0);
641 srv->tls_active = rc >= 0;
642 alarm(0);
643
644 if (!srv->tls_active) printf("%s\n", gnutls_strerror(rc));
645 }
646 #endif
647
648 if (!srv->tls_active)
649 {
650 printf("Failed to start TLS\n");
651 fflush(stdout);
652 }
653 #ifdef HAVE_GNUTLS
654 else if (ocsp_stapling)
655 {
656 if ((rc= gnutls_certificate_verify_peers2(tls_session, &verify)) < 0)
657 {
658 printf("Failed to verify certificate: %s\n", gnutls_strerror(rc));
659 fflush(stdout);
660 }
661 else if (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED))
662 {
663 printf("Bad certificate\n");
664 fflush(stdout);
665 }
666 #ifdef HAVE_OCSP
667 else if (gnutls_ocsp_status_request_is_checked(tls_session, 0) == 0)
668 {
669 printf("Failed to verify certificate status\n");
670 {
671 gnutls_datum_t stapling;
672 gnutls_ocsp_resp_t resp;
673 gnutls_datum_t printed;
674 if ( (rc= gnutls_ocsp_status_request_get(tls_session, &stapling)) == 0
675 && (rc= gnutls_ocsp_resp_init(&resp)) == 0
676 && (rc= gnutls_ocsp_resp_import(resp, &stapling)) == 0
677 && (rc= gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &printed)) == 0
678 )
679 {
680 fprintf(stderr, "%.4096s", printed.data);
681 gnutls_free(printed.data);
682 }
683 else
684 (void) fprintf(stderr,"ocsp decode: %s", gnutls_strerror(rc));
685 }
686 fflush(stdout);
687 }
688 #endif
689 }
690 #endif
691 else
692 printf("Succeeded in starting TLS\n");
693 }
694 else printf("Abandoning TLS start attempt\n");
695 }
696 srv->sent_starttls = 0;
697 #endif
698 }
699
700 /* Wait for a bit before proceeding */
701
702 else if (strncmp(CS outbuffer, "+++ ", 4) == 0)
703 {
704 printf("%s\n", outbuffer);
705 sleep(atoi(CS outbuffer + 4));
706 }
707
708 /* Stack new input file */
709
710 else if (strncmp(CS outbuffer, "<<< ", 4) == 0)
711 {
712 FILE * new_f;
713 if (!(new_f = fopen(outbuffer+4 , "r")))
714 {
768e8b5b 715 printf("Unable to open '%s': %s", inptr, strerror(errno));
7bbb3621
JH
716 exit(74);
717 }
718 do_file(srv, new_f, timeout, inbuffer, bsiz, inptr);
719 }
720
721
722 /* Send line outgoing, but barf if unconsumed incoming */
723
724 else
725 {
726 unsigned char * out = outbuffer;
727
728 if (strncmp(CS outbuffer, ">>> ", 4) == 0)
729 {
730 crlf = 0;
731 out += 4;
732 n -= 4;
733 }
734
735 if (*inptr != 0)
736 {
737 printf("Unconsumed input: %s", inptr);
738 printf(" About to send: %s\n", out);
739 exit(78);
740 }
741
742 #ifdef HAVE_TLS
743
744 /* Shutdown TLS */
745
746 if (strcmp(CS out, "stoptls") == 0 ||
747 strcmp(CS out, "STOPTLS") == 0)
748 {
749 if (!srv->tls_active)
750 {
751 printf("STOPTLS read when TLS not active\n");
752 exit(77);
753 }
754 printf("Shutting down TLS encryption\n");
755
756 #ifdef HAVE_OPENSSL
757 SSL_shutdown(srv->ssl);
758 SSL_free(srv->ssl);
759 #endif
760
761 #ifdef HAVE_GNUTLS
762 gnutls_bye(tls_session, GNUTLS_SHUT_WR);
763 gnutls_deinit(tls_session);
764 tls_session = NULL;
765 gnutls_global_deinit();
766 #endif
767
768 srv->tls_active = 0;
769 continue;
770 }
771
772 /* Remember that we sent STARTTLS */
773
774 srv->sent_starttls = (strcmp(CS out, "starttls") == 0 ||
775 strcmp(CS out, "STARTTLS") == 0);
776
777 /* Fudge: if the command is "starttls_wait", we send the starttls bit,
778 but we haven't set the flag, so that there is no negotiation. This is for
779 testing the server's timeout. */
780
781 if (strcmp(CS out, "starttls_wait") == 0)
782 {
783 out[8] = 0;
784 n = 8;
785 }
786 #endif
787
788 printf(">>> %s\n", out);
789 if (crlf)
790 {
791 strcpy(CS out + n, "\r\n");
792 n += 2;
793 }
794
795 n = unescape_buf(out, n);
796
797 /* OK, do it */
798
799 alarm(timeout);
800 if (srv->tls_active)
801 {
802 #ifdef HAVE_OPENSSL
803 rc = SSL_write (srv->ssl, out, n);
804 #endif
805 #ifdef HAVE_GNUTLS
806 if ((rc = gnutls_record_send(tls_session, CS out, n)) < 0)
807 {
808 printf("GnuTLS write error: %s\n", gnutls_strerror(rc));
809 exit(76);
810 }
811 #endif
812 }
813 else
814 rc = write(srv->sock, out, n);
815 alarm(0);
816
817 if (rc < 0)
818 {
819 printf("Write error: %s\n", strerror(errno));
820 exit(75);
821 }
822 }
823 }
824}
c55a77db
PH
825
826
827
828
829/*************************************************
830* Main Program *
831*************************************************/
832
4fe99a6c 833const char * const HELP_MESSAGE = "\n\
972e83f5
HSHR
834Usage: client\n"
835#ifdef HAVE_TLS
836"\
837 [-tls-on-connect]\n\
838 [-ocsp]\n"
839#endif
840"\
841 [-tn] n seconds timeout\n\
4fe99a6c
PP
842 <IP address>\n\
843 <port>\n\
844 [<outgoing interface>]\n\
845 [<cert file>]\n\
846 [<key file>]\n\
847\n";
c55a77db 848
5d036699
JH
849int
850main(int argc, char **argv)
c55a77db
PH
851{
852struct sockaddr *s_ptr;
853struct sockaddr_in s_in4;
854char *interface = NULL;
855char *address = NULL;
856char *certfile = NULL;
857char *keyfile = NULL;
1ec3f27d 858char *end = NULL;
c55a77db 859int argi = 1;
7bbb3621 860int host_af, port, s_len, rc, save_errno;
d528a389 861int timeout = 5;
c55a77db 862int tls_on_connect = 0;
1ec3f27d 863long tmplong;
c55a77db
PH
864
865#if HAVE_IPV6
866struct sockaddr_in6 s_in6;
867#endif
868
7bbb3621 869srv_ctx srv;
c55a77db 870
c55a77db
PH
871unsigned char inbuffer[10240];
872unsigned char *inptr = inbuffer;
873
874*inptr = 0; /* Buffer empty */
7bbb3621
JH
875srv.tls_active = 0;
876srv.sent_starttls = 0;
c55a77db
PH
877
878/* Options */
879
880while (argc >= argi + 1 && argv[argi][0] == '-')
881 {
4fe99a6c
PP
882 if (strcmp(argv[argi], "-help") == 0 ||
883 strcmp(argv[argi], "--help") == 0 ||
884 strcmp(argv[argi], "-h") == 0)
885 {
dd4c8678 886 puts(HELP_MESSAGE);
4fe99a6c
PP
887 exit(0);
888 }
c55a77db
PH
889 if (strcmp(argv[argi], "-tls-on-connect") == 0)
890 {
891 tls_on_connect = 1;
892 argi++;
893 }
2b4a568d 894#ifdef HAVE_TLS
f5d78688
JH
895 else if (strcmp(argv[argi], "-ocsp") == 0)
896 {
897 if (argc < ++argi + 1)
898 {
899 fprintf(stderr, "Missing required certificate file for ocsp option\n");
9ff403f8 900 exit(96);
f5d78688
JH
901 }
902 ocsp_stapling = argv[argi++];
903 }
2b4a568d 904
f5d78688 905#endif
c55a77db
PH
906 else if (argv[argi][1] == 't' && isdigit(argv[argi][2]))
907 {
1ec3f27d
PP
908 tmplong = strtol(argv[argi]+2, &end, 10);
909 if (end == argv[argi]+2 || *end)
910 {
911 fprintf(stderr, "Failed to parse seconds from option <%s>\n",
912 argv[argi]);
9ff403f8 913 exit(95);
1ec3f27d
PP
914 }
915 if (tmplong > 10000L)
916 {
dd4c8678 917 fprintf(stderr, "Unreasonably long wait of %ld seconds requested\n",
1ec3f27d 918 tmplong);
9ff403f8 919 exit(94);
1ec3f27d
PP
920 }
921 if (tmplong < 0L)
922 {
dd4c8678 923 fprintf(stderr, "Timeout must not be negative (%ld)\n", tmplong);
9ff403f8 924 exit(93);
1ec3f27d
PP
925 }
926 timeout = (int) tmplong;
c55a77db
PH
927 argi++;
928 }
929 else
930 {
1ec3f27d 931 fprintf(stderr, "Unrecognized option %s\n", argv[argi]);
9ff403f8 932 exit(92);
c55a77db
PH
933 }
934 }
935
936/* Mandatory 1st arg is IP address */
937
938if (argc < argi+1)
939 {
1ec3f27d 940 fprintf(stderr, "No IP address given\n");
9ff403f8 941 exit(91);
c55a77db
PH
942 }
943
944address = argv[argi++];
945host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET;
946
947/* Mandatory 2nd arg is port */
948
949if (argc < argi+1)
950 {
1ec3f27d 951 fprintf(stderr, "No port number given\n");
9ff403f8 952 exit(90);
c55a77db
PH
953 }
954
955port = atoi(argv[argi++]);
956
957/* Optional next arg is interface */
958
959if (argc > argi &&
960 (isdigit((unsigned char)argv[argi][0]) || argv[argi][0] == ':'))
961 interface = argv[argi++];
962
963/* Any more arguments are the name of a certificate file and key file */
964
965if (argc > argi) certfile = argv[argi++];
966if (argc > argi) keyfile = argv[argi++];
967
968
969#if HAVE_IPV6
970/* For an IPv6 address, use an IPv6 sockaddr structure. */
971
972if (host_af == AF_INET6)
973 {
974 s_ptr = (struct sockaddr *)&s_in6;
975 s_len = sizeof(s_in6);
976 }
977else
978#endif
979
980/* For an IPv4 address, use an IPv4 sockaddr structure,
981even on an IPv6 system. */
982
983 {
984 s_ptr = (struct sockaddr *)&s_in4;
985 s_len = sizeof(s_in4);
986 }
987
988printf("Connecting to %s port %d ... ", address, port);
989
7bbb3621
JH
990srv.sock = socket(host_af, SOCK_STREAM, 0);
991if (srv.sock < 0)
c55a77db
PH
992 {
993 printf("socket creation failed: %s\n", strerror(errno));
9ff403f8 994 exit(89);
c55a77db
PH
995 }
996
997/* Bind to a specific interface if requested. On an IPv6 system, this has
998to be of the same family as the address we are calling. On an IPv4 system the
999test is redundant, but it keeps the code tidier. */
1000
1001if (interface != NULL)
1002 {
1003 int interface_af = (strchr(interface, ':') != NULL)? AF_INET6 : AF_INET;
1004
1005 if (interface_af == host_af)
1006 {
1007 #if HAVE_IPV6
1008
1009 /* Set up for IPv6 binding */
1010
1011 if (host_af == AF_INET6)
1012 {
1013 memset(&s_in6, 0, sizeof(s_in6));
1014 s_in6.sin6_family = AF_INET6;
1015 s_in6.sin6_port = 0;
1016 if (inet_pton(AF_INET6, interface, &s_in6.sin6_addr) != 1)
1017 {
1018 printf("Unable to parse \"%s\"", interface);
9ff403f8 1019 exit(88);
c55a77db
PH
1020 }
1021 }
1022 else
1023 #endif
1024
1025 /* Set up for IPv4 binding */
1026
1027 {
1028 memset(&s_in4, 0, sizeof(s_in4));
1029 s_in4.sin_family = AF_INET;
1030 s_in4.sin_port = 0;
1031 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(interface);
1032 }
1033
1034 /* Bind */
1035
7bbb3621 1036 if (bind(srv.sock, s_ptr, s_len) < 0)
c55a77db
PH
1037 {
1038 printf("Unable to bind outgoing SMTP call to %s: %s",
1039 interface, strerror(errno));
9ff403f8 1040 exit(87);
c55a77db
PH
1041 }
1042 }
1043 }
1044
1045/* Set up a remote IPv6 address */
1046
1047#if HAVE_IPV6
1048if (host_af == AF_INET6)
1049 {
1050 memset(&s_in6, 0, sizeof(s_in6));
1051 s_in6.sin6_family = AF_INET6;
1052 s_in6.sin6_port = htons(port);
1053 if (inet_pton(host_af, address, &s_in6.sin6_addr) != 1)
1054 {
1055 printf("Unable to parse \"%s\"", address);
9ff403f8 1056 exit(86);
c55a77db
PH
1057 }
1058 }
1059else
1060#endif
1061
1062/* Set up a remote IPv4 address */
1063
1064 {
1065 memset(&s_in4, 0, sizeof(s_in4));
1066 s_in4.sin_family = AF_INET;
1067 s_in4.sin_port = htons(port);
1068 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(address);
1069 }
1070
1071/* SIGALRM handler crashes out */
1072
1073signal(SIGALRM, sigalrm_handler_crash);
1074alarm(timeout);
7bbb3621 1075rc = connect(srv.sock, s_ptr, s_len);
c55a77db
PH
1076save_errno = errno;
1077alarm(0);
1078
1079/* A failure whose error code is "Interrupted system call" is in fact
1080an externally applied timeout if the signal handler has been run. */
1081
1082if (rc < 0)
1083 {
7bbb3621 1084 close(srv.sock);
41fdef91 1085 printf("connect failed: %s\n", strerror(save_errno));
9ff403f8 1086 exit(85);
c55a77db
PH
1087 }
1088
1089printf("connected\n");
1090
1091
1092/* --------------- Set up for OpenSSL --------------- */
1093
1094#ifdef HAVE_OPENSSL
1095SSL_library_init();
1096SSL_load_error_strings();
1097
7bbb3621 1098if (!(srv.ctx = SSL_CTX_new(SSLv23_method())))
c55a77db
PH
1099 {
1100 printf ("SSL_CTX_new failed\n");
9ff403f8 1101 exit(84);
c55a77db
PH
1102 }
1103
7bbb3621 1104if (certfile)
c55a77db 1105 {
7bbb3621 1106 if (!SSL_CTX_use_certificate_file(srv.ctx, certfile, SSL_FILETYPE_PEM))
c55a77db
PH
1107 {
1108 printf("SSL_CTX_use_certificate_file failed\n");
9ff403f8 1109 exit(83);
c55a77db
PH
1110 }
1111 printf("Certificate file = %s\n", certfile);
1112 }
1113
7bbb3621 1114if (keyfile)
c55a77db 1115 {
7bbb3621 1116 if (!SSL_CTX_use_PrivateKey_file(srv.ctx, keyfile, SSL_FILETYPE_PEM))
c55a77db
PH
1117 {
1118 printf("SSL_CTX_use_PrivateKey_file failed\n");
9ff403f8 1119 exit(82);
c55a77db
PH
1120 }
1121 printf("Key file = %s\n", keyfile);
1122 }
1123
7bbb3621
JH
1124SSL_CTX_set_session_cache_mode(srv.ctx, SSL_SESS_CACHE_BOTH);
1125SSL_CTX_set_timeout(srv.ctx, 200);
1126SSL_CTX_set_info_callback(srv.ctx, (void (*)())info_callback);
c55a77db
PH
1127#endif
1128
1129
1130/* --------------- Set up for GnuTLS --------------- */
1131
1132#ifdef HAVE_GNUTLS
1133if (certfile != NULL) printf("Certificate file = %s\n", certfile);
1134if (keyfile != NULL) printf("Key file = %s\n", keyfile);
1135tls_init(certfile, keyfile);
1136tls_session = tls_session_init();
348051ad 1137#ifdef HAVE_OCSP
2b4a568d
JH
1138if (ocsp_stapling)
1139 gnutls_ocsp_status_request_enable_client(tls_session, NULL, 0, NULL);
348051ad 1140#endif
7bbb3621 1141gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr_t)(intptr_t)srv.sock);
c55a77db
PH
1142
1143/* When the server asks for a certificate and the client does not have one,
1144there is a SIGPIPE error in the gnutls_handshake() function for some reason
1145that is not understood. As luck would have it, this has never hit Exim itself
1146because it ignores SIGPIPE errors. Doing the same here allows it all to work as
1147one wants. */
1148
1149signal(SIGPIPE, SIG_IGN);
1150#endif
1151
1152/* ---------------------------------------------- */
1153
1154
1155/* Start TLS session if configured to do so without STARTTLS */
1156
1157#ifdef HAVE_TLS
1158if (tls_on_connect)
1159 {
1160 printf("Attempting to start TLS\n");
1161
fc4fcc34 1162#ifdef HAVE_OPENSSL
7bbb3621 1163 srv.tls_active = tls_start(srv.sock, &srv.ssl, srv.ctx);
fc4fcc34 1164#endif
c55a77db 1165
fc4fcc34
JH
1166#ifdef HAVE_GNUTLS
1167 {
1168 int rc;
c55a77db
PH
1169 sigalrm_seen = FALSE;
1170 alarm(timeout);
fc4fcc34
JH
1171 do {
1172 rc = gnutls_handshake(tls_session);
1173 } while (rc < 0 && gnutls_error_is_fatal(rc) == 0);
7bbb3621 1174 srv.tls_active = rc >= 0;
c55a77db 1175 alarm(0);
fc4fcc34 1176
7bbb3621 1177 if (!srv.tls_active) printf("%s\n", gnutls_strerror(rc));
fc4fcc34
JH
1178 }
1179#endif
c55a77db 1180
7bbb3621 1181 if (!srv.tls_active)
c55a77db 1182 printf("Failed to start TLS\n");
fc4fcc34 1183#if defined(HAVE_GNUTLS) && defined(HAVE_OCSP)
2b4a568d
JH
1184 else if ( ocsp_stapling
1185 && gnutls_ocsp_status_request_is_checked(tls_session, 0) == 0)
1186 printf("Failed to verify certificate status\n");
fc4fcc34 1187#endif
c55a77db
PH
1188 else
1189 printf("Succeeded in starting TLS\n");
1190 }
1191#endif
1192
7bbb3621 1193do_file(&srv, stdin, timeout, inbuffer, sizeof(inbuffer), inptr);
c55a77db
PH
1194
1195printf("End of script\n");
7bbb3621
JH
1196shutdown(srv.sock, SHUT_WR);
1197while (read(srv.sock, inbuffer, sizeof(inbuffer)) > 0) ;
1198close(srv.sock);
c55a77db
PH
1199
1200exit(0);
1201}
1202
1203/* End of client.c */