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