1 /* $Cambridge: exim/test/src/client.c,v 1.2 2006/10/16 13:42:19 ph10 Exp $ */
3 /* A little hacked up program that makes a TCP/IP call and reads a script to
4 drive it, for testing Exim server code running as a daemon. It's got a bit
5 messy with the addition of support for either OpenSSL or GnuTLS. The code for
6 those was hacked out of Exim itself. */
8 /* ANSI C standard includes */
23 #include <sys/types.h>
25 #include <netinet/in_systm.h>
26 #include <netinet/in.h>
27 #include <netinet/ip.h>
30 #include <arpa/inet.h>
32 #include <sys/resource.h>
33 #include <sys/socket.h>
44 #define S_ADDR_TYPE u_long
47 typedef unsigned char uschar;
50 #define US (unsigned char *)
57 static int sigalrm_seen = 0;
60 /* TLS support can be optionally included, either for OpenSSL or GnuTLS. The
61 latter needs a whole pile of tables. */
65 #include <openssl/crypto.h>
66 #include <openssl/x509.h>
67 #include <openssl/pem.h>
68 #include <openssl/ssl.h>
69 #include <openssl/err.h>
70 #include <openssl/rand.h>
76 #include <gnutls/gnutls.h>
77 #include <gnutls/x509.h>
81 /* Local static variables for GNUTLS */
83 static gnutls_dh_params dh_params = NULL;
85 static gnutls_certificate_credentials_t x509_cred = NULL;
86 static gnutls_session tls_session = NULL;
88 static int ssl_session_timeout = 200;
90 /* Priorities for TLS algorithms to use. */
92 static const int protocol_priority[16] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
94 static const int kx_priority[16] = {
100 static int default_cipher_priority[16] = {
101 GNUTLS_CIPHER_AES_256_CBC,
102 GNUTLS_CIPHER_AES_128_CBC,
103 GNUTLS_CIPHER_3DES_CBC,
104 GNUTLS_CIPHER_ARCFOUR_128,
107 static const int mac_priority[16] = {
112 static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 };
113 static const int cert_type_priority[16] = { GNUTLS_CRT_X509, 0 };
120 /*************************************************
121 * SIGALRM handler - crash out *
122 *************************************************/
125 sigalrm_handler_crash(int sig)
127 sig = sig; /* Keep picky compilers happy */
128 printf("\nClient timed out\n");
133 /*************************************************
134 * SIGALRM handler - set flag *
135 *************************************************/
138 sigalrm_handler_flag(int sig)
140 sig = sig; /* Keep picky compilers happy */
146 /****************************************************************************/
147 /****************************************************************************/
150 /*************************************************
151 * Start an OpenSSL TLS session *
152 *************************************************/
154 int tls_start(int sock, SSL **ssl, SSL_CTX *ctx)
157 static const char *sid_ctx = "exim";
159 RAND_load_file("client.c", -1); /* Not *very* random! */
161 *ssl = SSL_new (ctx);
162 SSL_set_session_id_context(*ssl, sid_ctx, strlen(sid_ctx));
163 SSL_set_fd (*ssl, sock);
164 SSL_set_connect_state(*ssl);
166 signal(SIGALRM, sigalrm_handler_flag);
169 rc = SSL_connect (*ssl);
174 printf("SSL_connect timed out\n");
180 ERR_print_errors_fp(stdout);
184 printf("SSL connection using %s\n", SSL_get_cipher (*ssl));
189 /*************************************************
190 * SSL Information callback *
191 *************************************************/
194 info_callback(SSL *s, int where, int ret)
198 printf("SSL info: %s\n", SSL_state_string_long(s));
203 /****************************************************************************/
204 /****************************************************************************/
208 /*************************************************
209 * Handle GnuTLS error *
210 *************************************************/
212 /* Called from lots of places when errors occur before actually starting to do
213 the TLS handshake, that is, while the session is still in clear.
217 err a GnuTLS error number, or 0 if local error
219 Returns: doesn't - it dies
223 gnutls_error(uschar *prefix, int err)
225 fprintf(stderr, "GnuTLS connection error: %s:", prefix);
226 if (err != 0) fprintf(stderr, " %s", gnutls_strerror(err));
227 fprintf(stderr, "\n");
233 /*************************************************
234 * Setup up DH parameters *
235 *************************************************/
237 /* For the test suite, the parameters should always be available in the spool
246 uschar filename[200];
249 /* Initialize the data structures for holding the parameters */
251 ret = gnutls_dh_params_init(&dh_params);
252 if (ret < 0) gnutls_error(US"init dh_params", ret);
254 /* Open the cache file for reading and if successful, read it and set up the
257 fd = open("aux-fixed/gnutls-params", O_RDONLY, 0);
260 fprintf(stderr, "Failed to open spool/gnutls-params: %s\n", strerror(errno));
264 if (fstat(fd, &statbuf) < 0)
267 return gnutls_error(US"TLS cache stat failed", 0);
270 m.size = statbuf.st_size;
271 m.data = malloc(m.size);
273 return gnutls_error(US"memory allocation failed", 0);
274 if (read(fd, m.data, m.size) != m.size)
275 return gnutls_error(US"TLS cache read failed", 0);
278 ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM);
279 if (ret < 0) return gnutls_error(US"DH params import", ret);
286 /*************************************************
287 * Initialize for GnuTLS *
288 *************************************************/
292 certificate certificate file
293 privatekey private key file
297 tls_init(uschar *certificate, uschar *privatekey)
301 rc = gnutls_global_init();
302 if (rc < 0) gnutls_error(US"gnutls_global_init", rc);
304 /* Read D-H parameters from the cache file. */
308 /* Create the credentials structure */
310 rc = gnutls_certificate_allocate_credentials(&x509_cred);
311 if (rc < 0) gnutls_error(US"certificate_allocate_credentials", rc);
313 /* Set the certificate and private keys */
315 if (certificate != NULL)
317 rc = gnutls_certificate_set_x509_key_file(x509_cred, CS certificate,
318 CS privatekey, GNUTLS_X509_FMT_PEM);
319 if (rc < 0) gnutls_error("gnutls_certificate", rc);
322 /* Associate the parameters with the x509 credentials structure. */
324 gnutls_certificate_set_dh_params(x509_cred, dh_params);
329 /*************************************************
330 * Initialize a single GNUTLS session *
331 *************************************************/
333 static gnutls_session
334 tls_session_init(void)
336 gnutls_session session;
338 gnutls_init(&session, GNUTLS_CLIENT);
340 gnutls_cipher_set_priority(session, default_cipher_priority);
341 gnutls_compression_set_priority(session, comp_priority);
342 gnutls_kx_set_priority(session, kx_priority);
343 gnutls_protocol_set_priority(session, protocol_priority);
344 gnutls_mac_set_priority(session, mac_priority);
346 gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
348 gnutls_dh_set_prime_bits(session, DH_BITS);
349 gnutls_db_set_cache_expiration(session, ssl_session_timeout);
356 /****************************************************************************/
357 /****************************************************************************/
362 /*************************************************
364 *************************************************/
369 [<outgoing interface>]
374 int main(int argc, char **argv)
376 struct sockaddr *s_ptr;
377 struct sockaddr_in s_in4;
378 char *interface = NULL;
379 char *address = NULL;
380 char *certfile = NULL;
381 char *keyfile = NULL;
383 int host_af, port, s_len, rc, sock, save_errno;
386 int sent_starttls = 0;
387 int tls_on_connect = 0;
390 struct sockaddr_in6 s_in6;
398 unsigned char outbuffer[10240];
399 unsigned char inbuffer[10240];
400 unsigned char *inptr = inbuffer;
402 *inptr = 0; /* Buffer empty */
406 while (argc >= argi + 1 && argv[argi][0] == '-')
408 if (strcmp(argv[argi], "-tls-on-connect") == 0)
413 else if (argv[argi][1] == 't' && isdigit(argv[argi][2]))
415 timeout = atoi(argv[argi]+1);
420 printf("Unrecognized option %s\n", argv[argi]);
425 /* Mandatory 1st arg is IP address */
429 printf("No IP address given\n");
433 address = argv[argi++];
434 host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET;
436 /* Mandatory 2nd arg is port */
440 printf("No port number given\n");
444 port = atoi(argv[argi++]);
446 /* Optional next arg is interface */
449 (isdigit((unsigned char)argv[argi][0]) || argv[argi][0] == ':'))
450 interface = argv[argi++];
452 /* Any more arguments are the name of a certificate file and key file */
454 if (argc > argi) certfile = argv[argi++];
455 if (argc > argi) keyfile = argv[argi++];
459 /* For an IPv6 address, use an IPv6 sockaddr structure. */
461 if (host_af == AF_INET6)
463 s_ptr = (struct sockaddr *)&s_in6;
464 s_len = sizeof(s_in6);
469 /* For an IPv4 address, use an IPv4 sockaddr structure,
470 even on an IPv6 system. */
473 s_ptr = (struct sockaddr *)&s_in4;
474 s_len = sizeof(s_in4);
477 printf("Connecting to %s port %d ... ", address, port);
479 sock = socket(host_af, SOCK_STREAM, 0);
482 printf("socket creation failed: %s\n", strerror(errno));
486 /* Bind to a specific interface if requested. On an IPv6 system, this has
487 to be of the same family as the address we are calling. On an IPv4 system the
488 test is redundant, but it keeps the code tidier. */
490 if (interface != NULL)
492 int interface_af = (strchr(interface, ':') != NULL)? AF_INET6 : AF_INET;
494 if (interface_af == host_af)
498 /* Set up for IPv6 binding */
500 if (host_af == AF_INET6)
502 memset(&s_in6, 0, sizeof(s_in6));
503 s_in6.sin6_family = AF_INET6;
505 if (inet_pton(AF_INET6, interface, &s_in6.sin6_addr) != 1)
507 printf("Unable to parse \"%s\"", interface);
514 /* Set up for IPv4 binding */
517 memset(&s_in4, 0, sizeof(s_in4));
518 s_in4.sin_family = AF_INET;
520 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(interface);
525 if (bind(sock, s_ptr, s_len) < 0)
527 printf("Unable to bind outgoing SMTP call to %s: %s",
528 interface, strerror(errno));
534 /* Set up a remote IPv6 address */
537 if (host_af == AF_INET6)
539 memset(&s_in6, 0, sizeof(s_in6));
540 s_in6.sin6_family = AF_INET6;
541 s_in6.sin6_port = htons(port);
542 if (inet_pton(host_af, address, &s_in6.sin6_addr) != 1)
544 printf("Unable to parse \"%s\"", address);
551 /* Set up a remote IPv4 address */
554 memset(&s_in4, 0, sizeof(s_in4));
555 s_in4.sin_family = AF_INET;
556 s_in4.sin_port = htons(port);
557 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(address);
560 /* SIGALRM handler crashes out */
562 signal(SIGALRM, sigalrm_handler_crash);
564 rc = connect(sock, s_ptr, s_len);
568 /* A failure whose error code is "Interrupted system call" is in fact
569 an externally applied timeout if the signal handler has been run. */
574 printf("failed: %s\n", strerror(save_errno));
578 printf("connected\n");
581 /* --------------- Set up for OpenSSL --------------- */
585 SSL_load_error_strings();
587 ctx = SSL_CTX_new(SSLv23_method());
590 printf ("SSL_CTX_new failed\n");
594 if (certfile != NULL)
596 if (!SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM))
598 printf("SSL_CTX_use_certificate_file failed\n");
601 printf("Certificate file = %s\n", certfile);
606 if (!SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))
608 printf("SSL_CTX_use_PrivateKey_file failed\n");
611 printf("Key file = %s\n", keyfile);
614 SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
615 SSL_CTX_set_timeout(ctx, 200);
616 SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
620 /* --------------- Set up for GnuTLS --------------- */
623 if (certfile != NULL) printf("Certificate file = %s\n", certfile);
624 if (keyfile != NULL) printf("Key file = %s\n", keyfile);
625 tls_init(certfile, keyfile);
626 tls_session = tls_session_init();
627 gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)sock);
629 /* When the server asks for a certificate and the client does not have one,
630 there is a SIGPIPE error in the gnutls_handshake() function for some reason
631 that is not understood. As luck would have it, this has never hit Exim itself
632 because it ignores SIGPIPE errors. Doing the same here allows it all to work as
635 signal(SIGPIPE, SIG_IGN);
638 /* ---------------------------------------------- */
641 /* Start TLS session if configured to do so without STARTTLS */
646 printf("Attempting to start TLS\n");
649 tls_active = tls_start(sock, &ssl, ctx);
653 sigalrm_seen = FALSE;
655 tls_active = gnutls_handshake(tls_session) >= 0;
660 printf("Failed to start TLS\n");
662 printf("Succeeded in starting TLS\n");
666 while (fgets(outbuffer, sizeof(outbuffer), stdin) != NULL)
668 int n = (int)strlen(outbuffer);
669 while (n > 0 && isspace(outbuffer[n-1])) n--;
672 /* Expect incoming */
674 if (strncmp(outbuffer, "??? ", 4) == 0)
676 unsigned char *lineptr;
677 printf("%s\n", outbuffer);
679 if (*inptr == 0) /* Refill input buffer */
684 rc = SSL_read (ssl, inbuffer, sizeof(inbuffer) - 1);
687 rc = gnutls_record_recv(tls_session, CS inbuffer, sizeof(inbuffer) - 1);
693 rc = read(sock, inbuffer, sizeof(inbuffer));
699 printf("Read error %s\n", strerror(errno));
704 printf("Unexpected EOF read\n");
716 while (*inptr != 0 && *inptr != '\r' && *inptr != '\n') inptr++;
720 if (*inptr == '\n') inptr++;
723 printf("<<< %s\n", lineptr);
724 if (strncmp(lineptr, outbuffer + 4, (int)strlen(outbuffer) - 4) != 0)
726 printf("\n******** Input mismatch ********\n");
733 if (lineptr[0] == '2')
735 printf("Attempting to start TLS\n");
739 tls_active = tls_start(sock, &ssl, ctx);
743 sigalrm_seen = FALSE;
745 tls_active = gnutls_handshake(tls_session) >= 0;
751 printf("Failed to start TLS\n");
755 printf("Succeeded in starting TLS\n");
757 else printf("Abandoning TLS start attempt\n");
763 /* Wait for a bit before proceeding */
765 else if (strncmp(outbuffer, "+++ ", 4) == 0)
767 printf("%s\n", outbuffer);
768 sleep(atoi(outbuffer + 4));
771 /* Send outgoing, but barf if unconsumed incoming */
775 unsigned char *escape;
779 printf("Unconsumed input: %s", inptr);
780 printf(" About to send: %s\n", outbuffer);
788 if (strcmp(outbuffer, "stoptls") == 0 ||
789 strcmp(outbuffer, "STOPTLS") == 0)
793 printf("STOPTLS read when TLS not active\n");
796 printf("Shutting down TLS encryption\n");
804 gnutls_bye(tls_session, GNUTLS_SHUT_WR);
805 gnutls_deinit(tls_session);
807 gnutls_global_deinit();
814 /* Remember that we sent STARTTLS */
816 sent_starttls = (strcmp(outbuffer, "starttls") == 0 ||
817 strcmp(outbuffer, "STARTTLS") == 0);
819 /* Fudge: if the command is "starttls_wait", we send the starttls bit,
820 but we haven't set the flag, so that there is no negotiation. This is for
821 testing the server's timeout. */
823 if (strcmp(outbuffer, "starttls_wait") == 0)
830 printf(">>> %s\n", outbuffer);
831 strcpy(outbuffer + n, "\r\n");
833 /* Turn "\n" and "\r" into the relevant characters. This is a hack. */
835 while ((escape = strstr(outbuffer, "\\r")) != NULL)
838 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
842 while ((escape = strstr(outbuffer, "\\n")) != NULL)
845 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
855 rc = SSL_write (ssl, outbuffer, n + 2);
858 rc = gnutls_record_send(tls_session, CS outbuffer, n + 2);
861 printf("GnuTLS write error: %s\n", gnutls_strerror(rc));
868 rc = write(sock, outbuffer, n + 2);
874 printf("Write error: %s\n", strerror(errno));
880 printf("End of script\n");
886 /* End of client.c */