4656b02af1b8c773674d9c926f17612099c1e69a
[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
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
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 (tfo)
334       {
335       int backlog = 5;
336       if (setsockopt(listen_socket[v4n], IPPROTO_TCP, TCP_FASTOPEN,
337                     &backlog, sizeof(backlog)))
338         if (debug) printf("setsockopt TCP_FASTOPEN: %s\n", strerror(errno));
339       }
340
341     }
342   }
343
344
345 /* Set SO_REUSEADDR on the IP sockets so that the program can be restarted
346 while a connection is being handled - this can happen as old connections lie
347 around for a bit while crashed processes are tidied away.  Without this, a
348 connection will prevent reuse of the smtp port for listening. */
349
350 for (i = v6n; i <= v4n; i++)
351   {
352   if (listen_socket[i] >= 0 &&
353       setsockopt(listen_socket[i], SOL_SOCKET, SO_REUSEADDR, (char *)(&on),
354         sizeof(on)) < 0)
355     {
356     printf("setting SO_REUSEADDR on socket failed: %s\n", strerror(errno));
357     exit(1);
358     }
359   }
360
361
362 /* Now bind the sockets to the required port or path. If a path, ensure
363 anyone can write to it. */
364
365 if (port == 0)
366   {
367   struct stat statbuf;
368   sockun.sun_family = AF_UNIX;
369   if (debug) printf("Binding Unix domain socket\n");
370   sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1), sockname);
371   if (bind(listen_socket[udn], (struct sockaddr *)&sockun, sizeof(sockun)) < 0)
372     {
373     printf("Unix domain socket bind() failed: %s\n", strerror(errno));
374     exit(1);
375     }
376   (void)stat(sockname, &statbuf);
377   if (debug) printf("Setting Unix domain socket mode: %0x\n",
378     statbuf.st_mode | 0777);
379   if (chmod(sockname, statbuf.st_mode | 0777) < 0)
380     {
381     printf("Unix domain socket chmod() failed: %s\n", strerror(errno));
382     exit(1);
383     }
384   }
385
386 else
387   {
388   for (i = 0; i < skn; i++)
389     {
390     if (listen_socket[i] < 0) continue;
391
392     /* For an IPv6 listen, use an IPv6 socket */
393
394     #if HAVE_IPV6
395     if (i == v6n)
396       {
397       memset(&sin6, 0, sizeof(sin6));
398       sin6.sin6_family = AF_INET6;
399       sin6.sin6_port = htons(port);
400       sin6.sin6_addr = anyaddr6;
401       if (bind(listen_socket[i], (struct sockaddr *)&sin6, sizeof(sin6)) < 0)
402         {
403         printf("IPv6 socket bind() failed: %s\n", strerror(errno));
404         exit(1);
405         }
406       }
407     else
408     #endif
409
410     /* For an IPv4 bind, use an IPv4 socket, even in an IPv6 world. If an IPv4
411     bind fails EADDRINUSE after IPv6 success, carry on, because it means the
412     IPv6 socket will handle IPv4 connections. */
413
414       {
415       memset(&sin4, 0, sizeof(sin4));
416       sin4.sin_family = AF_INET;
417       sin4.sin_addr.s_addr = (S_ADDR_TYPE)INADDR_ANY;
418       sin4.sin_port = htons(port);
419       if (bind(listen_socket[i], (struct sockaddr *)&sin4, sizeof(sin4)) < 0)
420         if (listen_socket[v6n] < 0 || errno != EADDRINUSE)
421           {
422           printf("IPv4 socket bind() failed: %s\n", strerror(errno));
423           exit(1);
424           }
425         else
426           {
427           close(listen_socket[i]);
428           listen_socket[i] = -1;
429           }
430       }
431     }
432   }
433
434
435 /* Start listening. If IPv4 fails EADDRINUSE after IPv6 succeeds, ignore the
436 error because it means that the IPv6 socket will handle IPv4 connections. Don't
437 output anything, because it will mess up the test output, which will be
438 different for systems that do this and those that don't. */
439
440 for (i = 0; i <= skn; i++)
441   {
442   if (listen_socket[i] >= 0 && listen(listen_socket[i], 5) < 0)
443     {
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   }
451
452
453 if (pidfile)
454   {
455   FILE * p;
456   if (!(p = fopen(pidfile, "w")))
457     {
458     fprintf(stderr, "pidfile create failed: %s\n", strerror(errno));
459     exit(1);
460     }
461   fprintf(p, "%ld\n", (long)getpid());
462   fclose(p);
463   }
464
465 /* This program handles only a fixed number of connections, in sequence. Before
466 waiting for the first connection, read the standard input, which contains the
467 script of things to do. A line containing "++++" is treated as end of file.
468 This is so that the Perl driving script doesn't have to close the pipe -
469 because that would cause it to wait for this process, which it doesn't yet want
470 to do. The driving script adds the "++++" automatically - it doesn't actually
471 appear in the test script. Within lines we interpret \xNN and \\ groups */
472
473 while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
474   {
475   line *next;
476   char * d;
477   int n = (int)strlen(CS buffer);
478
479   if (n > 1 && buffer[0] == '>' && buffer[1] == '>')
480     linebuf = 0;
481   while (n > 0 && isspace(buffer[n-1])) n--;
482   buffer[n] = 0;
483   if (strcmp(CS buffer, "++++") == 0) break;
484   next = malloc(sizeof(line) + n);
485   next->next = NULL;
486   d = next->line;
487     {
488     char * s = CS buffer;
489     do
490       {
491       char ch;
492       char cl = *s;
493       if (cl == '\\' && (cl = *++s) == 'x')
494         {
495         if ((ch = *++s - '0') > 9 && (ch -= 'A'-'9'-1) > 15) ch -= 'a'-'A';
496         if ((cl = *++s - '0') > 9 && (cl -= 'A'-'9'-1) > 15) cl -= 'a'-'A';
497         cl |= ch << 4;
498         }
499       *d++ = cl;
500       }
501     while (*s++);
502     }
503   next->len = d - next->line - 1;
504   if (last == NULL) script = last = next;
505     else last->next = next;
506   last = next;
507   }
508
509 fclose(stdin);
510
511 /* SIGALRM handler crashes out */
512
513 signal(SIGALRM, sigalrm_handler);
514
515 /* s points to the current place in the script */
516
517 s = script;
518
519 for (count = 0; count < connection_count; count++)
520   {
521   struct {
522     int left;
523     BOOL in_use;
524   } content_length = { 0, FALSE };
525
526   alarm(timeout);
527   if (port <= 0)
528     {
529     printf("Listening on %s ... ", sockname);
530     fflush(stdout);
531     accept_socket = accept(listen_socket[udn],
532       (struct sockaddr *)&sockun_accepted, &sockun_len);
533     }
534
535   else
536     {
537     int lcount;
538     int max_socket = 0;
539     fd_set select_listen;
540
541     printf("Listening on port %d ... ", port);
542     fflush(stdout);
543
544     FD_ZERO(&select_listen);
545     for (i = 0; i < skn; i++)
546       {
547       if (listen_socket[i] >= 0) FD_SET(listen_socket[i], &select_listen);
548       if (listen_socket[i] > max_socket) max_socket = listen_socket[i];
549       }
550
551     if ((lcount = select(max_socket + 1, &select_listen, NULL, NULL, NULL)) < 0)
552       {
553       printf("Select failed\n");
554       fflush(stdout);
555       continue;
556       }
557
558     accept_socket = -1;
559     for (i = 0; i < skn; i++)
560       if (listen_socket[i] > 0 && FD_ISSET(listen_socket[i], &select_listen))
561         {
562         accept_socket = accept(listen_socket[i],
563           (struct sockaddr *)&accepted, &len);
564         FD_CLR(listen_socket[i], &select_listen);
565         break;
566         }
567     }
568   alarm(0);
569
570   if (accept_socket < 0)
571     {
572     printf("accept() failed: %s\n", strerror(errno));
573     exit(1);
574     }
575
576   out = fdopen(accept_socket, "w");
577
578   dup_accept_socket = dup(accept_socket);
579
580   if (port > 0)
581     printf("\nConnection request from [%s]\n", host_ntoa(&accepted, CS buffer));
582   else
583     {
584     printf("\nConnection request\n");
585
586     /* Linux supports a feature for acquiring the peer's credentials, but it
587     appears to be Linux-specific. This code is untested and unused, just
588     saved here for reference. */
589
590     /**********--------------------
591     struct ucred cr;
592     int cl=sizeof(cr);
593
594     if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl)==0) {
595       printf("Peer's pid=%d, uid=%d, gid=%d\n",
596               cr.pid, cr.uid, cr.gid);
597     --------------*****************/
598     }
599   fflush(stdout);
600
601   if (dup_accept_socket < 0)
602     {
603     printf("Couldn't dup socket descriptor\n");
604     printf("421 Connection refused: %s\n", strerror(errno));
605     fprintf(out, "421 Connection refused: %s\r\n", strerror(errno));
606     fclose(out);
607     exit(2);
608     }
609
610   in = fdopen(dup_accept_socket, "r");
611
612   /* Loop for handling the conversation(s). For use in SMTP sessions, there are
613   default rules for determining input and output lines: the latter start with
614   digits. This means that the input looks like SMTP dialog. However, this
615   doesn't work for other tests (e.g. ident tests) so we have explicit '<' and
616   '>' flags for input and output as well as the defaults. */
617
618   for (; s; s = s->next)
619     {
620     char *ss = s->line;
621
622     /* Output lines either start with '>' or a digit. In the '>' case we can
623     fudge the sending of \r\n as required. Default is \r\n, ">>" send nothing,
624     ">CR>" sends \r only, and ">LF>" sends \n only. We can also force a
625     connection closedown by ">*eof". */
626
627     if (ss[0] == '>')
628       {
629       char *end = "\r\n";
630       unsigned len = s->len;
631       printit(ss++, len--);
632
633       if (strncmp(ss, "*eof", 4) == 0)
634         {
635         s = s->next;
636         goto END_OFF;
637         }
638
639       if (*ss == '>')
640         { end = ""; ss++; len--; }
641       else if (strncmp(ss, "CR>", 3) == 0)
642         { end = "\r"; ss += 3; len -= 3; }
643       else if (strncmp(ss, "LF>", 3) == 0)
644         { end = "\n"; ss += 3; len -= 3; }
645
646       fwrite(ss, 1, len, out);
647       if (*end) fprintf(out, end);
648       }
649
650     else if (isdigit((unsigned char)ss[0]))
651       {
652       printf("%s\n", ss);
653       fprintf(out, "%s\r\n", ss);
654       }
655
656     /* If the script line starts with "*sleep" we just sleep for a while
657     before continuing. */
658
659     else if (strncmp(ss, "*sleep ", 7) == 0)
660       {
661       int sleepfor = atoi(ss+7);
662       printf("%s\n", ss);
663       fflush(out);
664       sleep(sleepfor);
665       }
666
667     /* If the script line starts with "*data " we expect a numeric argument,
668     and we expect to read (and discard) that many data bytes from the input. */
669
670     else if (strncmp(ss, "*data ", 6) == 0)
671       {
672       int dlen = atoi(ss+6);
673       int n;
674
675       alarm(timeout);
676
677       if (!linebuf)
678         while (dlen > 0)
679           {
680           n = dlen < sizeof(buffer) ? dlen : sizeof(buffer);
681           if ((n = read(dup_accept_socket, CS buffer, n)) == 0)
682             {
683             printf("Unexpected EOF read from client\n");
684             s = s->next;
685             goto END_OFF;
686             }
687           dlen -= n;
688           }
689       else
690         while (dlen-- > 0)
691           if (fgetc(in) == EOF)
692             {
693             printf("Unexpected EOF read from client\n");
694             s = s->next;
695             goto END_OFF;
696             }
697       }
698
699     /* Otherwise the script line is the start of an input line we are expecting
700     from the client, or "*eof" indicating we expect the client to close the
701     connection. Read command line or data lines; the latter are indicated
702     by the expected line being just ".". If the line starts with '<', that
703     doesn't form part of the expected input. (This allows for incoming data
704     starting with a digit.) If the line starts with '<<' we operate in
705     unbuffered rather than line mode and assume that a single read gets the
706     entire message. */
707
708     else
709       {
710       int offset;
711       int data = strcmp(ss, ".") == 0;
712
713       if (ss[0] != '<')
714         offset = 0;
715       else
716         {
717         buffer[0] = '<';
718         if (ss[1] != '<')
719           offset = 1;
720         else
721           {
722           buffer[1] = '<';
723           offset = 2;
724           }
725         }
726
727       fflush(out);
728
729       if (!linebuf)
730         {
731         int n;
732         char c;
733
734         alarm(timeout);
735         n = read(dup_accept_socket, CS buffer+offset, s->len - offset);
736         if (content_length.in_use) content_length.left -= n;
737         if (n == 0)
738           {
739           printf("%sxpected EOF read from client\n",
740             (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
741           s = s->next;
742           goto END_OFF;
743           }
744         if (offset != 2)
745           while (read(dup_accept_socket, &c, 1) == 1 && c != '\n') ;
746         alarm(0);
747         n += offset;
748
749         printit(CS buffer, n);
750
751         if (data) do
752           {
753           n = (read(dup_accept_socket, &c, 1) == 1 && c == '.');
754           if (content_length.in_use) content_length.left--;
755           while (c != '\n' && read(dup_accept_socket, &c, 1) == 1)
756             if (content_length.in_use) content_length.left--;
757           } while (!n);
758         else if (memcmp(ss, buffer, n) != 0)
759           {
760           printf("Comparison failed - bailing out\nExpected: ");
761           printit(ss, n);
762           break;
763           }
764         }
765       else
766         {
767         for (;;)
768           {
769           int n;
770           alarm(timeout);
771           if (fgets(CS buffer+offset, sizeof(buffer)-offset, in) == NULL)
772             {
773             printf("%sxpected EOF read from client\n",
774               (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
775             s = s->next;
776             goto END_OFF;
777             }
778           alarm(0);
779           n = strlen(CS buffer);
780           if (content_length.in_use) content_length.left -= (n - offset);
781           while (n > 0 && isspace(buffer[n-1])) n--;
782           buffer[n] = 0;
783           printf("%s\n", buffer);
784           if (!data || strcmp(CS buffer, ".") == 0) break;
785           }
786
787         if (strncmp(ss, CS buffer, (int)strlen(ss)) != 0)
788           {
789           printf("Comparison failed - bailing out\n");
790           printf("Expected: %s\n", ss);
791           break;
792           }
793         }
794
795         if (sscanf(CCS buffer, "<Content-length: %d", &content_length.left))
796           content_length.in_use = TRUE;
797         if (content_length.in_use && content_length.left <= 0)
798           shutdown(dup_accept_socket, SHUT_RD);
799       }
800     }
801
802   END_OFF:
803   fclose(in);
804   fclose(out);
805   }
806
807 if (s == NULL) printf("End of script\n");
808
809 if (sockname) unlink(sockname);
810 exit(0);
811 }
812
813 /* End of server.c */