With a few minor exceptions for tests that had additional concerns and tests I don...
[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 */