Docs: expand TFO information
[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("%d: Inital pause of %d seconds\n", 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 #ifdef TCP_FASTOPEN
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 #ifdef TCP_FASTOPEN
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() failed: %s\n", 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() failed: %s\n", 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++)
442   {
443   if (listen_socket[i] >= 0 && listen(listen_socket[i], 5) < 0)
444     {
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   }
452
453
454 if (pidfile)
455   {
456   FILE * p;
457   if (!(p = fopen(pidfile, "w")))
458     {
459     fprintf(stderr, "pidfile create failed: %s\n", strerror(errno));
460     exit(1);
461     }
462   fprintf(p, "%ld\n", (long)getpid());
463   fclose(p);
464   }
465
466 /* This program handles only a fixed number of connections, in sequence. Before
467 waiting for the first connection, read the standard input, which contains the
468 script of things to do. A line containing "++++" is treated as end of file.
469 This is so that the Perl driving script doesn't have to close the pipe -
470 because that would cause it to wait for this process, which it doesn't yet want
471 to do. The driving script adds the "++++" automatically - it doesn't actually
472 appear in the test script. Within lines we interpret \xNN and \\ groups */
473
474 while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
475   {
476   line *next;
477   char * d;
478   int n = (int)strlen(CS buffer);
479
480   if (n > 1 && buffer[0] == '>' && buffer[1] == '>')
481     linebuf = 0;
482   while (n > 0 && isspace(buffer[n-1])) n--;
483   buffer[n] = 0;
484   if (strcmp(CS buffer, "++++") == 0) break;
485   next = malloc(sizeof(line) + n);
486   next->next = NULL;
487   d = next->line;
488     {
489     char * s = CS buffer;
490     do
491       {
492       char ch;
493       char cl = *s;
494       if (cl == '\\' && (cl = *++s) == 'x')
495         {
496         if ((ch = *++s - '0') > 9 && (ch -= 'A'-'9'-1) > 15) ch -= 'a'-'A';
497         if ((cl = *++s - '0') > 9 && (cl -= 'A'-'9'-1) > 15) cl -= 'a'-'A';
498         cl |= ch << 4;
499         }
500       *d++ = cl;
501       }
502     while (*s++);
503     }
504   next->len = d - next->line - 1;
505   if (last == NULL) script = last = next;
506     else last->next = next;
507   last = next;
508   }
509
510 fclose(stdin);
511
512 /* SIGALRM handler crashes out */
513
514 signal(SIGALRM, sigalrm_handler);
515
516 /* s points to the current place in the script */
517
518 s = script;
519
520 for (count = 0; count < connection_count; count++)
521   {
522   struct {
523     int left;
524     BOOL in_use;
525   } content_length = { 0, FALSE };
526
527   alarm(timeout);
528   if (port <= 0)
529     {
530     printf("Listening on %s ... ", sockname);
531     fflush(stdout);
532     accept_socket = accept(listen_socket[udn],
533       (struct sockaddr *)&sockun_accepted, &sockun_len);
534     }
535
536   else
537     {
538     int lcount;
539     int max_socket = 0;
540     fd_set select_listen;
541
542     printf("Listening on port %d ... ", port);
543     fflush(stdout);
544
545     FD_ZERO(&select_listen);
546     for (i = 0; i < skn; i++)
547       {
548       if (listen_socket[i] >= 0) FD_SET(listen_socket[i], &select_listen);
549       if (listen_socket[i] > max_socket) max_socket = listen_socket[i];
550       }
551
552     if ((lcount = select(max_socket + 1, &select_listen, NULL, NULL, NULL)) < 0)
553       {
554       printf("Select failed\n");
555       fflush(stdout);
556       continue;
557       }
558
559     accept_socket = -1;
560     for (i = 0; i < skn; i++)
561       if (listen_socket[i] > 0 && FD_ISSET(listen_socket[i], &select_listen))
562         {
563         accept_socket = accept(listen_socket[i],
564           (struct sockaddr *)&accepted, &len);
565         FD_CLR(listen_socket[i], &select_listen);
566         break;
567         }
568     }
569   alarm(0);
570
571   if (accept_socket < 0)
572     {
573     printf("accept() failed: %s\n", strerror(errno));
574     exit(1);
575     }
576
577   out = fdopen(accept_socket, "w");
578
579   dup_accept_socket = dup(accept_socket);
580
581   if (port > 0)
582     printf("\nConnection request from [%s]\n", host_ntoa(&accepted, CS buffer));
583   else
584     {
585     printf("\nConnection request\n");
586
587     /* Linux supports a feature for acquiring the peer's credentials, but it
588     appears to be Linux-specific. This code is untested and unused, just
589     saved here for reference. */
590
591     /**********--------------------
592     struct ucred cr;
593     int cl=sizeof(cr);
594
595     if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl)==0) {
596       printf("Peer's pid=%d, uid=%d, gid=%d\n",
597               cr.pid, cr.uid, cr.gid);
598     --------------*****************/
599     }
600   fflush(stdout);
601
602   if (dup_accept_socket < 0)
603     {
604     printf("Couldn't dup socket descriptor\n");
605     printf("421 Connection refused: %s\n", strerror(errno));
606     fprintf(out, "421 Connection refused: %s\r\n", strerror(errno));
607     fclose(out);
608     exit(2);
609     }
610
611   in = fdopen(dup_accept_socket, "r");
612
613   /* Loop for handling the conversation(s). For use in SMTP sessions, there are
614   default rules for determining input and output lines: the latter start with
615   digits. This means that the input looks like SMTP dialog. However, this
616   doesn't work for other tests (e.g. ident tests) so we have explicit '<' and
617   '>' flags for input and output as well as the defaults. */
618
619   for (; s; s = s->next)
620     {
621     char *ss = s->line;
622
623     /* Output lines either start with '>' or a digit. In the '>' case we can
624     fudge the sending of \r\n as required. Default is \r\n, ">>" send nothing,
625     ">CR>" sends \r only, and ">LF>" sends \n only. We can also force a
626     connection closedown by ">*eof". */
627
628     if (ss[0] == '>')
629       {
630       char *end = "\r\n";
631       unsigned len = s->len;
632       printit(ss++, len--);
633
634       if (strncmp(ss, "*eof", 4) == 0)
635         {
636         s = s->next;
637         goto END_OFF;
638         }
639
640       if (*ss == '>')
641         { end = ""; ss++; len--; }
642       else if (strncmp(ss, "CR>", 3) == 0)
643         { end = "\r"; ss += 3; len -= 3; }
644       else if (strncmp(ss, "LF>", 3) == 0)
645         { end = "\n"; ss += 3; len -= 3; }
646
647       fwrite(ss, 1, len, out);
648       if (*end) fprintf(out, end);
649       }
650
651     else if (isdigit((unsigned char)ss[0]))
652       {
653       printf("%s\n", ss);
654       fprintf(out, "%s\r\n", ss);
655       }
656
657     /* If the script line starts with "*sleep" we just sleep for a while
658     before continuing. */
659
660     else if (strncmp(ss, "*sleep ", 7) == 0)
661       {
662       int sleepfor = atoi(ss+7);
663       printf("%s\n", ss);
664       fflush(out);
665       sleep(sleepfor);
666       }
667
668     /* If the script line starts with "*data " we expect a numeric argument,
669     and we expect to read (and discard) that many data bytes from the input. */
670
671     else if (strncmp(ss, "*data ", 6) == 0)
672       {
673       int dlen = atoi(ss+6);
674       int n;
675
676       alarm(timeout);
677
678       if (!linebuf)
679         while (dlen > 0)
680           {
681           n = dlen < sizeof(buffer) ? dlen : sizeof(buffer);
682           if ((n = read(dup_accept_socket, CS buffer, n)) == 0)
683             {
684             printf("Unexpected EOF read from client\n");
685             s = s->next;
686             goto END_OFF;
687             }
688           dlen -= n;
689           }
690       else
691         while (dlen-- > 0)
692           if (fgetc(in) == EOF)
693             {
694             printf("Unexpected EOF read from client\n");
695             s = s->next;
696             goto END_OFF;
697             }
698       }
699
700     /* Otherwise the script line is the start of an input line we are expecting
701     from the client, or "*eof" indicating we expect the client to close the
702     connection. Read command line or data lines; the latter are indicated
703     by the expected line being just ".". If the line starts with '<', that
704     doesn't form part of the expected input. (This allows for incoming data
705     starting with a digit.) If the line starts with '<<' we operate in
706     unbuffered rather than line mode and assume that a single read gets the
707     entire message. */
708
709     else
710       {
711       int offset;
712       int data = strcmp(ss, ".") == 0;
713
714       if (ss[0] != '<')
715         offset = 0;
716       else
717         {
718         buffer[0] = '<';
719         if (ss[1] != '<')
720           offset = 1;
721         else
722           {
723           buffer[1] = '<';
724           offset = 2;
725           }
726         }
727
728       fflush(out);
729
730       if (!linebuf)
731         {
732         int n;
733         char c;
734
735         alarm(timeout);
736         n = read(dup_accept_socket, CS buffer+offset, s->len - offset);
737         if (content_length.in_use) content_length.left -= n;
738         if (n == 0)
739           {
740           printf("%sxpected EOF read from client\n",
741             (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
742           s = s->next;
743           goto END_OFF;
744           }
745         if (offset != 2)
746           while (read(dup_accept_socket, &c, 1) == 1 && c != '\n') ;
747         alarm(0);
748         n += offset;
749
750         printit(CS buffer, n);
751
752         if (data) do
753           {
754           n = (read(dup_accept_socket, &c, 1) == 1 && c == '.');
755           if (content_length.in_use) content_length.left--;
756           while (c != '\n' && read(dup_accept_socket, &c, 1) == 1)
757             if (content_length.in_use) content_length.left--;
758           } while (!n);
759         else if (memcmp(ss, buffer, n) != 0)
760           {
761           printf("Comparison failed - bailing out\nExpected: ");
762           printit(ss, n);
763           break;
764           }
765         }
766       else
767         {
768         for (;;)
769           {
770           int n;
771           alarm(timeout);
772           if (fgets(CS buffer+offset, sizeof(buffer)-offset, in) == NULL)
773             {
774             printf("%sxpected EOF read from client\n",
775               (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
776             s = s->next;
777             goto END_OFF;
778             }
779           alarm(0);
780           n = strlen(CS buffer);
781           if (content_length.in_use) content_length.left -= (n - offset);
782           while (n > 0 && isspace(buffer[n-1])) n--;
783           buffer[n] = 0;
784           printf("%s\n", buffer);
785           if (!data || strcmp(CS buffer, ".") == 0) break;
786           }
787
788         if (strncmp(ss, CS buffer, (int)strlen(ss)) != 0)
789           {
790           printf("Comparison failed - bailing out\n");
791           printf("Expected: %s\n", ss);
792           break;
793           }
794         }
795
796         if (sscanf(CCS buffer, "<Content-length: %d", &content_length.left))
797           content_length.in_use = TRUE;
798         if (content_length.in_use && content_length.left <= 0)
799           shutdown(dup_accept_socket, SHUT_RD);
800       }
801     }
802
803   END_OFF:
804   fclose(in);
805   fclose(out);
806   }
807
808 if (s == NULL) printf("End of script\n");
809
810 if (sockname) unlink(sockname);
811 exit(0);
812 }
813
814 /* End of server.c */