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