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