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