MacOS: TCP Fast Open
[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 */