Some tidying got overlooked in the "removal of RSA_EXPORT" patch.
[exim.git] / test / src / client.c
CommitLineData
c55a77db
PH
1/* $Cambridge: exim/test/src/client.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */
2
3/* A little hacked up program that makes a TCP/IP call and reads a script to
4drive it, for testing Exim server code running as a daemon. It's got a bit
5messy with the addition of support for either OpenSSL or GnuTLS. The code for
6those was hacked out of Exim itself. */
7
8/* ANSI C standard includes */
9
10#include <ctype.h>
11#include <signal.h>
12#include <stdarg.h>
13#include <stddef.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <time.h>
18
19/* Unix includes */
20
21#include <errno.h>
22#include <dirent.h>
23#include <sys/types.h>
24
25#include <netinet/in_systm.h>
26#include <netinet/in.h>
27#include <netinet/ip.h>
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. */
62
63#ifdef HAVE_OPENSSL
64#define HAVE_TLS
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>
71#endif
72
73
74#ifdef HAVE_GNUTLS
75#define HAVE_TLS
76#include <gnutls/gnutls.h>
77#include <gnutls/x509.h>
78
79#define DH_BITS 768
80#define RSA_BITS 512
81
82/* Local static variables for GNUTLS */
83
84static gnutls_rsa_params rsa_params = NULL;
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,
100 GNUTLS_KX_RSA_EXPORT,
101 0 };
102
103static 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,
108 0 };
109
110static const int mac_priority[16] = {
111 GNUTLS_MAC_SHA,
112 GNUTLS_MAC_MD5,
113 0 };
114
115static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 };
116static const int cert_type_priority[16] = { GNUTLS_CRT_X509, 0 };
117
118#endif
119
120
121
122
123/*************************************************
124* SIGALRM handler - crash out *
125*************************************************/
126
127static void
128sigalrm_handler_crash(int sig)
129{
130sig = sig; /* Keep picky compilers happy */
131printf("\nClient timed out\n");
132exit(99);
133}
134
135
136/*************************************************
137* SIGALRM handler - set flag *
138*************************************************/
139
140static void
141sigalrm_handler_flag(int sig)
142{
143sig = sig; /* Keep picky compilers happy */
144sigalrm_seen = 1;
145}
146
147
148
149/****************************************************************************/
150/****************************************************************************/
151
152#ifdef HAVE_OPENSSL
153/*************************************************
154* Start an OpenSSL TLS session *
155*************************************************/
156
157int tls_start(int sock, SSL **ssl, SSL_CTX *ctx)
158{
159int rc;
160static const char *sid_ctx = "exim";
161
162RAND_load_file("client.c", -1); /* Not *very* random! */
163
164*ssl = SSL_new (ctx);
165SSL_set_session_id_context(*ssl, sid_ctx, strlen(sid_ctx));
166SSL_set_fd (*ssl, sock);
167SSL_set_connect_state(*ssl);
168
169signal(SIGALRM, sigalrm_handler_flag);
170sigalrm_seen = 0;
171alarm(5);
172rc = SSL_connect (*ssl);
173alarm(0);
174
175if (sigalrm_seen)
176 {
177 printf("SSL_connect timed out\n");
178 return 0;
179 }
180
181if (rc <= 0)
182 {
183 ERR_print_errors_fp(stdout);
184 return 0;
185 }
186
187printf("SSL connection using %s\n", SSL_get_cipher (*ssl));
188return 1;
189}
190
191
192/*************************************************
193* SSL Information callback *
194*************************************************/
195
196static void
197info_callback(SSL *s, int where, int ret)
198{
199where = where;
200ret = ret;
201printf("SSL info: %s\n", SSL_state_string_long(s));
202}
203#endif
204
205
206/****************************************************************************/
207/****************************************************************************/
208
209
210#ifdef HAVE_GNUTLS
211/*************************************************
212* Handle GnuTLS error *
213*************************************************/
214
215/* Called from lots of places when errors occur before actually starting to do
216the TLS handshake, that is, while the session is still in clear.
217
218Argument:
219 prefix prefix text
220 err a GnuTLS error number, or 0 if local error
221
222Returns: doesn't - it dies
223*/
224
225static void
226gnutls_error(uschar *prefix, int err)
227{
228fprintf(stderr, "GnuTLS connection error:%s", prefix);
229if (err != 0) fprintf(stderr, " %s", gnutls_strerror(err));
230fprintf(stderr, "\n");
231exit(1);
232}
233
234
235
236/*************************************************
237* Setup up RSA and DH parameters *
238*************************************************/
239
240/* For the test suite, the parameters should always be available in the spool
241directory. */
242
243static void
244init_rsa_dh(void)
245{
246int fd;
247int ret;
248gnutls_datum m;
249uschar filename[200];
250struct stat statbuf;
251
252/* Initialize the data structures for holding the parameters */
253
254ret = gnutls_rsa_params_init(&rsa_params);
255if (ret < 0) gnutls_error(US"init rsa_params", ret);
256
257ret = gnutls_dh_params_init(&dh_params);
258if (ret < 0) gnutls_error(US"init dh_params", ret);
259
260/* Open the cache file for reading and if successful, read it and set up the
261parameters. If we can't set up the RSA parameters, assume that we are dealing
262with an old-style cache file that is in another format, and fall through to
263compute new values. However, if we correctly get RSA parameters, a failure to
264set up D-H parameters is treated as an error. */
265
266fd = open("aux-fixed/gnutls-params", O_RDONLY, 0);
267if (fd < 0)
268 {
269 fprintf(stderr, "Failed to open spool/gnutls-params: %s\n", strerror(errno));
270 exit(1);
271 }
272
273if (fstat(fd, &statbuf) < 0)
274 {
275 (void)close(fd);
276 return gnutls_error(US"TLS cache stat failed", 0);
277 }
278
279m.size = statbuf.st_size;
280m.data = malloc(m.size);
281if (m.data == NULL)
282 return gnutls_error(US"memory allocation failed", 0);
283if (read(fd, m.data, m.size) != m.size)
284 return gnutls_error(US"TLS cache read failed", 0);
285(void)close(fd);
286
287ret = gnutls_rsa_params_import_pkcs1(rsa_params, &m, GNUTLS_X509_FMT_PEM);
288if (ret < 0) return gnutls_error(US"RSA params import", ret);
289ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM);
290if (ret < 0) return gnutls_error(US"DH params import", ret);
291free(m.data);
292}
293
294
295
296
297/*************************************************
298* Initialize for GnuTLS *
299*************************************************/
300
301/*
302Arguments:
303 certificate certificate file
304 privatekey private key file
305*/
306
307static void
308tls_init(uschar *certificate, uschar *privatekey)
309{
310int rc;
311
312rc = gnutls_global_init();
313if (rc < 0) gnutls_error(US"gnutls_global_init", rc);
314
315/* Read RSA and D-H parameters from the cache file. */
316
317init_rsa_dh();
318
319/* Create the credentials structure */
320
321rc = gnutls_certificate_allocate_credentials(&x509_cred);
322if (rc < 0) gnutls_error(US"certificate_allocate_credentials", rc);
323
324/* Set the certificate and private keys */
325
326if (certificate != NULL)
327 {
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);
331 }
332
333/* Associate the parameters with the x509 credentials structure. */
334
335gnutls_certificate_set_dh_params(x509_cred, dh_params);
336gnutls_certificate_set_rsa_export_params(x509_cred, rsa_params);
337}
338
339
340
341/*************************************************
342* Initialize a single GNUTLS session *
343*************************************************/
344
345static gnutls_session
346tls_session_init(void)
347{
348gnutls_session session;
349
350gnutls_init(&session, GNUTLS_CLIENT);
351
352gnutls_cipher_set_priority(session, default_cipher_priority);
353gnutls_compression_set_priority(session, comp_priority);
354gnutls_kx_set_priority(session, kx_priority);
355gnutls_protocol_set_priority(session, protocol_priority);
356gnutls_mac_set_priority(session, mac_priority);
357
358gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
359
360gnutls_dh_set_prime_bits(session, DH_BITS);
361gnutls_db_set_cache_expiration(session, ssl_session_timeout);
362
363return session;
364}
365#endif
366
367
368/****************************************************************************/
369/****************************************************************************/
370
371
372
373
374/*************************************************
375* Main Program *
376*************************************************/
377
378/* Usage: client
379 <IP address>
380 <port>
381 [<outgoing interface>]
382 [<cert file>]
383 [<key file>]
384*/
385
386int main(int argc, char **argv)
387{
388struct sockaddr *s_ptr;
389struct sockaddr_in s_in4;
390char *interface = NULL;
391char *address = NULL;
392char *certfile = NULL;
393char *keyfile = NULL;
394int argi = 1;
395int host_af, port, s_len, rc, sock, save_errno;
396int timeout = 1;
397int tls_active = 0;
398int sent_starttls = 0;
399int tls_on_connect = 0;
400
401#if HAVE_IPV6
402struct sockaddr_in6 s_in6;
403#endif
404
405#ifdef HAVE_OPENSSL
406SSL_CTX* ctx;
407SSL* ssl;
408#endif
409
410unsigned char outbuffer[10240];
411unsigned char inbuffer[10240];
412unsigned char *inptr = inbuffer;
413
414*inptr = 0; /* Buffer empty */
415
416/* Options */
417
418while (argc >= argi + 1 && argv[argi][0] == '-')
419 {
420 if (strcmp(argv[argi], "-tls-on-connect") == 0)
421 {
422 tls_on_connect = 1;
423 argi++;
424 }
425 else if (argv[argi][1] == 't' && isdigit(argv[argi][2]))
426 {
427 timeout = atoi(argv[argi]+1);
428 argi++;
429 }
430 else
431 {
432 printf("Unrecognized option %s\n", argv[argi]);
433 exit(1);
434 }
435 }
436
437/* Mandatory 1st arg is IP address */
438
439if (argc < argi+1)
440 {
441 printf("No IP address given\n");
442 exit(1);
443 }
444
445address = argv[argi++];
446host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET;
447
448/* Mandatory 2nd arg is port */
449
450if (argc < argi+1)
451 {
452 printf("No port number given\n");
453 exit(1);
454 }
455
456port = atoi(argv[argi++]);
457
458/* Optional next arg is interface */
459
460if (argc > argi &&
461 (isdigit((unsigned char)argv[argi][0]) || argv[argi][0] == ':'))
462 interface = argv[argi++];
463
464/* Any more arguments are the name of a certificate file and key file */
465
466if (argc > argi) certfile = argv[argi++];
467if (argc > argi) keyfile = argv[argi++];
468
469
470#if HAVE_IPV6
471/* For an IPv6 address, use an IPv6 sockaddr structure. */
472
473if (host_af == AF_INET6)
474 {
475 s_ptr = (struct sockaddr *)&s_in6;
476 s_len = sizeof(s_in6);
477 }
478else
479#endif
480
481/* For an IPv4 address, use an IPv4 sockaddr structure,
482even on an IPv6 system. */
483
484 {
485 s_ptr = (struct sockaddr *)&s_in4;
486 s_len = sizeof(s_in4);
487 }
488
489printf("Connecting to %s port %d ... ", address, port);
490
491sock = socket(host_af, SOCK_STREAM, 0);
492if (sock < 0)
493 {
494 printf("socket creation failed: %s\n", strerror(errno));
495 exit(1);
496 }
497
498/* Bind to a specific interface if requested. On an IPv6 system, this has
499to be of the same family as the address we are calling. On an IPv4 system the
500test is redundant, but it keeps the code tidier. */
501
502if (interface != NULL)
503 {
504 int interface_af = (strchr(interface, ':') != NULL)? AF_INET6 : AF_INET;
505
506 if (interface_af == host_af)
507 {
508 #if HAVE_IPV6
509
510 /* Set up for IPv6 binding */
511
512 if (host_af == AF_INET6)
513 {
514 memset(&s_in6, 0, sizeof(s_in6));
515 s_in6.sin6_family = AF_INET6;
516 s_in6.sin6_port = 0;
517 if (inet_pton(AF_INET6, interface, &s_in6.sin6_addr) != 1)
518 {
519 printf("Unable to parse \"%s\"", interface);
520 exit(1);
521 }
522 }
523 else
524 #endif
525
526 /* Set up for IPv4 binding */
527
528 {
529 memset(&s_in4, 0, sizeof(s_in4));
530 s_in4.sin_family = AF_INET;
531 s_in4.sin_port = 0;
532 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(interface);
533 }
534
535 /* Bind */
536
537 if (bind(sock, s_ptr, s_len) < 0)
538 {
539 printf("Unable to bind outgoing SMTP call to %s: %s",
540 interface, strerror(errno));
541 exit(1);
542 }
543 }
544 }
545
546/* Set up a remote IPv6 address */
547
548#if HAVE_IPV6
549if (host_af == AF_INET6)
550 {
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)
555 {
556 printf("Unable to parse \"%s\"", address);
557 exit(1);
558 }
559 }
560else
561#endif
562
563/* Set up a remote IPv4 address */
564
565 {
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);
570 }
571
572/* SIGALRM handler crashes out */
573
574signal(SIGALRM, sigalrm_handler_crash);
575alarm(timeout);
576rc = connect(sock, s_ptr, s_len);
577save_errno = errno;
578alarm(0);
579
580/* A failure whose error code is "Interrupted system call" is in fact
581an externally applied timeout if the signal handler has been run. */
582
583if (rc < 0)
584 {
585 close(sock);
586 printf("failed: %s\n", strerror(save_errno));
587 exit(1);
588 }
589
590printf("connected\n");
591
592
593/* --------------- Set up for OpenSSL --------------- */
594
595#ifdef HAVE_OPENSSL
596SSL_library_init();
597SSL_load_error_strings();
598
599ctx = SSL_CTX_new(SSLv23_method());
600if (ctx == NULL)
601 {
602 printf ("SSL_CTX_new failed\n");
603 exit(1);
604 }
605
606if (certfile != NULL)
607 {
608 if (!SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM))
609 {
610 printf("SSL_CTX_use_certificate_file failed\n");
611 exit(1);
612 }
613 printf("Certificate file = %s\n", certfile);
614 }
615
616if (keyfile != NULL)
617 {
618 if (!SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))
619 {
620 printf("SSL_CTX_use_PrivateKey_file failed\n");
621 exit(1);
622 }
623 printf("Key file = %s\n", keyfile);
624 }
625
626SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
627SSL_CTX_set_timeout(ctx, 200);
628SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
629#endif
630
631
632/* --------------- Set up for GnuTLS --------------- */
633
634#ifdef HAVE_GNUTLS
635if (certfile != NULL) printf("Certificate file = %s\n", certfile);
636if (keyfile != NULL) printf("Key file = %s\n", keyfile);
637tls_init(certfile, keyfile);
638tls_session = tls_session_init();
639gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)sock);
640
641/* When the server asks for a certificate and the client does not have one,
642there is a SIGPIPE error in the gnutls_handshake() function for some reason
643that is not understood. As luck would have it, this has never hit Exim itself
644because it ignores SIGPIPE errors. Doing the same here allows it all to work as
645one wants. */
646
647signal(SIGPIPE, SIG_IGN);
648#endif
649
650/* ---------------------------------------------- */
651
652
653/* Start TLS session if configured to do so without STARTTLS */
654
655#ifdef HAVE_TLS
656if (tls_on_connect)
657 {
658 printf("Attempting to start TLS\n");
659
660 #ifdef HAVE_OPENSSL
661 tls_active = tls_start(sock, &ssl, ctx);
662 #endif
663
664 #ifdef HAVE_GNUTLS
665 sigalrm_seen = FALSE;
666 alarm(timeout);
667 tls_active = gnutls_handshake(tls_session) >= 0;
668 alarm(0);
669 #endif
670
671 if (!tls_active)
672 printf("Failed to start TLS\n");
673 else
674 printf("Succeeded in starting TLS\n");
675 }
676#endif
677
678while (fgets(outbuffer, sizeof(outbuffer), stdin) != NULL)
679 {
680 int n = (int)strlen(outbuffer);
681 while (n > 0 && isspace(outbuffer[n-1])) n--;
682 outbuffer[n] = 0;
683
684 /* Expect incoming */
685
686 if (strncmp(outbuffer, "??? ", 4) == 0)
687 {
688 unsigned char *lineptr;
689 printf("%s\n", outbuffer);
690
691 if (*inptr == 0) /* Refill input buffer */
692 {
693 if (tls_active)
694 {
695 #ifdef HAVE_OPENSSL
696 rc = SSL_read (ssl, inbuffer, sizeof(inbuffer) - 1);
697 #endif
698 #ifdef HAVE_GNUTLS
699 rc = gnutls_record_recv(tls_session, CS inbuffer, sizeof(inbuffer) - 1);
700 #endif
701 }
702 else
703 {
704 alarm(timeout);
705 rc = read(sock, inbuffer, sizeof(inbuffer));
706 alarm(0);
707 }
708
709 if (rc < 0)
710 {
711 printf("Read error %s\n", strerror(errno));
712 exit(1) ;
713 }
714 else if (rc == 0)
715 {
716 printf("Unexpected EOF read\n");
717 close(sock);
718 exit(1);
719 }
720 else
721 {
722 inbuffer[rc] = 0;
723 inptr = inbuffer;
724 }
725 }
726
727 lineptr = inptr;
728 while (*inptr != 0 && *inptr != '\r' && *inptr != '\n') inptr++;
729 if (*inptr != 0)
730 {
731 *inptr++ = 0;
732 if (*inptr == '\n') inptr++;
733 }
734
735 printf("<<< %s\n", lineptr);
736 if (strncmp(lineptr, outbuffer + 4, (int)strlen(outbuffer) - 4) != 0)
737 {
738 printf("\n******** Input mismatch ********\n");
739 exit(1);
740 }
741
742 #ifdef HAVE_TLS
743 if (sent_starttls)
744 {
745 if (lineptr[0] == '2')
746 {
747 printf("Attempting to start TLS\n");
748 fflush(stdout);
749
750 #ifdef HAVE_OPENSSL
751 tls_active = tls_start(sock, &ssl, ctx);
752 #endif
753
754 #ifdef HAVE_GNUTLS
755 sigalrm_seen = FALSE;
756 alarm(timeout);
757 tls_active = gnutls_handshake(tls_session) >= 0;
758 alarm(0);
759 #endif
760
761 if (!tls_active)
762 {
763 printf("Failed to start TLS\n");
764 fflush(stdout);
765 }
766 else
767 printf("Succeeded in starting TLS\n");
768 }
769 else printf("Abandoning TLS start attempt\n");
770 }
771 sent_starttls = 0;
772 #endif
773 }
774
775 /* Wait for a bit before proceeding */
776
777 else if (strncmp(outbuffer, "+++ ", 4) == 0)
778 {
779 printf("%s\n", outbuffer);
780 sleep(atoi(outbuffer + 4));
781 }
782
783 /* Send outgoing, but barf if unconsumed incoming */
784
785 else
786 {
787 unsigned char *escape;
788
789 if (*inptr != 0)
790 {
791 printf("Unconsumed input: %s", inptr);
792 printf(" About to send: %s\n", outbuffer);
793 exit(1);
794 }
795
796 #ifdef HAVE_TLS
797
798 /* Shutdown TLS */
799
800 if (strcmp(outbuffer, "stoptls") == 0 ||
801 strcmp(outbuffer, "STOPTLS") == 0)
802 {
803 if (!tls_active)
804 {
805 printf("STOPTLS read when TLS not active\n");
806 exit(1);
807 }
808 printf("Shutting down TLS encryption\n");
809
810 #ifdef HAVE_OPENSSL
811 SSL_shutdown(ssl);
812 SSL_free(ssl);
813 #endif
814
815 #ifdef HAVE_GNUTLS
816 gnutls_bye(tls_session, GNUTLS_SHUT_WR);
817 gnutls_deinit(tls_session);
818 tls_session = NULL;
819 gnutls_global_deinit();
820 #endif
821
822 tls_active = 0;
823 continue;
824 }
825
826 /* Remember that we sent STARTTLS */
827
828 sent_starttls = (strcmp(outbuffer, "starttls") == 0 ||
829 strcmp(outbuffer, "STARTTLS") == 0);
830
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. */
834
835 if (strcmp(outbuffer, "starttls_wait") == 0)
836 {
837 outbuffer[8] = 0;
838 n = 8;
839 }
840 #endif
841
842 printf(">>> %s\n", outbuffer);
843 strcpy(outbuffer + n, "\r\n");
844
845 /* Turn "\n" and "\r" into the relevant characters. This is a hack. */
846
847 while ((escape = strstr(outbuffer, "\\r")) != NULL)
848 {
849 *escape = '\r';
850 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
851 n--;
852 }
853
854 while ((escape = strstr(outbuffer, "\\n")) != NULL)
855 {
856 *escape = '\n';
857 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
858 n--;
859 }
860
861 /* OK, do it */
862
863 alarm(timeout);
864 if (tls_active)
865 {
866 #ifdef HAVE_OPENSSL
867 rc = SSL_write (ssl, outbuffer, n + 2);
868 #endif
869 #ifdef HAVE_GNUTLS
870 rc = gnutls_record_send(tls_session, CS outbuffer, n + 2);
871 if (rc < 0)
872 {
873 printf("GnuTLS write error: %s\n", gnutls_strerror(rc));
874 exit(1);
875 }
876 #endif
877 }
878 else
879 {
880 rc = write(sock, outbuffer, n + 2);
881 }
882 alarm(0);
883
884 if (rc < 0)
885 {
886 printf("Write error: %s\n", strerror(errno));
887 exit(1);
888 }
889 }
890 }
891
892printf("End of script\n");
893close(sock);
894
895exit(0);
896}
897
898/* End of client.c */