Ensure socket is nonblocking before draining. Bug 1914
[exim.git] / test / src / server.c
1 /* A little hacked up program that listens on a given port and allows a script
2 to play the part of a remote MTA for testing purposes. This scripted version is
3 hacked from my original interactive version. A further hack allows it to listen
4 on a Unix domain socket as an alternative to a TCP/IP port.
5
6 In an IPv6 world, listening happens on both an IPv6 and an IPv4 socket, always
7 on all interfaces, unless the option -noipv6 is given. */
8
9 /* ANSI C standard includes */
10
11 #include <ctype.h>
12 #include <signal.h>
13 #include <stdarg.h>
14 #include <stddef.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <time.h>
19
20 /* Unix includes */
21
22 #include <errno.h>
23 #include <dirent.h>
24 #include <sys/types.h>
25
26 #include <netinet/in_systm.h>
27 #include <netinet/in.h>
28 #include <netinet/ip.h>
29
30 #ifdef HAVE_NETINET_IP_VAR_H
31 # include <netinet/ip_var.h>
32 #endif
33
34 #include <netdb.h>
35 #include <arpa/inet.h>
36 #include <sys/time.h>
37 #include <sys/resource.h>
38 #include <sys/socket.h>
39 #include <sys/un.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <utime.h>
44
45 #ifdef AF_INET6
46 # define HAVE_IPV6 1
47 #endif
48
49 #ifndef S_ADDR_TYPE
50 # define S_ADDR_TYPE u_long
51 #endif
52
53 #ifndef CS
54 # define CS (char *)
55 #endif
56
57
58 typedef struct line {
59 struct line *next;
60 unsigned len;
61 char line[1];
62 } line;
63
64 typedef unsigned BOOL;
65 #define FALSE 0
66 #define TRUE 1
67
68
69 /*************************************************
70 * SIGALRM handler - crash out *
71 *************************************************/
72 int tmo_noerror = 0;
73
74 static void
75 sigalrm_handler(int sig)
76 {
77 sig = sig; /* Keep picky compilers happy */
78 printf("\nServer timed out\n");
79 exit(tmo_noerror ? 0 : 99);
80 }
81
82
83 /*************************************************
84 * Get textual IP address *
85 *************************************************/
86
87 /* This function is copied from Exim */
88
89 char *
90 host_ntoa(const void *arg, char *buffer)
91 {
92 char *yield;
93
94 /* The new world. It is annoying that we have to fish out the address from
95 different places in the block, depending on what kind of address it is. It
96 is also a pain that inet_ntop() returns a const char *, whereas the IPv4
97 function inet_ntoa() returns just char *, and some picky compilers insist
98 on warning if one assigns a const char * to a char *. Hence the casts. */
99
100 #if HAVE_IPV6
101 char addr_buffer[46];
102 int family = ((struct sockaddr *)arg)->sa_family;
103 if (family == AF_INET6)
104 {
105 struct sockaddr_in6 *sk = (struct sockaddr_in6 *)arg;
106 yield = (char *)inet_ntop(family, &(sk->sin6_addr), addr_buffer,
107 sizeof(addr_buffer));
108 }
109 else
110 {
111 struct sockaddr_in *sk = (struct sockaddr_in *)arg;
112 yield = (char *)inet_ntop(family, &(sk->sin_addr), addr_buffer,
113 sizeof(addr_buffer));
114 }
115
116 /* If the result is a mapped IPv4 address, show it in V4 format. */
117
118 if (strncmp(yield, "::ffff:", 7) == 0) yield += 7;
119
120 #else /* HAVE_IPV6 */
121
122 /* The old world */
123
124 yield = inet_ntoa(((struct sockaddr_in *)arg)->sin_addr);
125 #endif
126
127 strcpy(buffer, yield);
128 return buffer;
129 }
130
131
132
133 static void
134 printit(char * s, int n)
135 {
136 while(n--)
137 {
138 unsigned char c = *s++;
139 if (c == '\\')
140 printf("\\\\");
141 else if (c >= ' ' && c <= '~') /* assumes ascii */
142 putchar(c);
143 else
144 printf("\\x%02x", c);
145 }
146 putchar('\n');
147 }
148
149
150
151 /*************************************************
152 * Main Program *
153 *************************************************/
154
155 #define v6n 0 /* IPv6 socket number */
156 #define v4n 1 /* IPv4 socket number */
157 #define udn 2 /* Unix domain socket number */
158 #define skn 2 /* Potential number of sockets */
159
160 int main(int argc, char **argv)
161 {
162 int i;
163 int port = 0;
164 int listen_socket[3] = { -1, -1, -1 };
165 int accept_socket;
166 int dup_accept_socket;
167 int connection_count = 1;
168 int count;
169 int on = 1;
170 int timeout = 5;
171 int initial_pause = 0;
172 int use_ipv4 = 1;
173 int use_ipv6 = 1;
174 int debug = 0;
175 int na = 1;
176 line *script = NULL;
177 line *last = NULL;
178 line *s;
179 FILE *in, *out;
180 int linebuf = 1;
181 char *pidfile = NULL;
182
183 char *sockname = NULL;
184 unsigned char buffer[10240];
185
186 struct sockaddr_un sockun; /* don't use "sun" */
187 struct sockaddr_un sockun_accepted;
188 int sockun_len = sizeof(sockun_accepted);
189
190 #if HAVE_IPV6
191 struct sockaddr_in6 sin6;
192 struct sockaddr_in6 accepted;
193 struct in6_addr anyaddr6 = IN6ADDR_ANY_INIT ;
194 #else
195 struct sockaddr_in accepted;
196 #endif
197
198 /* Always need an IPv4 structure */
199
200 struct sockaddr_in sin4;
201
202 int len = sizeof(accepted);
203
204
205 /* Sort out the arguments */
206 if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
207 {
208 printf("Usage: %s [options] port|socket [connection count]\n", argv[0]);
209 puts("Options"
210 "\n\t-d debug"
211 "\n\t-i n n seconds initial delay"
212 "\n\t-noipv4 disable ipv4"
213 "\n\t-noipv6 disable ipv6"
214 "\n\t-oP file write PID to file"
215 "\n\t-t n n seconds timeout"
216 );
217 exit(0);
218 }
219
220 while (na < argc && argv[na][0] == '-')
221 {
222 if (strcmp(argv[na], "-d") == 0) debug = 1;
223 else if (strcmp(argv[na], "-t") == 0)
224 {
225 if (tmo_noerror = ((timeout = atoi(argv[++na])) < 0)) timeout = -timeout;
226 }
227 else if (strcmp(argv[na], "-i") == 0) initial_pause = atoi(argv[++na]);
228 else if (strcmp(argv[na], "-noipv4") == 0) use_ipv4 = 0;
229 else if (strcmp(argv[na], "-noipv6") == 0) use_ipv6 = 0;
230 else if (strcmp(argv[na], "-oP") == 0) pidfile = argv[++na];
231 else
232 {
233 printf("server: unknown option %s, try -h or --help\n", argv[na]);
234 exit(1);
235 }
236 na++;
237 }
238
239 if (!use_ipv4 && !use_ipv6)
240 {
241 printf("server: -noipv4 and -noipv6 cannot both be given\n");
242 exit(1);
243 }
244
245 if (na >= argc)
246 {
247 printf("server: no port number or socket name given\n");
248 exit(1);
249 }
250
251 if (argv[na][0] == '/')
252 {
253 sockname = argv[na];
254 unlink(sockname); /* in case left lying around */
255 }
256 else port = atoi(argv[na]);
257 na++;
258
259 if (na < argc) connection_count = atoi(argv[na]);
260
261
262 /* Initial pause (before creating listen sockets */
263 if (initial_pause > 0)
264 {
265 if (debug)
266 printf("%d: Inital pause of %d seconds\n", time(NULL), initial_pause);
267 else
268 printf("Inital pause of %d seconds\n", initial_pause);
269 while (initial_pause > 0)
270 initial_pause = sleep(initial_pause);
271 }
272
273 /* Create sockets */
274
275 if (port == 0) /* Unix domain */
276 {
277 if (debug) printf("%d: Creating Unix domain socket\n", time(NULL));
278 listen_socket[udn] = socket(PF_UNIX, SOCK_STREAM, 0);
279 if (listen_socket[udn] < 0)
280 {
281 printf("Unix domain socket creation failed: %s\n", strerror(errno));
282 exit(1);
283 }
284 }
285 else
286 {
287 #if HAVE_IPV6
288 if (use_ipv6)
289 {
290 if (debug) printf("Creating IPv6 socket\n");
291 listen_socket[v6n] = socket(AF_INET6, SOCK_STREAM, 0);
292 if (listen_socket[v6n] < 0)
293 {
294 printf("IPv6 socket creation failed: %s\n", strerror(errno));
295 exit(1);
296 }
297
298 /* If this is an IPv6 wildcard socket, set IPV6_V6ONLY if that option is
299 available. */
300
301 #ifdef IPV6_V6ONLY
302 if (setsockopt(listen_socket[v6n], IPPROTO_IPV6, IPV6_V6ONLY, (char *)(&on),
303 sizeof(on)) < 0)
304 printf("Setting IPV6_V6ONLY on IPv6 wildcard "
305 "socket failed (%s): carrying on without it\n", strerror(errno));
306 #endif /* IPV6_V6ONLY */
307 }
308 #endif /* HAVE_IPV6 */
309
310 /* Create an IPv4 socket if required */
311
312 if (use_ipv4)
313 {
314 if (debug) printf("Creating IPv4 socket\n");
315 listen_socket[v4n] = socket(AF_INET, SOCK_STREAM, 0);
316 if (listen_socket[v4n] < 0)
317 {
318 printf("IPv4 socket creation failed: %s\n", strerror(errno));
319 exit(1);
320 }
321 }
322 }
323
324
325 /* Set SO_REUSEADDR on the IP sockets so that the program can be restarted
326 while a connection is being handled - this can happen as old connections lie
327 around for a bit while crashed processes are tidied away. Without this, a
328 connection will prevent reuse of the smtp port for listening. */
329
330 for (i = v6n; i <= v4n; i++)
331 {
332 if (listen_socket[i] >= 0 &&
333 setsockopt(listen_socket[i], SOL_SOCKET, SO_REUSEADDR, (char *)(&on),
334 sizeof(on)) < 0)
335 {
336 printf("setting SO_REUSEADDR on socket failed: %s\n", strerror(errno));
337 exit(1);
338 }
339 }
340
341
342 /* Now bind the sockets to the required port or path. If a path, ensure
343 anyone can write to it. */
344
345 if (port == 0)
346 {
347 struct stat statbuf;
348 sockun.sun_family = AF_UNIX;
349 if (debug) printf("Binding Unix domain socket\n");
350 sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1), sockname);
351 if (bind(listen_socket[udn], (struct sockaddr *)&sockun, sizeof(sockun)) < 0)
352 {
353 printf("Unix domain socket bind() failed: %s\n", strerror(errno));
354 exit(1);
355 }
356 (void)stat(sockname, &statbuf);
357 if (debug) printf("Setting Unix domain socket mode: %0x\n",
358 statbuf.st_mode | 0777);
359 if (chmod(sockname, statbuf.st_mode | 0777) < 0)
360 {
361 printf("Unix domain socket chmod() failed: %s\n", strerror(errno));
362 exit(1);
363 }
364 }
365
366 else
367 {
368 for (i = 0; i < skn; i++)
369 {
370 if (listen_socket[i] < 0) continue;
371
372 /* For an IPv6 listen, use an IPv6 socket */
373
374 #if HAVE_IPV6
375 if (i == v6n)
376 {
377 memset(&sin6, 0, sizeof(sin6));
378 sin6.sin6_family = AF_INET6;
379 sin6.sin6_port = htons(port);
380 sin6.sin6_addr = anyaddr6;
381 if (bind(listen_socket[i], (struct sockaddr *)&sin6, sizeof(sin6)) < 0)
382 {
383 printf("IPv6 socket bind() failed: %s\n", strerror(errno));
384 exit(1);
385 }
386 }
387 else
388 #endif
389
390 /* For an IPv4 bind, use an IPv4 socket, even in an IPv6 world. If an IPv4
391 bind fails EADDRINUSE after IPv6 success, carry on, because it means the
392 IPv6 socket will handle IPv4 connections. */
393
394 {
395 memset(&sin4, 0, sizeof(sin4));
396 sin4.sin_family = AF_INET;
397 sin4.sin_addr.s_addr = (S_ADDR_TYPE)INADDR_ANY;
398 sin4.sin_port = htons(port);
399 if (bind(listen_socket[i], (struct sockaddr *)&sin4, sizeof(sin4)) < 0)
400 if (listen_socket[v6n] < 0 || errno != EADDRINUSE)
401 {
402 printf("IPv4 socket bind() failed: %s\n", strerror(errno));
403 exit(1);
404 }
405 else
406 {
407 close(listen_socket[i]);
408 listen_socket[i] = -1;
409 }
410 }
411 }
412 }
413
414
415 /* Start listening. If IPv4 fails EADDRINUSE after IPv6 succeeds, ignore the
416 error because it means that the IPv6 socket will handle IPv4 connections. Don't
417 output anything, because it will mess up the test output, which will be
418 different for systems that do this and those that don't. */
419
420 for (i = 0; i <= skn; i++)
421 {
422 if (listen_socket[i] >= 0 && listen(listen_socket[i], 5) < 0)
423 {
424 if (i != v4n || listen_socket[v6n] < 0 || errno != EADDRINUSE)
425 {
426 printf("listen() failed: %s\n", strerror(errno));
427 exit(1);
428 }
429 }
430 }
431
432
433 if (pidfile)
434 {
435 FILE * p;
436 if (!(p = fopen(pidfile, "w")))
437 {
438 fprintf(stderr, "pidfile create failed: %s\n", strerror(errno));
439 exit(1);
440 }
441 fprintf(p, "%ld\n", (long)getpid());
442 fclose(p);
443 }
444
445 /* This program handles only a fixed number of connections, in sequence. Before
446 waiting for the first connection, read the standard input, which contains the
447 script of things to do. A line containing "++++" is treated as end of file.
448 This is so that the Perl driving script doesn't have to close the pipe -
449 because that would cause it to wait for this process, which it doesn't yet want
450 to do. The driving script adds the "++++" automatically - it doesn't actually
451 appear in the test script. Within lines we interpret \xNN and \\ groups */
452
453 while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
454 {
455 line *next;
456 char * d;
457 int n = (int)strlen(CS buffer);
458
459 if (n > 1 && buffer[0] == '>' && buffer[1] == '>')
460 linebuf = 0;
461 while (n > 0 && isspace(buffer[n-1])) n--;
462 buffer[n] = 0;
463 if (strcmp(CS buffer, "++++") == 0) break;
464 next = malloc(sizeof(line) + n);
465 next->next = NULL;
466 d = next->line;
467 {
468 char * s = CS buffer;
469 do
470 {
471 char ch;
472 char cl = *s;
473 if (cl == '\\' && (cl = *++s) == 'x')
474 {
475 if ((ch = *++s - '0') > 9 && (ch -= 'A'-'9'-1) > 15) ch -= 'a'-'A';
476 if ((cl = *++s - '0') > 9 && (cl -= 'A'-'9'-1) > 15) cl -= 'a'-'A';
477 cl |= ch << 4;
478 }
479 *d++ = cl;
480 }
481 while (*s++);
482 }
483 next->len = d - next->line - 1;
484 if (last == NULL) script = last = next;
485 else last->next = next;
486 last = next;
487 }
488
489 fclose(stdin);
490
491 /* SIGALRM handler crashes out */
492
493 signal(SIGALRM, sigalrm_handler);
494
495 /* s points to the current place in the script */
496
497 s = script;
498
499 for (count = 0; count < connection_count; count++)
500 {
501 struct {
502 int left;
503 BOOL in_use;
504 } content_length = { 0, FALSE };
505
506 alarm(timeout);
507 if (port <= 0)
508 {
509 printf("Listening on %s ... ", sockname);
510 fflush(stdout);
511 accept_socket = accept(listen_socket[udn],
512 (struct sockaddr *)&sockun_accepted, &sockun_len);
513 }
514
515 else
516 {
517 int lcount;
518 int max_socket = 0;
519 fd_set select_listen;
520
521 printf("Listening on port %d ... ", port);
522 fflush(stdout);
523
524 FD_ZERO(&select_listen);
525 for (i = 0; i < skn; i++)
526 {
527 if (listen_socket[i] >= 0) FD_SET(listen_socket[i], &select_listen);
528 if (listen_socket[i] > max_socket) max_socket = listen_socket[i];
529 }
530
531 if ((lcount = select(max_socket + 1, &select_listen, NULL, NULL, NULL)) < 0)
532 {
533 printf("Select failed\n");
534 fflush(stdout);
535 continue;
536 }
537
538 accept_socket = -1;
539 for (i = 0; i < skn; i++)
540 if (listen_socket[i] > 0 && FD_ISSET(listen_socket[i], &select_listen))
541 {
542 accept_socket = accept(listen_socket[i],
543 (struct sockaddr *)&accepted, &len);
544 FD_CLR(listen_socket[i], &select_listen);
545 break;
546 }
547 }
548 alarm(0);
549
550 if (accept_socket < 0)
551 {
552 printf("accept() failed: %s\n", strerror(errno));
553 exit(1);
554 }
555
556 out = fdopen(accept_socket, "w");
557
558 dup_accept_socket = dup(accept_socket);
559
560 if (port > 0)
561 printf("\nConnection request from [%s]\n", host_ntoa(&accepted, CS buffer));
562 else
563 {
564 printf("\nConnection request\n");
565
566 /* Linux supports a feature for acquiring the peer's credentials, but it
567 appears to be Linux-specific. This code is untested and unused, just
568 saved here for reference. */
569
570 /**********--------------------
571 struct ucred cr;
572 int cl=sizeof(cr);
573
574 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl)==0) {
575 printf("Peer's pid=%d, uid=%d, gid=%d\n",
576 cr.pid, cr.uid, cr.gid);
577 --------------*****************/
578 }
579 fflush(stdout);
580
581 if (dup_accept_socket < 0)
582 {
583 printf("Couldn't dup socket descriptor\n");
584 printf("421 Connection refused: %s\n", strerror(errno));
585 fprintf(out, "421 Connection refused: %s\r\n", strerror(errno));
586 fclose(out);
587 exit(2);
588 }
589
590 in = fdopen(dup_accept_socket, "r");
591
592 /* Loop for handling the conversation(s). For use in SMTP sessions, there are
593 default rules for determining input and output lines: the latter start with
594 digits. This means that the input looks like SMTP dialog. However, this
595 doesn't work for other tests (e.g. ident tests) so we have explicit '<' and
596 '>' flags for input and output as well as the defaults. */
597
598 for (; s; s = s->next)
599 {
600 char *ss = s->line;
601
602 /* Output lines either start with '>' or a digit. In the '>' case we can
603 fudge the sending of \r\n as required. Default is \r\n, ">>" send nothing,
604 ">CR>" sends \r only, and ">LF>" sends \n only. We can also force a
605 connection closedown by ">*eof". */
606
607 if (ss[0] == '>')
608 {
609 char *end = "\r\n";
610 unsigned len = s->len;
611 printit(ss++, len--);
612
613 if (strncmp(ss, "*eof", 4) == 0)
614 {
615 s = s->next;
616 goto END_OFF;
617 }
618
619 if (*ss == '>')
620 { end = ""; ss++; len--; }
621 else if (strncmp(ss, "CR>", 3) == 0)
622 { end = "\r"; ss += 3; len -= 3; }
623 else if (strncmp(ss, "LF>", 3) == 0)
624 { end = "\n"; ss += 3; len -= 3; }
625
626 fwrite(ss, 1, len, out);
627 if (*end) fprintf(out, end);
628 }
629
630 else if (isdigit((unsigned char)ss[0]))
631 {
632 printf("%s\n", ss);
633 fprintf(out, "%s\r\n", ss);
634 }
635
636 /* If the script line starts with "*sleep" we just sleep for a while
637 before continuing. */
638
639 else if (strncmp(ss, "*sleep ", 7) == 0)
640 {
641 int sleepfor = atoi(ss+7);
642 printf("%s\n", ss);
643 fflush(out);
644 sleep(sleepfor);
645 }
646
647 /* If the script line starts with "*data " we expect a numeric argument,
648 and we expect to read (and discard) that many data bytes from the input. */
649
650 else if (strncmp(ss, "*data ", 6) == 0)
651 {
652 int dlen = atoi(ss+6);
653 int n;
654
655 alarm(timeout);
656
657 if (!linebuf)
658 while (dlen > 0)
659 {
660 n = dlen < sizeof(buffer) ? dlen : sizeof(buffer);
661 if ((n = read(dup_accept_socket, CS buffer, n)) == 0)
662 {
663 printf("Unxpected EOF read from client\n");
664 s = s->next;
665 goto END_OFF;
666 }
667 dlen -= n;
668 }
669 else
670 while (dlen-- > 0)
671 if (fgetc(in) == EOF)
672 {
673 printf("Unxpected EOF read from client\n");
674 s = s->next;
675 goto END_OFF;
676 }
677 }
678
679 /* Otherwise the script line is the start of an input line we are expecting
680 from the client, or "*eof" indicating we expect the client to close the
681 connection. Read command line or data lines; the latter are indicated
682 by the expected line being just ".". If the line starts with '<', that
683 doesn't form part of the expected input. (This allows for incoming data
684 starting with a digit.) If the line starts with '<<' we operate in
685 unbuffered rather than line mode and assume that a single read gets the
686 entire message. */
687
688 else
689 {
690 int offset;
691 int data = strcmp(ss, ".") == 0;
692
693 if (ss[0] != '<')
694 offset = 0;
695 else
696 {
697 buffer[0] = '<';
698 if (ss[1] != '<')
699 offset = 1;
700 else
701 {
702 buffer[1] = '<';
703 offset = 2;
704 }
705 }
706
707 fflush(out);
708
709 if (!linebuf)
710 {
711 int n;
712 char c;
713
714 alarm(timeout);
715 n = read(dup_accept_socket, CS buffer+offset, s->len - offset);
716 if (content_length.in_use) content_length.left -= n;
717 if (n == 0)
718 {
719 printf("%sxpected EOF read from client\n",
720 (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
721 s = s->next;
722 goto END_OFF;
723 }
724 if (offset != 2)
725 while (read(dup_accept_socket, &c, 1) == 1 && c != '\n') ;
726 alarm(0);
727 n += offset;
728
729 printit(buffer, n);
730
731 if (data) do
732 {
733 n = (read(dup_accept_socket, &c, 1) == 1 && c == '.');
734 if (content_length.in_use) content_length.left--;
735 while (c != '\n' && read(dup_accept_socket, &c, 1) == 1)
736 if (content_length.in_use) content_length.left--;
737 } while (!n);
738 else if (memcmp(ss, buffer, n) != 0)
739 {
740 printf("Comparison failed - bailing out\nExpected: ");
741 printit(ss, n);
742 break;
743 }
744 }
745 else
746 {
747 for (;;)
748 {
749 int n;
750 alarm(timeout);
751 if (fgets(CS buffer+offset, sizeof(buffer)-offset, in) == NULL)
752 {
753 printf("%sxpected EOF read from client\n",
754 (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
755 s = s->next;
756 goto END_OFF;
757 }
758 alarm(0);
759 n = strlen(CS buffer);
760 if (content_length.in_use) content_length.left -= (n - offset);
761 while (n > 0 && isspace(buffer[n-1])) n--;
762 buffer[n] = 0;
763 printf("%s\n", buffer);
764 if (!data || strcmp(CS buffer, ".") == 0) break;
765 }
766
767 if (strncmp(ss, CS buffer, (int)strlen(ss)) != 0)
768 {
769 printf("Comparison failed - bailing out\n");
770 printf("Expected: %s\n", ss);
771 break;
772 }
773 }
774
775 if (sscanf(buffer, "<Content-length: %d", &content_length.left)) content_length.in_use = TRUE;
776 if (content_length.in_use && content_length.left <= 0) shutdown(dup_accept_socket, SHUT_RD);
777 }
778 }
779
780 END_OFF:
781 fclose(in);
782 fclose(out);
783 }
784
785 if (s == NULL) printf("End of script\n");
786
787 if (sockname) unlink(sockname);
788 exit(0);
789 }
790
791 /* End of server.c */