exipick: add -C|--config option
[exim.git] / test / src / server.c
CommitLineData
c55a77db
PH
1/* A little hacked up program that listens on a given port and allows a script
2to play the part of a remote MTA for testing purposes. This scripted version is
3hacked from my original interactive version. A further hack allows it to listen
4on a Unix domain socket as an alternative to a TCP/IP port.
5
6In an IPv6 world, listening happens on both an IPv6 and an IPv4 socket, always
7on 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
30#ifdef HAVE_NETINET_IP_VAR_H
dd7b74e9 31# include <netinet/ip_var.h>
c55a77db
PH
32#endif
33
34#include <netdb.h>
35#include <arpa/inet.h>
36#include <sys/time.h>
37#include <sys/resource.h>
38#include <sys/socket.h>
39#include <sys/un.h>
40#include <sys/stat.h>
41#include <fcntl.h>
42#include <unistd.h>
43#include <utime.h>
44
45#ifdef AF_INET6
a719fce4 46# define HAVE_IPV6 1
c55a77db
PH
47#endif
48
49#ifndef S_ADDR_TYPE
a719fce4
JH
50# define S_ADDR_TYPE u_long
51#endif
52
53#ifndef CS
54# define CS (char *)
8cfd0f7b 55# define CCS (const char *)
c55a77db
PH
56#endif
57
58
59typedef struct line {
60 struct line *next;
7eb6c37c 61 unsigned len;
c55a77db
PH
62 char line[1];
63} line;
64
dd7b74e9
JH
65typedef unsigned BOOL;
66#define FALSE 0
67#define TRUE 1
68
c55a77db
PH
69
70/*************************************************
71* SIGALRM handler - crash out *
72*************************************************/
59eaad2b 73int tmo_noerror = 0;
c55a77db
PH
74
75static void
76sigalrm_handler(int sig)
77{
78sig = sig; /* Keep picky compilers happy */
79printf("\nServer timed out\n");
59eaad2b 80exit(tmo_noerror ? 0 : 99);
c55a77db
PH
81}
82
83
84/*************************************************
85* Get textual IP address *
86*************************************************/
87
88/* This function is copied from Exim */
89
90char *
91host_ntoa(const void *arg, char *buffer)
92{
93char *yield;
94
95/* The new world. It is annoying that we have to fish out the address from
96different places in the block, depending on what kind of address it is. It
97is also a pain that inet_ntop() returns a const char *, whereas the IPv4
98function inet_ntoa() returns just char *, and some picky compilers insist
99on warning if one assigns a const char * to a char *. Hence the casts. */
100
101#if HAVE_IPV6
102char addr_buffer[46];
103int family = ((struct sockaddr *)arg)->sa_family;
104if (family == AF_INET6)
105 {
106 struct sockaddr_in6 *sk = (struct sockaddr_in6 *)arg;
107 yield = (char *)inet_ntop(family, &(sk->sin6_addr), addr_buffer,
108 sizeof(addr_buffer));
109 }
110else
111 {
112 struct sockaddr_in *sk = (struct sockaddr_in *)arg;
113 yield = (char *)inet_ntop(family, &(sk->sin_addr), addr_buffer,
114 sizeof(addr_buffer));
115 }
116
117/* If the result is a mapped IPv4 address, show it in V4 format. */
118
119if (strncmp(yield, "::ffff:", 7) == 0) yield += 7;
120
121#else /* HAVE_IPV6 */
122
123/* The old world */
124
125yield = inet_ntoa(((struct sockaddr_in *)arg)->sin_addr);
126#endif
127
128strcpy(buffer, yield);
129return buffer;
130}
131
132
7eb6c37c
JH
133
134static void
135printit(char * s, int n)
136{
137while(n--)
138 {
139 unsigned char c = *s++;
140 if (c == '\\')
141 printf("\\\\");
142 else if (c >= ' ' && c <= '~') /* assumes ascii */
143 putchar(c);
144 else
145 printf("\\x%02x", c);
146 }
147putchar('\n');
148}
149
150
151
c55a77db
PH
152/*************************************************
153* Main Program *
154*************************************************/
155
156#define v6n 0 /* IPv6 socket number */
157#define v4n 1 /* IPv4 socket number */
158#define udn 2 /* Unix domain socket number */
159#define skn 2 /* Potential number of sockets */
160
161int main(int argc, char **argv)
162{
163int i;
164int port = 0;
165int listen_socket[3] = { -1, -1, -1 };
166int accept_socket;
167int dup_accept_socket;
168int connection_count = 1;
169int count;
170int on = 1;
171int timeout = 5;
8a512ed5 172int initial_pause = 0;
c55a77db
PH
173int use_ipv4 = 1;
174int use_ipv6 = 1;
175int debug = 0;
176int na = 1;
177line *script = NULL;
178line *last = NULL;
179line *s;
180FILE *in, *out;
7eb6c37c 181int linebuf = 1;
f41e0506 182char *pidfile = NULL;
c55a77db
PH
183
184char *sockname = NULL;
185unsigned char buffer[10240];
186
187struct sockaddr_un sockun; /* don't use "sun" */
188struct sockaddr_un sockun_accepted;
189int sockun_len = sizeof(sockun_accepted);
190
191#if HAVE_IPV6
192struct sockaddr_in6 sin6;
193struct sockaddr_in6 accepted;
194struct in6_addr anyaddr6 = IN6ADDR_ANY_INIT ;
195#else
196struct sockaddr_in accepted;
197#endif
198
199/* Always need an IPv4 structure */
200
201struct sockaddr_in sin4;
202
203int len = sizeof(accepted);
204
205
206/* Sort out the arguments */
e5c9fb3c
HSHR
207if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
208 {
214133f2 209 printf("Usage: %s [options] port|socket [connection count]\n", argv[0]);
e5c9fb3c
HSHR
210 puts("Options"
211 "\n\t-d debug"
212 "\n\t-i n n seconds initial delay"
213 "\n\t-noipv4 disable ipv4"
214 "\n\t-noipv6 disable ipv6"
215 "\n\t-oP file write PID to file"
216 "\n\t-t n n seconds timeout"
217 );
218 exit(0);
219 }
c55a77db
PH
220
221while (na < argc && argv[na][0] == '-')
222 {
223 if (strcmp(argv[na], "-d") == 0) debug = 1;
59eaad2b
JH
224 else if (strcmp(argv[na], "-t") == 0)
225 {
226 if (tmo_noerror = ((timeout = atoi(argv[++na])) < 0)) timeout = -timeout;
227 }
8a512ed5 228 else if (strcmp(argv[na], "-i") == 0) initial_pause = atoi(argv[++na]);
c55a77db
PH
229 else if (strcmp(argv[na], "-noipv4") == 0) use_ipv4 = 0;
230 else if (strcmp(argv[na], "-noipv6") == 0) use_ipv6 = 0;
f41e0506 231 else if (strcmp(argv[na], "-oP") == 0) pidfile = argv[++na];
c55a77db
PH
232 else
233 {
e5c9fb3c 234 printf("server: unknown option %s, try -h or --help\n", argv[na]);
c55a77db
PH
235 exit(1);
236 }
237 na++;
238 }
239
240if (!use_ipv4 && !use_ipv6)
241 {
242 printf("server: -noipv4 and -noipv6 cannot both be given\n");
243 exit(1);
244 }
245
246if (na >= argc)
247 {
248 printf("server: no port number or socket name given\n");
249 exit(1);
250 }
251
252if (argv[na][0] == '/')
253 {
254 sockname = argv[na];
255 unlink(sockname); /* in case left lying around */
256 }
257else port = atoi(argv[na]);
258na++;
259
260if (na < argc) connection_count = atoi(argv[na]);
261
262
8a512ed5
JH
263/* Initial pause (before creating listen sockets */
264if (initial_pause > 0)
265 {
266 if (debug)
267 printf("%d: Inital pause of %d seconds\n", time(NULL), initial_pause);
268 else
269 printf("Inital pause of %d seconds\n", initial_pause);
270 while (initial_pause > 0)
271 initial_pause = sleep(initial_pause);
272 }
273
c55a77db
PH
274/* Create sockets */
275
276if (port == 0) /* Unix domain */
277 {
8a512ed5 278 if (debug) printf("%d: Creating Unix domain socket\n", time(NULL));
c55a77db
PH
279 listen_socket[udn] = socket(PF_UNIX, SOCK_STREAM, 0);
280 if (listen_socket[udn] < 0)
281 {
282 printf("Unix domain socket creation failed: %s\n", strerror(errno));
283 exit(1);
284 }
285 }
286else
287 {
288 #if HAVE_IPV6
289 if (use_ipv6)
290 {
291 if (debug) printf("Creating IPv6 socket\n");
292 listen_socket[v6n] = socket(AF_INET6, SOCK_STREAM, 0);
293 if (listen_socket[v6n] < 0)
294 {
295 printf("IPv6 socket creation failed: %s\n", strerror(errno));
296 exit(1);
297 }
298
299 /* If this is an IPv6 wildcard socket, set IPV6_V6ONLY if that option is
300 available. */
301
302 #ifdef IPV6_V6ONLY
303 if (setsockopt(listen_socket[v6n], IPPROTO_IPV6, IPV6_V6ONLY, (char *)(&on),
304 sizeof(on)) < 0)
305 printf("Setting IPV6_V6ONLY on IPv6 wildcard "
306 "socket failed (%s): carrying on without it\n", strerror(errno));
307 #endif /* IPV6_V6ONLY */
308 }
309 #endif /* HAVE_IPV6 */
310
311 /* Create an IPv4 socket if required */
312
313 if (use_ipv4)
314 {
315 if (debug) printf("Creating IPv4 socket\n");
316 listen_socket[v4n] = socket(AF_INET, SOCK_STREAM, 0);
317 if (listen_socket[v4n] < 0)
318 {
319 printf("IPv4 socket creation failed: %s\n", strerror(errno));
320 exit(1);
321 }
322 }
323 }
324
325
326/* Set SO_REUSEADDR on the IP sockets so that the program can be restarted
327while a connection is being handled - this can happen as old connections lie
328around for a bit while crashed processes are tidied away. Without this, a
329connection will prevent reuse of the smtp port for listening. */
330
331for (i = v6n; i <= v4n; i++)
332 {
333 if (listen_socket[i] >= 0 &&
334 setsockopt(listen_socket[i], SOL_SOCKET, SO_REUSEADDR, (char *)(&on),
335 sizeof(on)) < 0)
336 {
337 printf("setting SO_REUSEADDR on socket failed: %s\n", strerror(errno));
338 exit(1);
339 }
340 }
341
342
343/* Now bind the sockets to the required port or path. If a path, ensure
344anyone can write to it. */
345
346if (port == 0)
347 {
348 struct stat statbuf;
349 sockun.sun_family = AF_UNIX;
350 if (debug) printf("Binding Unix domain socket\n");
351 sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1), sockname);
352 if (bind(listen_socket[udn], (struct sockaddr *)&sockun, sizeof(sockun)) < 0)
353 {
354 printf("Unix domain socket bind() failed: %s\n", strerror(errno));
355 exit(1);
356 }
357 (void)stat(sockname, &statbuf);
358 if (debug) printf("Setting Unix domain socket mode: %0x\n",
359 statbuf.st_mode | 0777);
360 if (chmod(sockname, statbuf.st_mode | 0777) < 0)
361 {
362 printf("Unix domain socket chmod() failed: %s\n", strerror(errno));
363 exit(1);
364 }
365 }
366
367else
368 {
369 for (i = 0; i < skn; i++)
370 {
371 if (listen_socket[i] < 0) continue;
372
373 /* For an IPv6 listen, use an IPv6 socket */
374
375 #if HAVE_IPV6
376 if (i == v6n)
377 {
378 memset(&sin6, 0, sizeof(sin6));
379 sin6.sin6_family = AF_INET6;
380 sin6.sin6_port = htons(port);
381 sin6.sin6_addr = anyaddr6;
382 if (bind(listen_socket[i], (struct sockaddr *)&sin6, sizeof(sin6)) < 0)
383 {
384 printf("IPv6 socket bind() failed: %s\n", strerror(errno));
385 exit(1);
386 }
387 }
388 else
389 #endif
390
391 /* For an IPv4 bind, use an IPv4 socket, even in an IPv6 world. If an IPv4
392 bind fails EADDRINUSE after IPv6 success, carry on, because it means the
393 IPv6 socket will handle IPv4 connections. */
394
395 {
396 memset(&sin4, 0, sizeof(sin4));
397 sin4.sin_family = AF_INET;
398 sin4.sin_addr.s_addr = (S_ADDR_TYPE)INADDR_ANY;
399 sin4.sin_port = htons(port);
400 if (bind(listen_socket[i], (struct sockaddr *)&sin4, sizeof(sin4)) < 0)
c55a77db
PH
401 if (listen_socket[v6n] < 0 || errno != EADDRINUSE)
402 {
403 printf("IPv4 socket bind() failed: %s\n", strerror(errno));
404 exit(1);
405 }
406 else
407 {
408 close(listen_socket[i]);
409 listen_socket[i] = -1;
410 }
c55a77db
PH
411 }
412 }
413 }
414
415
416/* Start listening. If IPv4 fails EADDRINUSE after IPv6 succeeds, ignore the
417error because it means that the IPv6 socket will handle IPv4 connections. Don't
418output anything, because it will mess up the test output, which will be
419different for systems that do this and those that don't. */
420
421for (i = 0; i <= skn; i++)
422 {
423 if (listen_socket[i] >= 0 && listen(listen_socket[i], 5) < 0)
424 {
425 if (i != v4n || listen_socket[v6n] < 0 || errno != EADDRINUSE)
426 {
427 printf("listen() failed: %s\n", strerror(errno));
428 exit(1);
429 }
430 }
431 }
432
433
f41e0506
JH
434if (pidfile)
435 {
436 FILE * p;
437 if (!(p = fopen(pidfile, "w")))
438 {
439 fprintf(stderr, "pidfile create failed: %s\n", strerror(errno));
440 exit(1);
441 }
442 fprintf(p, "%ld\n", (long)getpid());
443 fclose(p);
444 }
445
c55a77db
PH
446/* This program handles only a fixed number of connections, in sequence. Before
447waiting for the first connection, read the standard input, which contains the
448script of things to do. A line containing "++++" is treated as end of file.
449This is so that the Perl driving script doesn't have to close the pipe -
450because that would cause it to wait for this process, which it doesn't yet want
451to do. The driving script adds the "++++" automatically - it doesn't actually
7eb6c37c 452appear in the test script. Within lines we interpret \xNN and \\ groups */
c55a77db 453
a719fce4 454while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
c55a77db
PH
455 {
456 line *next;
7eb6c37c 457 char * d;
a719fce4 458 int n = (int)strlen(CS buffer);
7eb6c37c
JH
459
460 if (n > 1 && buffer[0] == '>' && buffer[1] == '>')
461 linebuf = 0;
c55a77db
PH
462 while (n > 0 && isspace(buffer[n-1])) n--;
463 buffer[n] = 0;
a719fce4 464 if (strcmp(CS buffer, "++++") == 0) break;
c55a77db
PH
465 next = malloc(sizeof(line) + n);
466 next->next = NULL;
7eb6c37c
JH
467 d = next->line;
468 {
469 char * s = CS buffer;
470 do
471 {
472 char ch;
473 char cl = *s;
474 if (cl == '\\' && (cl = *++s) == 'x')
475 {
476 if ((ch = *++s - '0') > 9 && (ch -= 'A'-'9'-1) > 15) ch -= 'a'-'A';
477 if ((cl = *++s - '0') > 9 && (cl -= 'A'-'9'-1) > 15) cl -= 'a'-'A';
478 cl |= ch << 4;
479 }
480 *d++ = cl;
481 }
482 while (*s++);
483 }
484 next->len = d - next->line - 1;
c55a77db
PH
485 if (last == NULL) script = last = next;
486 else last->next = next;
487 last = next;
488 }
489
490fclose(stdin);
491
492/* SIGALRM handler crashes out */
493
494signal(SIGALRM, sigalrm_handler);
495
496/* s points to the current place in the script */
497
498s = script;
499
500for (count = 0; count < connection_count; count++)
501 {
f6f23946
HSHR
502 struct {
503 int left;
dd7b74e9
JH
504 BOOL in_use;
505 } content_length = { 0, FALSE };
f6f23946 506
c55a77db
PH
507 alarm(timeout);
508 if (port <= 0)
509 {
510 printf("Listening on %s ... ", sockname);
511 fflush(stdout);
512 accept_socket = accept(listen_socket[udn],
513 (struct sockaddr *)&sockun_accepted, &sockun_len);
514 }
515
516 else
517 {
518 int lcount;
519 int max_socket = 0;
520 fd_set select_listen;
521
522 printf("Listening on port %d ... ", port);
523 fflush(stdout);
524
525 FD_ZERO(&select_listen);
526 for (i = 0; i < skn; i++)
527 {
528 if (listen_socket[i] >= 0) FD_SET(listen_socket[i], &select_listen);
529 if (listen_socket[i] > max_socket) max_socket = listen_socket[i];
530 }
531
dd7b74e9 532 if ((lcount = select(max_socket + 1, &select_listen, NULL, NULL, NULL)) < 0)
c55a77db
PH
533 {
534 printf("Select failed\n");
535 fflush(stdout);
536 continue;
537 }
538
539 accept_socket = -1;
540 for (i = 0; i < skn; i++)
c55a77db
PH
541 if (listen_socket[i] > 0 && FD_ISSET(listen_socket[i], &select_listen))
542 {
543 accept_socket = accept(listen_socket[i],
544 (struct sockaddr *)&accepted, &len);
545 FD_CLR(listen_socket[i], &select_listen);
546 break;
547 }
c55a77db
PH
548 }
549 alarm(0);
550
551 if (accept_socket < 0)
552 {
553 printf("accept() failed: %s\n", strerror(errno));
554 exit(1);
555 }
556
557 out = fdopen(accept_socket, "w");
558
559 dup_accept_socket = dup(accept_socket);
560
561 if (port > 0)
a719fce4 562 printf("\nConnection request from [%s]\n", host_ntoa(&accepted, CS buffer));
c55a77db
PH
563 else
564 {
565 printf("\nConnection request\n");
566
567 /* Linux supports a feature for acquiring the peer's credentials, but it
568 appears to be Linux-specific. This code is untested and unused, just
569 saved here for reference. */
570
571 /**********--------------------
572 struct ucred cr;
573 int cl=sizeof(cr);
574
575 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl)==0) {
576 printf("Peer's pid=%d, uid=%d, gid=%d\n",
577 cr.pid, cr.uid, cr.gid);
578 --------------*****************/
579 }
b9d9c5a2 580 fflush(stdout);
c55a77db
PH
581
582 if (dup_accept_socket < 0)
583 {
584 printf("Couldn't dup socket descriptor\n");
585 printf("421 Connection refused: %s\n", strerror(errno));
586 fprintf(out, "421 Connection refused: %s\r\n", strerror(errno));
587 fclose(out);
588 exit(2);
589 }
590
591 in = fdopen(dup_accept_socket, "r");
592
593 /* Loop for handling the conversation(s). For use in SMTP sessions, there are
594 default rules for determining input and output lines: the latter start with
595 digits. This means that the input looks like SMTP dialog. However, this
596 doesn't work for other tests (e.g. ident tests) so we have explicit '<' and
597 '>' flags for input and output as well as the defaults. */
598
dd7b74e9 599 for (; s; s = s->next)
c55a77db
PH
600 {
601 char *ss = s->line;
602
603 /* Output lines either start with '>' or a digit. In the '>' case we can
604 fudge the sending of \r\n as required. Default is \r\n, ">>" send nothing,
605 ">CR>" sends \r only, and ">LF>" sends \n only. We can also force a
606 connection closedown by ">*eof". */
607
608 if (ss[0] == '>')
609 {
610 char *end = "\r\n";
7eb6c37c
JH
611 unsigned len = s->len;
612 printit(ss++, len--);
c55a77db
PH
613
614 if (strncmp(ss, "*eof", 4) == 0)
615 {
616 s = s->next;
617 goto END_OFF;
618 }
619
620 if (*ss == '>')
7eb6c37c 621 { end = ""; ss++; len--; }
c55a77db 622 else if (strncmp(ss, "CR>", 3) == 0)
7eb6c37c 623 { end = "\r"; ss += 3; len -= 3; }
c55a77db 624 else if (strncmp(ss, "LF>", 3) == 0)
7eb6c37c 625 { end = "\n"; ss += 3; len -= 3; }
c55a77db 626
7eb6c37c
JH
627 fwrite(ss, 1, len, out);
628 if (*end) fprintf(out, end);
c55a77db
PH
629 }
630
631 else if (isdigit((unsigned char)ss[0]))
632 {
633 printf("%s\n", ss);
634 fprintf(out, "%s\r\n", ss);
635 }
636
637 /* If the script line starts with "*sleep" we just sleep for a while
638 before continuing. */
639
640 else if (strncmp(ss, "*sleep ", 7) == 0)
641 {
642 int sleepfor = atoi(ss+7);
643 printf("%s\n", ss);
644 fflush(out);
645 sleep(sleepfor);
646 }
647
e027f545
JH
648 /* If the script line starts with "*data " we expect a numeric argument,
649 and we expect to read (and discard) that many data bytes from the input. */
650
651 else if (strncmp(ss, "*data ", 6) == 0)
652 {
653 int dlen = atoi(ss+6);
654 int n;
655
656 alarm(timeout);
657
658 if (!linebuf)
659 while (dlen > 0)
660 {
661 n = dlen < sizeof(buffer) ? dlen : sizeof(buffer);
662 if ((n = read(dup_accept_socket, CS buffer, n)) == 0)
663 {
4c04137d 664 printf("Unexpected EOF read from client\n");
e027f545
JH
665 s = s->next;
666 goto END_OFF;
667 }
668 dlen -= n;
669 }
670 else
671 while (dlen-- > 0)
672 if (fgetc(in) == EOF)
673 {
4c04137d 674 printf("Unexpected EOF read from client\n");
e027f545
JH
675 s = s->next;
676 goto END_OFF;
677 }
678 }
679
c55a77db
PH
680 /* Otherwise the script line is the start of an input line we are expecting
681 from the client, or "*eof" indicating we expect the client to close the
682 connection. Read command line or data lines; the latter are indicated
683 by the expected line being just ".". If the line starts with '<', that
684 doesn't form part of the expected input. (This allows for incoming data
7eb6c37c
JH
685 starting with a digit.) If the line starts with '<<' we operate in
686 unbuffered rather than line mode and assume that a single read gets the
687 entire message. */
c55a77db
PH
688
689 else
690 {
691 int offset;
692 int data = strcmp(ss, ".") == 0;
693
7eb6c37c
JH
694 if (ss[0] != '<')
695 offset = 0;
696 else
c55a77db
PH
697 {
698 buffer[0] = '<';
7eb6c37c
JH
699 if (ss[1] != '<')
700 offset = 1;
701 else
702 {
703 buffer[1] = '<';
704 offset = 2;
705 }
c55a77db 706 }
c55a77db
PH
707
708 fflush(out);
709
7eb6c37c
JH
710 if (!linebuf)
711 {
712 int n;
713 char c;
714
715 alarm(timeout);
716 n = read(dup_accept_socket, CS buffer+offset, s->len - offset);
f6f23946 717 if (content_length.in_use) content_length.left -= n;
7eb6c37c
JH
718 if (n == 0)
719 {
720 printf("%sxpected EOF read from client\n",
721 (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
722 s = s->next;
723 goto END_OFF;
724 }
725 if (offset != 2)
726 while (read(dup_accept_socket, &c, 1) == 1 && c != '\n') ;
727 alarm(0);
728 n += offset;
729
8cfd0f7b 730 printit(CS buffer, n);
7eb6c37c
JH
731
732 if (data) do
733 {
734 n = (read(dup_accept_socket, &c, 1) == 1 && c == '.');
f6f23946 735 if (content_length.in_use) content_length.left--;
7eb6c37c 736 while (c != '\n' && read(dup_accept_socket, &c, 1) == 1)
f6f23946 737 if (content_length.in_use) content_length.left--;
7eb6c37c
JH
738 } while (!n);
739 else if (memcmp(ss, buffer, n) != 0)
740 {
741 printf("Comparison failed - bailing out\nExpected: ");
742 printit(ss, n);
743 break;
744 }
745 }
746 else
747 {
748 for (;;)
749 {
750 int n;
751 alarm(timeout);
752 if (fgets(CS buffer+offset, sizeof(buffer)-offset, in) == NULL)
753 {
754 printf("%sxpected EOF read from client\n",
755 (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
756 s = s->next;
757 goto END_OFF;
758 }
759 alarm(0);
dd7b74e9 760 n = strlen(CS buffer);
f6f23946 761 if (content_length.in_use) content_length.left -= (n - offset);
7eb6c37c
JH
762 while (n > 0 && isspace(buffer[n-1])) n--;
763 buffer[n] = 0;
764 printf("%s\n", buffer);
765 if (!data || strcmp(CS buffer, ".") == 0) break;
766 }
767
768 if (strncmp(ss, CS buffer, (int)strlen(ss)) != 0)
769 {
770 printf("Comparison failed - bailing out\n");
771 printf("Expected: %s\n", ss);
772 break;
773 }
774 }
f6f23946 775
8cfd0f7b
JH
776 if (sscanf(CCS buffer, "<Content-length: %d", &content_length.left))
777 content_length.in_use = TRUE;
778 if (content_length.in_use && content_length.left <= 0)
779 shutdown(dup_accept_socket, SHUT_RD);
c55a77db
PH
780 }
781 }
782
783 END_OFF:
784 fclose(in);
785 fclose(out);
786 }
787
788if (s == NULL) printf("End of script\n");
789
dd7b74e9 790if (sockname) unlink(sockname);
c55a77db
PH
791exit(0);
792}
793
794/* End of server.c */