With a few minor exceptions for tests that had additional concerns and tests I don...
[exim.git] / test / src / client.c
CommitLineData
bb727765 1/* $Cambridge: exim/test/src/client.c,v 1.2 2006/10/16 13:42:19 ph10 Exp $ */
c55a77db
PH
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
c55a77db
PH
80
81/* Local static variables for GNUTLS */
82
c55a77db
PH
83static gnutls_dh_params dh_params = NULL;
84
85static gnutls_certificate_credentials_t x509_cred = NULL;
86static gnutls_session tls_session = NULL;
87
88static int ssl_session_timeout = 200;
89
90/* Priorities for TLS algorithms to use. */
91
92static const int protocol_priority[16] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
93
94static const int kx_priority[16] = {
95 GNUTLS_KX_RSA,
96 GNUTLS_KX_DHE_DSS,
97 GNUTLS_KX_DHE_RSA,
c55a77db
PH
98 0 };
99
100static 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
107static const int mac_priority[16] = {
108 GNUTLS_MAC_SHA,
109 GNUTLS_MAC_MD5,
110 0 };
111
112static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 };
113static 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
124static void
125sigalrm_handler_crash(int sig)
126{
127sig = sig; /* Keep picky compilers happy */
128printf("\nClient timed out\n");
129exit(99);
130}
131
132
133/*************************************************
134* SIGALRM handler - set flag *
135*************************************************/
136
137static void
138sigalrm_handler_flag(int sig)
139{
140sig = sig; /* Keep picky compilers happy */
141sigalrm_seen = 1;
142}
143
144
145
146/****************************************************************************/
147/****************************************************************************/
148
149#ifdef HAVE_OPENSSL
150/*************************************************
151* Start an OpenSSL TLS session *
152*************************************************/
153
154int tls_start(int sock, SSL **ssl, SSL_CTX *ctx)
155{
156int rc;
157static const char *sid_ctx = "exim";
158
159RAND_load_file("client.c", -1); /* Not *very* random! */
160
161*ssl = SSL_new (ctx);
162SSL_set_session_id_context(*ssl, sid_ctx, strlen(sid_ctx));
163SSL_set_fd (*ssl, sock);
164SSL_set_connect_state(*ssl);
165
166signal(SIGALRM, sigalrm_handler_flag);
167sigalrm_seen = 0;
168alarm(5);
169rc = SSL_connect (*ssl);
170alarm(0);
171
172if (sigalrm_seen)
173 {
174 printf("SSL_connect timed out\n");
175 return 0;
176 }
177
178if (rc <= 0)
179 {
180 ERR_print_errors_fp(stdout);
181 return 0;
182 }
183
184printf("SSL connection using %s\n", SSL_get_cipher (*ssl));
185return 1;
186}
187
188
189/*************************************************
190* SSL Information callback *
191*************************************************/
192
193static void
194info_callback(SSL *s, int where, int ret)
195{
196where = where;
197ret = ret;
198printf("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
213the TLS handshake, that is, while the session is still in clear.
214
215Argument:
216 prefix prefix text
217 err a GnuTLS error number, or 0 if local error
218
219Returns: doesn't - it dies
220*/
221
222static void
223gnutls_error(uschar *prefix, int err)
224{
bb727765 225fprintf(stderr, "GnuTLS connection error: %s:", prefix);
c55a77db
PH
226if (err != 0) fprintf(stderr, " %s", gnutls_strerror(err));
227fprintf(stderr, "\n");
228exit(1);
229}
230
231
232
233/*************************************************
bb727765 234* Setup up DH parameters *
c55a77db
PH
235*************************************************/
236
237/* For the test suite, the parameters should always be available in the spool
238directory. */
239
240static void
bb727765 241init_dh(void)
c55a77db
PH
242{
243int fd;
244int ret;
245gnutls_datum m;
246uschar filename[200];
247struct stat statbuf;
248
249/* Initialize the data structures for holding the parameters */
250
c55a77db
PH
251ret = gnutls_dh_params_init(&dh_params);
252if (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
bb727765 255parameters. */
c55a77db
PH
256
257fd = open("aux-fixed/gnutls-params", O_RDONLY, 0);
258if (fd < 0)
259 {
260 fprintf(stderr, "Failed to open spool/gnutls-params: %s\n", strerror(errno));
261 exit(1);
262 }
263
264if (fstat(fd, &statbuf) < 0)
265 {
266 (void)close(fd);
267 return gnutls_error(US"TLS cache stat failed", 0);
268 }
269
270m.size = statbuf.st_size;
271m.data = malloc(m.size);
272if (m.data == NULL)
273 return gnutls_error(US"memory allocation failed", 0);
274if (read(fd, m.data, m.size) != m.size)
275 return gnutls_error(US"TLS cache read failed", 0);
276(void)close(fd);
277
c55a77db
PH
278ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM);
279if (ret < 0) return gnutls_error(US"DH params import", ret);
280free(m.data);
281}
282
283
284
285
286/*************************************************
287* Initialize for GnuTLS *
288*************************************************/
289
290/*
291Arguments:
292 certificate certificate file
293 privatekey private key file
294*/
295
296static void
297tls_init(uschar *certificate, uschar *privatekey)
298{
299int rc;
300
301rc = gnutls_global_init();
302if (rc < 0) gnutls_error(US"gnutls_global_init", rc);
303
bb727765 304/* Read D-H parameters from the cache file. */
c55a77db 305
bb727765 306init_dh();
c55a77db
PH
307
308/* Create the credentials structure */
309
310rc = gnutls_certificate_allocate_credentials(&x509_cred);
311if (rc < 0) gnutls_error(US"certificate_allocate_credentials", rc);
312
313/* Set the certificate and private keys */
314
315if (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
324gnutls_certificate_set_dh_params(x509_cred, dh_params);
c55a77db
PH
325}
326
327
328
329/*************************************************
330* Initialize a single GNUTLS session *
331*************************************************/
332
333static gnutls_session
334tls_session_init(void)
335{
336gnutls_session session;
337
338gnutls_init(&session, GNUTLS_CLIENT);
339
340gnutls_cipher_set_priority(session, default_cipher_priority);
341gnutls_compression_set_priority(session, comp_priority);
342gnutls_kx_set_priority(session, kx_priority);
343gnutls_protocol_set_priority(session, protocol_priority);
344gnutls_mac_set_priority(session, mac_priority);
345
346gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
347
348gnutls_dh_set_prime_bits(session, DH_BITS);
349gnutls_db_set_cache_expiration(session, ssl_session_timeout);
350
351return 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
374int main(int argc, char **argv)
375{
376struct sockaddr *s_ptr;
377struct sockaddr_in s_in4;
378char *interface = NULL;
379char *address = NULL;
380char *certfile = NULL;
381char *keyfile = NULL;
382int argi = 1;
383int host_af, port, s_len, rc, sock, save_errno;
384int timeout = 1;
385int tls_active = 0;
386int sent_starttls = 0;
387int tls_on_connect = 0;
388
389#if HAVE_IPV6
390struct sockaddr_in6 s_in6;
391#endif
392
393#ifdef HAVE_OPENSSL
394SSL_CTX* ctx;
395SSL* ssl;
396#endif
397
398unsigned char outbuffer[10240];
399unsigned char inbuffer[10240];
400unsigned char *inptr = inbuffer;
401
402*inptr = 0; /* Buffer empty */
403
404/* Options */
405
406while (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
427if (argc < argi+1)
428 {
429 printf("No IP address given\n");
430 exit(1);
431 }
432
433address = argv[argi++];
434host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET;
435
436/* Mandatory 2nd arg is port */
437
438if (argc < argi+1)
439 {
440 printf("No port number given\n");
441 exit(1);
442 }
443
444port = atoi(argv[argi++]);
445
446/* Optional next arg is interface */
447
448if (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
454if (argc > argi) certfile = argv[argi++];
455if (argc > argi) keyfile = argv[argi++];
456
457
458#if HAVE_IPV6
459/* For an IPv6 address, use an IPv6 sockaddr structure. */
460
461if (host_af == AF_INET6)
462 {
463 s_ptr = (struct sockaddr *)&s_in6;
464 s_len = sizeof(s_in6);
465 }
466else
467#endif
468
469/* For an IPv4 address, use an IPv4 sockaddr structure,
470even on an IPv6 system. */
471
472 {
473 s_ptr = (struct sockaddr *)&s_in4;
474 s_len = sizeof(s_in4);
475 }
476
477printf("Connecting to %s port %d ... ", address, port);
478
479sock = socket(host_af, SOCK_STREAM, 0);
480if (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
487to be of the same family as the address we are calling. On an IPv4 system the
488test is redundant, but it keeps the code tidier. */
489
490if (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
537if (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 }
548else
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
562signal(SIGALRM, sigalrm_handler_crash);
563alarm(timeout);
564rc = connect(sock, s_ptr, s_len);
565save_errno = errno;
566alarm(0);
567
568/* A failure whose error code is "Interrupted system call" is in fact
569an externally applied timeout if the signal handler has been run. */
570
571if (rc < 0)
572 {
573 close(sock);
574 printf("failed: %s\n", strerror(save_errno));
575 exit(1);
576 }
577
578printf("connected\n");
579
580
581/* --------------- Set up for OpenSSL --------------- */
582
583#ifdef HAVE_OPENSSL
584SSL_library_init();
585SSL_load_error_strings();
586
587ctx = SSL_CTX_new(SSLv23_method());
588if (ctx == NULL)
589 {
590 printf ("SSL_CTX_new failed\n");
591 exit(1);
592 }
593
594if (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
604if (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
614SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
615SSL_CTX_set_timeout(ctx, 200);
616SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
617#endif
618
619
620/* --------------- Set up for GnuTLS --------------- */
621
622#ifdef HAVE_GNUTLS
623if (certfile != NULL) printf("Certificate file = %s\n", certfile);
624if (keyfile != NULL) printf("Key file = %s\n", keyfile);
625tls_init(certfile, keyfile);
626tls_session = tls_session_init();
627gnutls_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,
630there is a SIGPIPE error in the gnutls_handshake() function for some reason
631that is not understood. As luck would have it, this has never hit Exim itself
632because it ignores SIGPIPE errors. Doing the same here allows it all to work as
633one wants. */
634
635signal(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
644if (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
666while (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
880printf("End of script\n");
881close(sock);
882
883exit(0);
884}
885
886/* End of client.c */