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