1 /* $Cambridge: exim/test/src/client.c,v 1.1 2006/02/06 16:24:05 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>
82 /* Local static variables for GNUTLS */
84 static gnutls_rsa_params rsa_params = NULL;
85 static gnutls_dh_params dh_params = NULL;
87 static gnutls_certificate_credentials_t x509_cred = NULL;
88 static gnutls_session tls_session = NULL;
90 static int ssl_session_timeout = 200;
92 /* Priorities for TLS algorithms to use. */
94 static const int protocol_priority[16] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
96 static const int kx_priority[16] = {
100 GNUTLS_KX_RSA_EXPORT,
103 static int default_cipher_priority[16] = {
104 GNUTLS_CIPHER_AES_256_CBC,
105 GNUTLS_CIPHER_AES_128_CBC,
106 GNUTLS_CIPHER_3DES_CBC,
107 GNUTLS_CIPHER_ARCFOUR_128,
110 static const int mac_priority[16] = {
115 static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 };
116 static const int cert_type_priority[16] = { GNUTLS_CRT_X509, 0 };
123 /*************************************************
124 * SIGALRM handler - crash out *
125 *************************************************/
128 sigalrm_handler_crash(int sig)
130 sig = sig; /* Keep picky compilers happy */
131 printf("\nClient timed out\n");
136 /*************************************************
137 * SIGALRM handler - set flag *
138 *************************************************/
141 sigalrm_handler_flag(int sig)
143 sig = sig; /* Keep picky compilers happy */
149 /****************************************************************************/
150 /****************************************************************************/
153 /*************************************************
154 * Start an OpenSSL TLS session *
155 *************************************************/
157 int tls_start(int sock, SSL **ssl, SSL_CTX *ctx)
160 static const char *sid_ctx = "exim";
162 RAND_load_file("client.c", -1); /* Not *very* random! */
164 *ssl = SSL_new (ctx);
165 SSL_set_session_id_context(*ssl, sid_ctx, strlen(sid_ctx));
166 SSL_set_fd (*ssl, sock);
167 SSL_set_connect_state(*ssl);
169 signal(SIGALRM, sigalrm_handler_flag);
172 rc = SSL_connect (*ssl);
177 printf("SSL_connect timed out\n");
183 ERR_print_errors_fp(stdout);
187 printf("SSL connection using %s\n", SSL_get_cipher (*ssl));
192 /*************************************************
193 * SSL Information callback *
194 *************************************************/
197 info_callback(SSL *s, int where, int ret)
201 printf("SSL info: %s\n", SSL_state_string_long(s));
206 /****************************************************************************/
207 /****************************************************************************/
211 /*************************************************
212 * Handle GnuTLS error *
213 *************************************************/
215 /* Called from lots of places when errors occur before actually starting to do
216 the TLS handshake, that is, while the session is still in clear.
220 err a GnuTLS error number, or 0 if local error
222 Returns: doesn't - it dies
226 gnutls_error(uschar *prefix, int err)
228 fprintf(stderr, "GnuTLS connection error:%s", prefix);
229 if (err != 0) fprintf(stderr, " %s", gnutls_strerror(err));
230 fprintf(stderr, "\n");
236 /*************************************************
237 * Setup up RSA and DH parameters *
238 *************************************************/
240 /* For the test suite, the parameters should always be available in the spool
249 uschar filename[200];
252 /* Initialize the data structures for holding the parameters */
254 ret = gnutls_rsa_params_init(&rsa_params);
255 if (ret < 0) gnutls_error(US"init rsa_params", ret);
257 ret = gnutls_dh_params_init(&dh_params);
258 if (ret < 0) gnutls_error(US"init dh_params", ret);
260 /* Open the cache file for reading and if successful, read it and set up the
261 parameters. If we can't set up the RSA parameters, assume that we are dealing
262 with an old-style cache file that is in another format, and fall through to
263 compute new values. However, if we correctly get RSA parameters, a failure to
264 set up D-H parameters is treated as an error. */
266 fd = open("aux-fixed/gnutls-params", O_RDONLY, 0);
269 fprintf(stderr, "Failed to open spool/gnutls-params: %s\n", strerror(errno));
273 if (fstat(fd, &statbuf) < 0)
276 return gnutls_error(US"TLS cache stat failed", 0);
279 m.size = statbuf.st_size;
280 m.data = malloc(m.size);
282 return gnutls_error(US"memory allocation failed", 0);
283 if (read(fd, m.data, m.size) != m.size)
284 return gnutls_error(US"TLS cache read failed", 0);
287 ret = gnutls_rsa_params_import_pkcs1(rsa_params, &m, GNUTLS_X509_FMT_PEM);
288 if (ret < 0) return gnutls_error(US"RSA params import", ret);
289 ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM);
290 if (ret < 0) return gnutls_error(US"DH params import", ret);
297 /*************************************************
298 * Initialize for GnuTLS *
299 *************************************************/
303 certificate certificate file
304 privatekey private key file
308 tls_init(uschar *certificate, uschar *privatekey)
312 rc = gnutls_global_init();
313 if (rc < 0) gnutls_error(US"gnutls_global_init", rc);
315 /* Read RSA and D-H parameters from the cache file. */
319 /* Create the credentials structure */
321 rc = gnutls_certificate_allocate_credentials(&x509_cred);
322 if (rc < 0) gnutls_error(US"certificate_allocate_credentials", rc);
324 /* Set the certificate and private keys */
326 if (certificate != NULL)
328 rc = gnutls_certificate_set_x509_key_file(x509_cred, CS certificate,
329 CS privatekey, GNUTLS_X509_FMT_PEM);
330 if (rc < 0) gnutls_error("gnutls_certificate", rc);
333 /* Associate the parameters with the x509 credentials structure. */
335 gnutls_certificate_set_dh_params(x509_cred, dh_params);
336 gnutls_certificate_set_rsa_export_params(x509_cred, rsa_params);
341 /*************************************************
342 * Initialize a single GNUTLS session *
343 *************************************************/
345 static gnutls_session
346 tls_session_init(void)
348 gnutls_session session;
350 gnutls_init(&session, GNUTLS_CLIENT);
352 gnutls_cipher_set_priority(session, default_cipher_priority);
353 gnutls_compression_set_priority(session, comp_priority);
354 gnutls_kx_set_priority(session, kx_priority);
355 gnutls_protocol_set_priority(session, protocol_priority);
356 gnutls_mac_set_priority(session, mac_priority);
358 gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
360 gnutls_dh_set_prime_bits(session, DH_BITS);
361 gnutls_db_set_cache_expiration(session, ssl_session_timeout);
368 /****************************************************************************/
369 /****************************************************************************/
374 /*************************************************
376 *************************************************/
381 [<outgoing interface>]
386 int main(int argc, char **argv)
388 struct sockaddr *s_ptr;
389 struct sockaddr_in s_in4;
390 char *interface = NULL;
391 char *address = NULL;
392 char *certfile = NULL;
393 char *keyfile = NULL;
395 int host_af, port, s_len, rc, sock, save_errno;
398 int sent_starttls = 0;
399 int tls_on_connect = 0;
402 struct sockaddr_in6 s_in6;
410 unsigned char outbuffer[10240];
411 unsigned char inbuffer[10240];
412 unsigned char *inptr = inbuffer;
414 *inptr = 0; /* Buffer empty */
418 while (argc >= argi + 1 && argv[argi][0] == '-')
420 if (strcmp(argv[argi], "-tls-on-connect") == 0)
425 else if (argv[argi][1] == 't' && isdigit(argv[argi][2]))
427 timeout = atoi(argv[argi]+1);
432 printf("Unrecognized option %s\n", argv[argi]);
437 /* Mandatory 1st arg is IP address */
441 printf("No IP address given\n");
445 address = argv[argi++];
446 host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET;
448 /* Mandatory 2nd arg is port */
452 printf("No port number given\n");
456 port = atoi(argv[argi++]);
458 /* Optional next arg is interface */
461 (isdigit((unsigned char)argv[argi][0]) || argv[argi][0] == ':'))
462 interface = argv[argi++];
464 /* Any more arguments are the name of a certificate file and key file */
466 if (argc > argi) certfile = argv[argi++];
467 if (argc > argi) keyfile = argv[argi++];
471 /* For an IPv6 address, use an IPv6 sockaddr structure. */
473 if (host_af == AF_INET6)
475 s_ptr = (struct sockaddr *)&s_in6;
476 s_len = sizeof(s_in6);
481 /* For an IPv4 address, use an IPv4 sockaddr structure,
482 even on an IPv6 system. */
485 s_ptr = (struct sockaddr *)&s_in4;
486 s_len = sizeof(s_in4);
489 printf("Connecting to %s port %d ... ", address, port);
491 sock = socket(host_af, SOCK_STREAM, 0);
494 printf("socket creation failed: %s\n", strerror(errno));
498 /* Bind to a specific interface if requested. On an IPv6 system, this has
499 to be of the same family as the address we are calling. On an IPv4 system the
500 test is redundant, but it keeps the code tidier. */
502 if (interface != NULL)
504 int interface_af = (strchr(interface, ':') != NULL)? AF_INET6 : AF_INET;
506 if (interface_af == host_af)
510 /* Set up for IPv6 binding */
512 if (host_af == AF_INET6)
514 memset(&s_in6, 0, sizeof(s_in6));
515 s_in6.sin6_family = AF_INET6;
517 if (inet_pton(AF_INET6, interface, &s_in6.sin6_addr) != 1)
519 printf("Unable to parse \"%s\"", interface);
526 /* Set up for IPv4 binding */
529 memset(&s_in4, 0, sizeof(s_in4));
530 s_in4.sin_family = AF_INET;
532 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(interface);
537 if (bind(sock, s_ptr, s_len) < 0)
539 printf("Unable to bind outgoing SMTP call to %s: %s",
540 interface, strerror(errno));
546 /* Set up a remote IPv6 address */
549 if (host_af == AF_INET6)
551 memset(&s_in6, 0, sizeof(s_in6));
552 s_in6.sin6_family = AF_INET6;
553 s_in6.sin6_port = htons(port);
554 if (inet_pton(host_af, address, &s_in6.sin6_addr) != 1)
556 printf("Unable to parse \"%s\"", address);
563 /* Set up a remote IPv4 address */
566 memset(&s_in4, 0, sizeof(s_in4));
567 s_in4.sin_family = AF_INET;
568 s_in4.sin_port = htons(port);
569 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(address);
572 /* SIGALRM handler crashes out */
574 signal(SIGALRM, sigalrm_handler_crash);
576 rc = connect(sock, s_ptr, s_len);
580 /* A failure whose error code is "Interrupted system call" is in fact
581 an externally applied timeout if the signal handler has been run. */
586 printf("failed: %s\n", strerror(save_errno));
590 printf("connected\n");
593 /* --------------- Set up for OpenSSL --------------- */
597 SSL_load_error_strings();
599 ctx = SSL_CTX_new(SSLv23_method());
602 printf ("SSL_CTX_new failed\n");
606 if (certfile != NULL)
608 if (!SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM))
610 printf("SSL_CTX_use_certificate_file failed\n");
613 printf("Certificate file = %s\n", certfile);
618 if (!SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))
620 printf("SSL_CTX_use_PrivateKey_file failed\n");
623 printf("Key file = %s\n", keyfile);
626 SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
627 SSL_CTX_set_timeout(ctx, 200);
628 SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
632 /* --------------- Set up for GnuTLS --------------- */
635 if (certfile != NULL) printf("Certificate file = %s\n", certfile);
636 if (keyfile != NULL) printf("Key file = %s\n", keyfile);
637 tls_init(certfile, keyfile);
638 tls_session = tls_session_init();
639 gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)sock);
641 /* When the server asks for a certificate and the client does not have one,
642 there is a SIGPIPE error in the gnutls_handshake() function for some reason
643 that is not understood. As luck would have it, this has never hit Exim itself
644 because it ignores SIGPIPE errors. Doing the same here allows it all to work as
647 signal(SIGPIPE, SIG_IGN);
650 /* ---------------------------------------------- */
653 /* Start TLS session if configured to do so without STARTTLS */
658 printf("Attempting to start TLS\n");
661 tls_active = tls_start(sock, &ssl, ctx);
665 sigalrm_seen = FALSE;
667 tls_active = gnutls_handshake(tls_session) >= 0;
672 printf("Failed to start TLS\n");
674 printf("Succeeded in starting TLS\n");
678 while (fgets(outbuffer, sizeof(outbuffer), stdin) != NULL)
680 int n = (int)strlen(outbuffer);
681 while (n > 0 && isspace(outbuffer[n-1])) n--;
684 /* Expect incoming */
686 if (strncmp(outbuffer, "??? ", 4) == 0)
688 unsigned char *lineptr;
689 printf("%s\n", outbuffer);
691 if (*inptr == 0) /* Refill input buffer */
696 rc = SSL_read (ssl, inbuffer, sizeof(inbuffer) - 1);
699 rc = gnutls_record_recv(tls_session, CS inbuffer, sizeof(inbuffer) - 1);
705 rc = read(sock, inbuffer, sizeof(inbuffer));
711 printf("Read error %s\n", strerror(errno));
716 printf("Unexpected EOF read\n");
728 while (*inptr != 0 && *inptr != '\r' && *inptr != '\n') inptr++;
732 if (*inptr == '\n') inptr++;
735 printf("<<< %s\n", lineptr);
736 if (strncmp(lineptr, outbuffer + 4, (int)strlen(outbuffer) - 4) != 0)
738 printf("\n******** Input mismatch ********\n");
745 if (lineptr[0] == '2')
747 printf("Attempting to start TLS\n");
751 tls_active = tls_start(sock, &ssl, ctx);
755 sigalrm_seen = FALSE;
757 tls_active = gnutls_handshake(tls_session) >= 0;
763 printf("Failed to start TLS\n");
767 printf("Succeeded in starting TLS\n");
769 else printf("Abandoning TLS start attempt\n");
775 /* Wait for a bit before proceeding */
777 else if (strncmp(outbuffer, "+++ ", 4) == 0)
779 printf("%s\n", outbuffer);
780 sleep(atoi(outbuffer + 4));
783 /* Send outgoing, but barf if unconsumed incoming */
787 unsigned char *escape;
791 printf("Unconsumed input: %s", inptr);
792 printf(" About to send: %s\n", outbuffer);
800 if (strcmp(outbuffer, "stoptls") == 0 ||
801 strcmp(outbuffer, "STOPTLS") == 0)
805 printf("STOPTLS read when TLS not active\n");
808 printf("Shutting down TLS encryption\n");
816 gnutls_bye(tls_session, GNUTLS_SHUT_WR);
817 gnutls_deinit(tls_session);
819 gnutls_global_deinit();
826 /* Remember that we sent STARTTLS */
828 sent_starttls = (strcmp(outbuffer, "starttls") == 0 ||
829 strcmp(outbuffer, "STARTTLS") == 0);
831 /* Fudge: if the command is "starttls_wait", we send the starttls bit,
832 but we haven't set the flag, so that there is no negotiation. This is for
833 testing the server's timeout. */
835 if (strcmp(outbuffer, "starttls_wait") == 0)
842 printf(">>> %s\n", outbuffer);
843 strcpy(outbuffer + n, "\r\n");
845 /* Turn "\n" and "\r" into the relevant characters. This is a hack. */
847 while ((escape = strstr(outbuffer, "\\r")) != NULL)
850 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
854 while ((escape = strstr(outbuffer, "\\n")) != NULL)
857 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
867 rc = SSL_write (ssl, outbuffer, n + 2);
870 rc = gnutls_record_send(tls_session, CS outbuffer, n + 2);
873 printf("GnuTLS write error: %s\n", gnutls_strerror(rc));
880 rc = write(sock, outbuffer, n + 2);
886 printf("Write error: %s\n", strerror(errno));
892 printf("End of script\n");
898 /* End of client.c */