Add -Mvc option.
[users/jgh/exim.git] / test / src / client.c
1 /* $Cambridge: exim/test/src/client.c,v 1.2 2006/10/16 13:42:19 ph10 Exp $ */
2
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. */
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
47 typedef 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
57 static int sigalrm_seen = 0;
58
59
60 /* TLS support can be optionally included, either for OpenSSL or GnuTLS. The
61 latter 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
81 /* Local static variables for GNUTLS */
82
83 static gnutls_dh_params dh_params = NULL;
84
85 static gnutls_certificate_credentials_t x509_cred = NULL;
86 static gnutls_session tls_session = NULL;
87
88 static int  ssl_session_timeout = 200;
89
90 /* Priorities for TLS algorithms to use. */
91
92 static const int protocol_priority[16] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
93
94 static const int kx_priority[16] = {
95   GNUTLS_KX_RSA,
96   GNUTLS_KX_DHE_DSS,
97   GNUTLS_KX_DHE_RSA,
98   0 };
99
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,
105   0 };
106
107 static const int mac_priority[16] = {
108   GNUTLS_MAC_SHA,
109   GNUTLS_MAC_MD5,
110   0 };
111
112 static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 };
113 static const int cert_type_priority[16] = { GNUTLS_CRT_X509, 0 };
114
115 #endif
116
117
118
119
120 /*************************************************
121 *            SIGALRM handler - crash out         *
122 *************************************************/
123
124 static void
125 sigalrm_handler_crash(int sig)
126 {
127 sig = sig;    /* Keep picky compilers happy */
128 printf("\nClient timed out\n");
129 exit(99);
130 }
131
132
133 /*************************************************
134 *            SIGALRM handler - set flag          *
135 *************************************************/
136
137 static void
138 sigalrm_handler_flag(int sig)
139 {
140 sig = sig;    /* Keep picky compilers happy */
141 sigalrm_seen = 1;
142 }
143
144
145
146 /****************************************************************************/
147 /****************************************************************************/
148
149 #ifdef HAVE_OPENSSL
150 /*************************************************
151 *         Start an OpenSSL TLS session           *
152 *************************************************/
153
154 int tls_start(int sock, SSL **ssl, SSL_CTX *ctx)
155 {
156 int rc;
157 static const char *sid_ctx = "exim";
158
159 RAND_load_file("client.c", -1);   /* Not *very* random! */
160
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);
165
166 signal(SIGALRM, sigalrm_handler_flag);
167 sigalrm_seen = 0;
168 alarm(5);
169 rc = SSL_connect (*ssl);
170 alarm(0);
171
172 if (sigalrm_seen)
173   {
174   printf("SSL_connect timed out\n");
175   return 0;
176   }
177
178 if (rc <= 0)
179   {
180   ERR_print_errors_fp(stdout);
181   return 0;
182   }
183
184 printf("SSL connection using %s\n", SSL_get_cipher (*ssl));
185 return 1;
186 }
187
188
189 /*************************************************
190 *           SSL Information callback             *
191 *************************************************/
192
193 static void
194 info_callback(SSL *s, int where, int ret)
195 {
196 where = where;
197 ret = ret;
198 printf("SSL info: %s\n", SSL_state_string_long(s));
199 }
200 #endif
201
202
203 /****************************************************************************/
204 /****************************************************************************/
205
206
207 #ifdef HAVE_GNUTLS
208 /*************************************************
209 *            Handle GnuTLS error                 *
210 *************************************************/
211
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.
214
215 Argument:
216   prefix    prefix text
217   err       a GnuTLS error number, or 0 if local error
218
219 Returns:    doesn't - it dies
220 */
221
222 static void
223 gnutls_error(uschar *prefix, int err)
224 {
225 fprintf(stderr, "GnuTLS connection error: %s:", prefix);
226 if (err != 0) fprintf(stderr, " %s", gnutls_strerror(err));
227 fprintf(stderr, "\n");
228 exit(1);
229 }
230
231
232
233 /*************************************************
234 *             Setup up DH parameters             *
235 *************************************************/
236
237 /* For the test suite, the parameters should always be available in the spool
238 directory. */
239
240 static void
241 init_dh(void)
242 {
243 int fd;
244 int ret;
245 gnutls_datum m;
246 uschar filename[200];
247 struct stat statbuf;
248
249 /* Initialize the data structures for holding the parameters */
250
251 ret = gnutls_dh_params_init(&dh_params);
252 if (ret < 0) gnutls_error(US"init dh_params", ret);
253
254 /* Open the cache file for reading and if successful, read it and set up the
255 parameters. */
256
257 fd = open("aux-fixed/gnutls-params", O_RDONLY, 0);
258 if (fd < 0)
259   {
260   fprintf(stderr, "Failed to open spool/gnutls-params: %s\n", strerror(errno));
261   exit(1);
262   }
263
264 if (fstat(fd, &statbuf) < 0)
265   {
266   (void)close(fd);
267   return gnutls_error(US"TLS cache stat failed", 0);
268   }
269
270 m.size = statbuf.st_size;
271 m.data = malloc(m.size);
272 if (m.data == NULL)
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);
276 (void)close(fd);
277
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);
280 free(m.data);
281 }
282
283
284
285
286 /*************************************************
287 *            Initialize for GnuTLS               *
288 *************************************************/
289
290 /*
291 Arguments:
292   certificate     certificate file
293   privatekey      private key file
294 */
295
296 static void
297 tls_init(uschar *certificate, uschar *privatekey)
298 {
299 int rc;
300
301 rc = gnutls_global_init();
302 if (rc < 0) gnutls_error(US"gnutls_global_init", rc);
303
304 /* Read D-H parameters from the cache file. */
305
306 init_dh();
307
308 /* Create the credentials structure */
309
310 rc = gnutls_certificate_allocate_credentials(&x509_cred);
311 if (rc < 0) gnutls_error(US"certificate_allocate_credentials", rc);
312
313 /* Set the certificate and private keys */
314
315 if (certificate != NULL)
316   {
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);
320   }
321
322 /* Associate the parameters with the x509 credentials structure. */
323
324 gnutls_certificate_set_dh_params(x509_cred, dh_params);
325 }
326
327
328
329 /*************************************************
330 *        Initialize a single GNUTLS session      *
331 *************************************************/
332
333 static gnutls_session
334 tls_session_init(void)
335 {
336 gnutls_session session;
337
338 gnutls_init(&session, GNUTLS_CLIENT);
339
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);
345
346 gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
347
348 gnutls_dh_set_prime_bits(session, DH_BITS);
349 gnutls_db_set_cache_expiration(session, ssl_session_timeout);
350
351 return session;
352 }
353 #endif
354
355
356 /****************************************************************************/
357 /****************************************************************************/
358
359
360
361
362 /*************************************************
363 *                 Main Program                   *
364 *************************************************/
365
366 /* Usage: client
367           <IP address>
368           <port>
369           [<outgoing interface>]
370           [<cert file>]
371           [<key file>]
372 */
373
374 int main(int argc, char **argv)
375 {
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;
382 int argi = 1;
383 int host_af, port, s_len, rc, sock, save_errno;
384 int timeout = 1;
385 int tls_active = 0;
386 int sent_starttls = 0;
387 int tls_on_connect = 0;
388
389 #if HAVE_IPV6
390 struct sockaddr_in6 s_in6;
391 #endif
392
393 #ifdef HAVE_OPENSSL
394 SSL_CTX* ctx;
395 SSL*     ssl;
396 #endif
397
398 unsigned char outbuffer[10240];
399 unsigned char inbuffer[10240];
400 unsigned char *inptr = inbuffer;
401
402 *inptr = 0;   /* Buffer empty */
403
404 /* Options */
405
406 while (argc >= argi + 1 && argv[argi][0] == '-')
407   {
408   if (strcmp(argv[argi], "-tls-on-connect") == 0)
409     {
410     tls_on_connect = 1;
411     argi++;
412     }
413   else if (argv[argi][1] == 't' && isdigit(argv[argi][2]))
414     {
415     timeout = atoi(argv[argi]+1);
416     argi++;
417     }
418   else
419     {
420     printf("Unrecognized option %s\n", argv[argi]);
421     exit(1);
422     }
423   }
424
425 /* Mandatory 1st arg is IP address */
426
427 if (argc < argi+1)
428   {
429   printf("No IP address given\n");
430   exit(1);
431   }
432
433 address = argv[argi++];
434 host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET;
435
436 /* Mandatory 2nd arg is port */
437
438 if (argc < argi+1)
439   {
440   printf("No port number given\n");
441   exit(1);
442   }
443
444 port = atoi(argv[argi++]);
445
446 /* Optional next arg is interface */
447
448 if (argc > argi &&
449   (isdigit((unsigned char)argv[argi][0]) || argv[argi][0] == ':'))
450     interface = argv[argi++];
451
452 /* Any more arguments are the name of a certificate file and key file */
453
454 if (argc > argi) certfile = argv[argi++];
455 if (argc > argi) keyfile = argv[argi++];
456
457
458 #if HAVE_IPV6
459 /* For an IPv6 address, use an IPv6 sockaddr structure. */
460
461 if (host_af == AF_INET6)
462   {
463   s_ptr = (struct sockaddr *)&s_in6;
464   s_len = sizeof(s_in6);
465   }
466 else
467 #endif
468
469 /* For an IPv4 address, use an IPv4 sockaddr structure,
470 even on an IPv6 system. */
471
472   {
473   s_ptr = (struct sockaddr *)&s_in4;
474   s_len = sizeof(s_in4);
475   }
476
477 printf("Connecting to %s port %d ... ", address, port);
478
479 sock = socket(host_af, SOCK_STREAM, 0);
480 if (sock < 0)
481   {
482   printf("socket creation failed: %s\n", strerror(errno));
483   exit(1);
484   }
485
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. */
489
490 if (interface != NULL)
491   {
492   int interface_af = (strchr(interface, ':') != NULL)? AF_INET6 : AF_INET;
493
494   if (interface_af == host_af)
495     {
496     #if HAVE_IPV6
497
498     /* Set up for IPv6 binding */
499
500     if (host_af == AF_INET6)
501       {
502       memset(&s_in6, 0, sizeof(s_in6));
503       s_in6.sin6_family = AF_INET6;
504       s_in6.sin6_port = 0;
505       if (inet_pton(AF_INET6, interface, &s_in6.sin6_addr) != 1)
506         {
507         printf("Unable to parse \"%s\"", interface);
508         exit(1);
509         }
510       }
511     else
512     #endif
513
514     /* Set up for IPv4 binding */
515
516       {
517       memset(&s_in4, 0, sizeof(s_in4));
518       s_in4.sin_family = AF_INET;
519       s_in4.sin_port = 0;
520       s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(interface);
521       }
522
523     /* Bind */
524
525     if (bind(sock, s_ptr, s_len) < 0)
526       {
527       printf("Unable to bind outgoing SMTP call to %s: %s",
528         interface, strerror(errno));
529       exit(1);
530       }
531     }
532   }
533
534 /* Set up a remote IPv6 address */
535
536 #if HAVE_IPV6
537 if (host_af == AF_INET6)
538   {
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)
543     {
544     printf("Unable to parse \"%s\"", address);
545     exit(1);
546     }
547   }
548 else
549 #endif
550
551 /* Set up a remote IPv4 address */
552
553   {
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);
558   }
559
560 /* SIGALRM handler crashes out */
561
562 signal(SIGALRM, sigalrm_handler_crash);
563 alarm(timeout);
564 rc = connect(sock, s_ptr, s_len);
565 save_errno = errno;
566 alarm(0);
567
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. */
570
571 if (rc < 0)
572   {
573   close(sock);
574   printf("failed: %s\n", strerror(save_errno));
575   exit(1);
576   }
577
578 printf("connected\n");
579
580
581 /* --------------- Set up for OpenSSL --------------- */
582
583 #ifdef HAVE_OPENSSL
584 SSL_library_init();
585 SSL_load_error_strings();
586
587 ctx = SSL_CTX_new(SSLv23_method());
588 if (ctx == NULL)
589   {
590   printf ("SSL_CTX_new failed\n");
591   exit(1);
592   }
593
594 if (certfile != NULL)
595   {
596   if (!SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM))
597     {
598     printf("SSL_CTX_use_certificate_file failed\n");
599     exit(1);
600     }
601   printf("Certificate file = %s\n", certfile);
602   }
603
604 if (keyfile != NULL)
605   {
606   if (!SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))
607     {
608     printf("SSL_CTX_use_PrivateKey_file failed\n");
609     exit(1);
610     }
611   printf("Key file = %s\n", keyfile);
612   }
613
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);
617 #endif
618
619
620 /* --------------- Set up for GnuTLS --------------- */
621
622 #ifdef HAVE_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);
628
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
633 one wants. */
634
635 signal(SIGPIPE, SIG_IGN);
636 #endif
637
638 /* ---------------------------------------------- */
639
640
641 /* Start TLS session if configured to do so without STARTTLS */
642
643 #ifdef HAVE_TLS
644 if (tls_on_connect)
645   {
646   printf("Attempting to start TLS\n");
647
648   #ifdef HAVE_OPENSSL
649   tls_active = tls_start(sock, &ssl, ctx);
650   #endif
651
652   #ifdef HAVE_GNUTLS
653   sigalrm_seen = FALSE;
654   alarm(timeout);
655   tls_active = gnutls_handshake(tls_session) >= 0;
656   alarm(0);
657   #endif
658
659   if (!tls_active)
660     printf("Failed to start TLS\n");
661   else
662     printf("Succeeded in starting TLS\n");
663   }
664 #endif
665
666 while (fgets(outbuffer, sizeof(outbuffer), stdin) != NULL)
667   {
668   int n = (int)strlen(outbuffer);
669   while (n > 0 && isspace(outbuffer[n-1])) n--;
670   outbuffer[n] = 0;
671
672   /* Expect incoming */
673
674   if (strncmp(outbuffer, "??? ", 4) == 0)
675     {
676     unsigned char *lineptr;
677     printf("%s\n", outbuffer);
678
679     if (*inptr == 0)   /* Refill input buffer */
680       {
681       if (tls_active)
682         {
683         #ifdef HAVE_OPENSSL
684         rc = SSL_read (ssl, inbuffer, sizeof(inbuffer) - 1);
685         #endif
686         #ifdef HAVE_GNUTLS
687         rc = gnutls_record_recv(tls_session, CS inbuffer, sizeof(inbuffer) - 1);
688         #endif
689         }
690       else
691         {
692         alarm(timeout);
693         rc = read(sock, inbuffer, sizeof(inbuffer));
694         alarm(0);
695         }
696
697       if (rc < 0)
698         {
699         printf("Read error %s\n", strerror(errno));
700         exit(1)  ;
701         }
702       else if (rc == 0)
703         {
704         printf("Unexpected EOF read\n");
705         close(sock);
706         exit(1);
707         }
708       else
709         {
710         inbuffer[rc] = 0;
711         inptr = inbuffer;
712         }
713       }
714
715     lineptr = inptr;
716     while (*inptr != 0 && *inptr != '\r' && *inptr != '\n') inptr++;
717     if (*inptr != 0)
718       {
719       *inptr++ = 0;
720       if (*inptr == '\n') inptr++;
721       }
722
723     printf("<<< %s\n", lineptr);
724     if (strncmp(lineptr, outbuffer + 4, (int)strlen(outbuffer) - 4) != 0)
725       {
726       printf("\n******** Input mismatch ********\n");
727       exit(1);
728       }
729
730     #ifdef HAVE_TLS
731     if (sent_starttls)
732       {
733       if (lineptr[0] == '2')
734         {
735         printf("Attempting to start TLS\n");
736         fflush(stdout);
737
738         #ifdef HAVE_OPENSSL
739         tls_active = tls_start(sock, &ssl, ctx);
740         #endif
741
742         #ifdef HAVE_GNUTLS
743         sigalrm_seen = FALSE;
744         alarm(timeout);
745         tls_active = gnutls_handshake(tls_session) >= 0;
746         alarm(0);
747         #endif
748
749         if (!tls_active)
750           {
751           printf("Failed to start TLS\n");
752           fflush(stdout);
753           }
754         else
755           printf("Succeeded in starting TLS\n");
756         }
757       else printf("Abandoning TLS start attempt\n");
758       }
759     sent_starttls = 0;
760     #endif
761     }
762
763   /* Wait for a bit before proceeding */
764
765   else if (strncmp(outbuffer, "+++ ", 4) == 0)
766     {
767     printf("%s\n", outbuffer);
768     sleep(atoi(outbuffer + 4));
769     }
770
771   /* Send outgoing, but barf if unconsumed incoming */
772
773   else
774     {
775     unsigned char *escape;
776
777     if (*inptr != 0)
778       {
779       printf("Unconsumed input: %s", inptr);
780       printf("   About to send: %s\n", outbuffer);
781       exit(1);
782       }
783
784     #ifdef HAVE_TLS
785
786     /* Shutdown TLS */
787
788     if (strcmp(outbuffer, "stoptls") == 0 ||
789         strcmp(outbuffer, "STOPTLS") == 0)
790       {
791       if (!tls_active)
792         {
793         printf("STOPTLS read when TLS not active\n");
794         exit(1);
795         }
796       printf("Shutting down TLS encryption\n");
797
798       #ifdef HAVE_OPENSSL
799       SSL_shutdown(ssl);
800       SSL_free(ssl);
801       #endif
802
803       #ifdef HAVE_GNUTLS
804       gnutls_bye(tls_session, GNUTLS_SHUT_WR);
805       gnutls_deinit(tls_session);
806       tls_session = NULL;
807       gnutls_global_deinit();
808       #endif
809
810       tls_active = 0;
811       continue;
812       }
813
814     /* Remember that we sent STARTTLS */
815
816     sent_starttls = (strcmp(outbuffer, "starttls") == 0 ||
817                      strcmp(outbuffer, "STARTTLS") == 0);
818
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. */
822
823     if (strcmp(outbuffer, "starttls_wait") == 0)
824       {
825       outbuffer[8] = 0;
826       n = 8;
827       }
828     #endif
829
830     printf(">>> %s\n", outbuffer);
831     strcpy(outbuffer + n, "\r\n");
832
833     /* Turn "\n" and "\r" into the relevant characters. This is a hack. */
834
835     while ((escape = strstr(outbuffer, "\\r")) != NULL)
836       {
837       *escape = '\r';
838       memmove(escape + 1, escape + 2,  (n + 2) - (escape - outbuffer) - 2);
839       n--;
840       }
841
842     while ((escape = strstr(outbuffer, "\\n")) != NULL)
843       {
844       *escape = '\n';
845       memmove(escape + 1, escape + 2,  (n + 2) - (escape - outbuffer) - 2);
846       n--;
847       }
848
849     /* OK, do it */
850
851     alarm(timeout);
852     if (tls_active)
853       {
854       #ifdef HAVE_OPENSSL
855         rc = SSL_write (ssl, outbuffer, n + 2);
856       #endif
857       #ifdef HAVE_GNUTLS
858         rc = gnutls_record_send(tls_session, CS outbuffer, n + 2);
859         if (rc < 0)
860           {
861           printf("GnuTLS write error: %s\n", gnutls_strerror(rc));
862           exit(1);
863           }
864       #endif
865       }
866     else
867       {
868       rc = write(sock, outbuffer, n + 2);
869       }
870     alarm(0);
871
872     if (rc < 0)
873       {
874       printf("Write error: %s\n", strerror(errno));
875       exit(1);
876       }
877     }
878   }
879
880 printf("End of script\n");
881 close(sock);
882
883 exit(0);
884 }
885
886 /* End of client.c */