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