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