Fix cert-try-verify when denied by event action
[exim.git] / test / src / server.c
1 /* A little hacked up program that listens on a given port and allows a script
2 to play the part of a remote MTA for testing purposes. This scripted version is
3 hacked from my original interactive version. A further hack allows it to listen
4 on a Unix domain socket as an alternative to a TCP/IP port.
5
6 In an IPv6 world, listening happens on both an IPv6 and an IPv4 socket, always
7 on 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
31 #include <netinet/ip_var.h>
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
46 # define HAVE_IPV6 1
47 #endif
48
49 #ifndef S_ADDR_TYPE
50 # define S_ADDR_TYPE u_long
51 #endif
52
53 #ifndef CS
54 # define CS (char *)
55 #endif
56
57
58 typedef struct line {
59 struct line *next;
60 char line[1];
61 } line;
62
63
64 /*************************************************
65 * SIGALRM handler - crash out *
66 *************************************************/
67
68 static void
69 sigalrm_handler(int sig)
70 {
71 sig = sig; /* Keep picky compilers happy */
72 printf("\nServer timed out\n");
73 exit(99);
74 }
75
76
77 /*************************************************
78 * Get textual IP address *
79 *************************************************/
80
81 /* This function is copied from Exim */
82
83 char *
84 host_ntoa(const void *arg, char *buffer)
85 {
86 char *yield;
87
88 /* The new world. It is annoying that we have to fish out the address from
89 different places in the block, depending on what kind of address it is. It
90 is also a pain that inet_ntop() returns a const char *, whereas the IPv4
91 function inet_ntoa() returns just char *, and some picky compilers insist
92 on warning if one assigns a const char * to a char *. Hence the casts. */
93
94 #if HAVE_IPV6
95 char addr_buffer[46];
96 int family = ((struct sockaddr *)arg)->sa_family;
97 if (family == AF_INET6)
98 {
99 struct sockaddr_in6 *sk = (struct sockaddr_in6 *)arg;
100 yield = (char *)inet_ntop(family, &(sk->sin6_addr), addr_buffer,
101 sizeof(addr_buffer));
102 }
103 else
104 {
105 struct sockaddr_in *sk = (struct sockaddr_in *)arg;
106 yield = (char *)inet_ntop(family, &(sk->sin_addr), addr_buffer,
107 sizeof(addr_buffer));
108 }
109
110 /* If the result is a mapped IPv4 address, show it in V4 format. */
111
112 if (strncmp(yield, "::ffff:", 7) == 0) yield += 7;
113
114 #else /* HAVE_IPV6 */
115
116 /* The old world */
117
118 yield = inet_ntoa(((struct sockaddr_in *)arg)->sin_addr);
119 #endif
120
121 strcpy(buffer, yield);
122 return buffer;
123 }
124
125
126 /*************************************************
127 * Main Program *
128 *************************************************/
129
130 #define v6n 0 /* IPv6 socket number */
131 #define v4n 1 /* IPv4 socket number */
132 #define udn 2 /* Unix domain socket number */
133 #define skn 2 /* Potential number of sockets */
134
135 int main(int argc, char **argv)
136 {
137 int i;
138 int port = 0;
139 int listen_socket[3] = { -1, -1, -1 };
140 int accept_socket;
141 int dup_accept_socket;
142 int connection_count = 1;
143 int count;
144 int on = 1;
145 int timeout = 5;
146 int use_ipv4 = 1;
147 int use_ipv6 = 1;
148 int debug = 0;
149 int na = 1;
150 line *script = NULL;
151 line *last = NULL;
152 line *s;
153 FILE *in, *out;
154
155 char *sockname = NULL;
156 unsigned char buffer[10240];
157
158 struct sockaddr_un sockun; /* don't use "sun" */
159 struct sockaddr_un sockun_accepted;
160 int sockun_len = sizeof(sockun_accepted);
161
162 #if HAVE_IPV6
163 struct sockaddr_in6 sin6;
164 struct sockaddr_in6 accepted;
165 struct in6_addr anyaddr6 = IN6ADDR_ANY_INIT ;
166 #else
167 struct sockaddr_in accepted;
168 #endif
169
170 /* Always need an IPv4 structure */
171
172 struct sockaddr_in sin4;
173
174 int len = sizeof(accepted);
175
176
177 /* Sort out the arguments */
178
179 while (na < argc && argv[na][0] == '-')
180 {
181 if (strcmp(argv[na], "-d") == 0) debug = 1;
182 else if (strcmp(argv[na], "-t") == 0) timeout = atoi(argv[++na]);
183 else if (strcmp(argv[na], "-noipv4") == 0) use_ipv4 = 0;
184 else if (strcmp(argv[na], "-noipv6") == 0) use_ipv6 = 0;
185 else
186 {
187 printf("server: unknown option %s\n", argv[na]);
188 exit(1);
189 }
190 na++;
191 }
192
193 if (!use_ipv4 && !use_ipv6)
194 {
195 printf("server: -noipv4 and -noipv6 cannot both be given\n");
196 exit(1);
197 }
198
199 if (na >= argc)
200 {
201 printf("server: no port number or socket name given\n");
202 exit(1);
203 }
204
205 if (argv[na][0] == '/')
206 {
207 sockname = argv[na];
208 unlink(sockname); /* in case left lying around */
209 }
210 else port = atoi(argv[na]);
211 na++;
212
213 if (na < argc) connection_count = atoi(argv[na]);
214
215
216 /* Create sockets */
217
218 if (port == 0) /* Unix domain */
219 {
220 if (debug) printf("Creating Unix domain socket\n");
221 listen_socket[udn] = socket(PF_UNIX, SOCK_STREAM, 0);
222 if (listen_socket[udn] < 0)
223 {
224 printf("Unix domain socket creation failed: %s\n", strerror(errno));
225 exit(1);
226 }
227 }
228 else
229 {
230 #if HAVE_IPV6
231 if (use_ipv6)
232 {
233 if (debug) printf("Creating IPv6 socket\n");
234 listen_socket[v6n] = socket(AF_INET6, SOCK_STREAM, 0);
235 if (listen_socket[v6n] < 0)
236 {
237 printf("IPv6 socket creation failed: %s\n", strerror(errno));
238 exit(1);
239 }
240
241 /* If this is an IPv6 wildcard socket, set IPV6_V6ONLY if that option is
242 available. */
243
244 #ifdef IPV6_V6ONLY
245 if (setsockopt(listen_socket[v6n], IPPROTO_IPV6, IPV6_V6ONLY, (char *)(&on),
246 sizeof(on)) < 0)
247 printf("Setting IPV6_V6ONLY on IPv6 wildcard "
248 "socket failed (%s): carrying on without it\n", strerror(errno));
249 #endif /* IPV6_V6ONLY */
250 }
251 #endif /* HAVE_IPV6 */
252
253 /* Create an IPv4 socket if required */
254
255 if (use_ipv4)
256 {
257 if (debug) printf("Creating IPv4 socket\n");
258 listen_socket[v4n] = socket(AF_INET, SOCK_STREAM, 0);
259 if (listen_socket[v4n] < 0)
260 {
261 printf("IPv4 socket creation failed: %s\n", strerror(errno));
262 exit(1);
263 }
264 }
265 }
266
267
268 /* Set SO_REUSEADDR on the IP sockets so that the program can be restarted
269 while a connection is being handled - this can happen as old connections lie
270 around for a bit while crashed processes are tidied away. Without this, a
271 connection will prevent reuse of the smtp port for listening. */
272
273 for (i = v6n; i <= v4n; i++)
274 {
275 if (listen_socket[i] >= 0 &&
276 setsockopt(listen_socket[i], SOL_SOCKET, SO_REUSEADDR, (char *)(&on),
277 sizeof(on)) < 0)
278 {
279 printf("setting SO_REUSEADDR on socket failed: %s\n", strerror(errno));
280 exit(1);
281 }
282 }
283
284
285 /* Now bind the sockets to the required port or path. If a path, ensure
286 anyone can write to it. */
287
288 if (port == 0)
289 {
290 struct stat statbuf;
291 sockun.sun_family = AF_UNIX;
292 if (debug) printf("Binding Unix domain socket\n");
293 sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1), sockname);
294 if (bind(listen_socket[udn], (struct sockaddr *)&sockun, sizeof(sockun)) < 0)
295 {
296 printf("Unix domain socket bind() failed: %s\n", strerror(errno));
297 exit(1);
298 }
299 (void)stat(sockname, &statbuf);
300 if (debug) printf("Setting Unix domain socket mode: %0x\n",
301 statbuf.st_mode | 0777);
302 if (chmod(sockname, statbuf.st_mode | 0777) < 0)
303 {
304 printf("Unix domain socket chmod() failed: %s\n", strerror(errno));
305 exit(1);
306 }
307 }
308
309 else
310 {
311 for (i = 0; i < skn; i++)
312 {
313 if (listen_socket[i] < 0) continue;
314
315 /* For an IPv6 listen, use an IPv6 socket */
316
317 #if HAVE_IPV6
318 if (i == v6n)
319 {
320 memset(&sin6, 0, sizeof(sin6));
321 sin6.sin6_family = AF_INET6;
322 sin6.sin6_port = htons(port);
323 sin6.sin6_addr = anyaddr6;
324 if (bind(listen_socket[i], (struct sockaddr *)&sin6, sizeof(sin6)) < 0)
325 {
326 printf("IPv6 socket bind() failed: %s\n", strerror(errno));
327 exit(1);
328 }
329 }
330 else
331 #endif
332
333 /* For an IPv4 bind, use an IPv4 socket, even in an IPv6 world. If an IPv4
334 bind fails EADDRINUSE after IPv6 success, carry on, because it means the
335 IPv6 socket will handle IPv4 connections. */
336
337 {
338 memset(&sin4, 0, sizeof(sin4));
339 sin4.sin_family = AF_INET;
340 sin4.sin_addr.s_addr = (S_ADDR_TYPE)INADDR_ANY;
341 sin4.sin_port = htons(port);
342 if (bind(listen_socket[i], (struct sockaddr *)&sin4, sizeof(sin4)) < 0)
343 {
344 if (listen_socket[v6n] < 0 || errno != EADDRINUSE)
345 {
346 printf("IPv4 socket bind() failed: %s\n", strerror(errno));
347 exit(1);
348 }
349 else
350 {
351 close(listen_socket[i]);
352 listen_socket[i] = -1;
353 }
354 }
355 }
356 }
357 }
358
359
360 /* Start listening. If IPv4 fails EADDRINUSE after IPv6 succeeds, ignore the
361 error because it means that the IPv6 socket will handle IPv4 connections. Don't
362 output anything, because it will mess up the test output, which will be
363 different for systems that do this and those that don't. */
364
365 for (i = 0; i <= skn; i++)
366 {
367 if (listen_socket[i] >= 0 && listen(listen_socket[i], 5) < 0)
368 {
369 if (i != v4n || listen_socket[v6n] < 0 || errno != EADDRINUSE)
370 {
371 printf("listen() failed: %s\n", strerror(errno));
372 exit(1);
373 }
374 }
375 }
376
377
378 /* This program handles only a fixed number of connections, in sequence. Before
379 waiting for the first connection, read the standard input, which contains the
380 script of things to do. A line containing "++++" is treated as end of file.
381 This is so that the Perl driving script doesn't have to close the pipe -
382 because that would cause it to wait for this process, which it doesn't yet want
383 to do. The driving script adds the "++++" automatically - it doesn't actually
384 appear in the test script. */
385
386 while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
387 {
388 line *next;
389 int n = (int)strlen(CS buffer);
390 while (n > 0 && isspace(buffer[n-1])) n--;
391 buffer[n] = 0;
392 if (strcmp(CS buffer, "++++") == 0) break;
393 next = malloc(sizeof(line) + n);
394 next->next = NULL;
395 strcpy(next->line, CS buffer);
396 if (last == NULL) script = last = next;
397 else last->next = next;
398 last = next;
399 }
400
401 fclose(stdin);
402
403 /* SIGALRM handler crashes out */
404
405 signal(SIGALRM, sigalrm_handler);
406
407 /* s points to the current place in the script */
408
409 s = script;
410
411 for (count = 0; count < connection_count; count++)
412 {
413 alarm(timeout);
414 if (port <= 0)
415 {
416 printf("Listening on %s ... ", sockname);
417 fflush(stdout);
418 accept_socket = accept(listen_socket[udn],
419 (struct sockaddr *)&sockun_accepted, &sockun_len);
420 }
421
422 else
423 {
424 int lcount;
425 int max_socket = 0;
426 fd_set select_listen;
427
428 printf("Listening on port %d ... ", port);
429 fflush(stdout);
430
431 FD_ZERO(&select_listen);
432 for (i = 0; i < skn; i++)
433 {
434 if (listen_socket[i] >= 0) FD_SET(listen_socket[i], &select_listen);
435 if (listen_socket[i] > max_socket) max_socket = listen_socket[i];
436 }
437
438 lcount = select(max_socket + 1, &select_listen, NULL, NULL, NULL);
439 if (lcount < 0)
440 {
441 printf("Select failed\n");
442 fflush(stdout);
443 continue;
444 }
445
446 accept_socket = -1;
447 for (i = 0; i < skn; i++)
448 {
449 if (listen_socket[i] > 0 && FD_ISSET(listen_socket[i], &select_listen))
450 {
451 accept_socket = accept(listen_socket[i],
452 (struct sockaddr *)&accepted, &len);
453 FD_CLR(listen_socket[i], &select_listen);
454 break;
455 }
456 }
457 }
458 alarm(0);
459
460 if (accept_socket < 0)
461 {
462 printf("accept() failed: %s\n", strerror(errno));
463 exit(1);
464 }
465
466 out = fdopen(accept_socket, "w");
467
468 dup_accept_socket = dup(accept_socket);
469
470 if (port > 0)
471 printf("\nConnection request from [%s]\n", host_ntoa(&accepted, CS buffer));
472 else
473 {
474 printf("\nConnection request\n");
475
476 /* Linux supports a feature for acquiring the peer's credentials, but it
477 appears to be Linux-specific. This code is untested and unused, just
478 saved here for reference. */
479
480 /**********--------------------
481 struct ucred cr;
482 int cl=sizeof(cr);
483
484 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl)==0) {
485 printf("Peer's pid=%d, uid=%d, gid=%d\n",
486 cr.pid, cr.uid, cr.gid);
487 --------------*****************/
488 }
489
490 if (dup_accept_socket < 0)
491 {
492 printf("Couldn't dup socket descriptor\n");
493 printf("421 Connection refused: %s\n", strerror(errno));
494 fprintf(out, "421 Connection refused: %s\r\n", strerror(errno));
495 fclose(out);
496 exit(2);
497 }
498
499 in = fdopen(dup_accept_socket, "r");
500
501 /* Loop for handling the conversation(s). For use in SMTP sessions, there are
502 default rules for determining input and output lines: the latter start with
503 digits. This means that the input looks like SMTP dialog. However, this
504 doesn't work for other tests (e.g. ident tests) so we have explicit '<' and
505 '>' flags for input and output as well as the defaults. */
506
507 for (; s != NULL; s = s->next)
508 {
509 char *ss = s->line;
510
511 /* Output lines either start with '>' or a digit. In the '>' case we can
512 fudge the sending of \r\n as required. Default is \r\n, ">>" send nothing,
513 ">CR>" sends \r only, and ">LF>" sends \n only. We can also force a
514 connection closedown by ">*eof". */
515
516 if (ss[0] == '>')
517 {
518 char *end = "\r\n";
519 printf("%s\n", ss++);
520
521 if (strncmp(ss, "*eof", 4) == 0)
522 {
523 s = s->next;
524 goto END_OFF;
525 }
526
527 if (*ss == '>')
528 { end = ""; ss++; }
529 else if (strncmp(ss, "CR>", 3) == 0)
530 { end = "\r"; ss += 3; }
531 else if (strncmp(ss, "LF>", 3) == 0)
532 { end = "\n"; ss += 3; }
533
534 fprintf(out, "%s%s", ss, end);
535 }
536
537 else if (isdigit((unsigned char)ss[0]))
538 {
539 printf("%s\n", ss);
540 fprintf(out, "%s\r\n", ss);
541 }
542
543 /* If the script line starts with "*sleep" we just sleep for a while
544 before continuing. */
545
546 else if (strncmp(ss, "*sleep ", 7) == 0)
547 {
548 int sleepfor = atoi(ss+7);
549 printf("%s\n", ss);
550 fflush(out);
551 sleep(sleepfor);
552 }
553
554 /* Otherwise the script line is the start of an input line we are expecting
555 from the client, or "*eof" indicating we expect the client to close the
556 connection. Read command line or data lines; the latter are indicated
557 by the expected line being just ".". If the line starts with '<', that
558 doesn't form part of the expected input. (This allows for incoming data
559 starting with a digit.) */
560
561 else
562 {
563 int offset;
564 int data = strcmp(ss, ".") == 0;
565
566 if (ss[0] == '<')
567 {
568 buffer[0] = '<';
569 offset = 1;
570 }
571 else offset = 0;
572
573 fflush(out);
574
575 for (;;)
576 {
577 int n;
578 alarm(timeout);
579 if (fgets(CS buffer+offset, sizeof(buffer)-offset, in) == NULL)
580 {
581 printf("%sxpected EOF read from client\n",
582 (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
583 s = s->next;
584 goto END_OFF;
585 }
586 alarm(0);
587 n = (int)strlen(CS buffer);
588 while (n > 0 && isspace(buffer[n-1])) n--;
589 buffer[n] = 0;
590 printf("%s\n", buffer);
591 if (!data || strcmp(CS buffer, ".") == 0) break;
592 }
593
594 if (strncmp(ss, CS buffer, (int)strlen(ss)) != 0)
595 {
596 printf("Comparison failed - bailing out\n");
597 printf("Expected: %s\n", ss);
598 break;
599 }
600 }
601 }
602
603 END_OFF:
604 fclose(in);
605 fclose(out);
606 }
607
608 if (s == NULL) printf("End of script\n");
609
610 if (sockname != NULL) unlink(sockname);
611 exit(0);
612 }
613
614 /* End of server.c */