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