OpenSSL: full-chain OCSP stapling. Bug 1466
[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 */