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