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