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.
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. */
9 /* ANSI C standard includes */
24 #include <sys/types.h>
26 #include <netinet/in_systm.h>
27 #include <netinet/in.h>
28 #include <netinet/ip.h>
30 #ifdef HAVE_NETINET_IP_VAR_H
31 #include <netinet/ip_var.h>
35 #include <arpa/inet.h>
37 #include <sys/resource.h>
38 #include <sys/socket.h>
50 # define S_ADDR_TYPE u_long
65 /*************************************************
66 * SIGALRM handler - crash out *
67 *************************************************/
70 sigalrm_handler(int sig)
72 sig = sig; /* Keep picky compilers happy */
73 printf("\nServer timed out\n");
78 /*************************************************
79 * Get textual IP address *
80 *************************************************/
82 /* This function is copied from Exim */
85 host_ntoa(const void *arg, char *buffer)
89 /* The new world. It is annoying that we have to fish out the address from
90 different places in the block, depending on what kind of address it is. It
91 is also a pain that inet_ntop() returns a const char *, whereas the IPv4
92 function inet_ntoa() returns just char *, and some picky compilers insist
93 on warning if one assigns a const char * to a char *. Hence the casts. */
97 int family = ((struct sockaddr *)arg)->sa_family;
98 if (family == AF_INET6)
100 struct sockaddr_in6 *sk = (struct sockaddr_in6 *)arg;
101 yield = (char *)inet_ntop(family, &(sk->sin6_addr), addr_buffer,
102 sizeof(addr_buffer));
106 struct sockaddr_in *sk = (struct sockaddr_in *)arg;
107 yield = (char *)inet_ntop(family, &(sk->sin_addr), addr_buffer,
108 sizeof(addr_buffer));
111 /* If the result is a mapped IPv4 address, show it in V4 format. */
113 if (strncmp(yield, "::ffff:", 7) == 0) yield += 7;
115 #else /* HAVE_IPV6 */
119 yield = inet_ntoa(((struct sockaddr_in *)arg)->sin_addr);
122 strcpy(buffer, yield);
129 printit(char * s, int n)
133 unsigned char c = *s++;
136 else if (c >= ' ' && c <= '~') /* assumes ascii */
139 printf("\\x%02x", c);
146 /*************************************************
148 *************************************************/
150 #define v6n 0 /* IPv6 socket number */
151 #define v4n 1 /* IPv4 socket number */
152 #define udn 2 /* Unix domain socket number */
153 #define skn 2 /* Potential number of sockets */
155 int main(int argc, char **argv)
159 int listen_socket[3] = { -1, -1, -1 };
161 int dup_accept_socket;
162 int connection_count = 1;
166 int initial_pause = 0;
176 char *pidfile = NULL;
178 char *sockname = NULL;
179 unsigned char buffer[10240];
181 struct sockaddr_un sockun; /* don't use "sun" */
182 struct sockaddr_un sockun_accepted;
183 int sockun_len = sizeof(sockun_accepted);
186 struct sockaddr_in6 sin6;
187 struct sockaddr_in6 accepted;
188 struct in6_addr anyaddr6 = IN6ADDR_ANY_INIT ;
190 struct sockaddr_in accepted;
193 /* Always need an IPv4 structure */
195 struct sockaddr_in sin4;
197 int len = sizeof(accepted);
200 /* Sort out the arguments */
202 while (na < argc && argv[na][0] == '-')
204 if (strcmp(argv[na], "-d") == 0) debug = 1;
205 else if (strcmp(argv[na], "-t") == 0) timeout = atoi(argv[++na]);
206 else if (strcmp(argv[na], "-i") == 0) initial_pause = atoi(argv[++na]);
207 else if (strcmp(argv[na], "-noipv4") == 0) use_ipv4 = 0;
208 else if (strcmp(argv[na], "-noipv6") == 0) use_ipv6 = 0;
209 else if (strcmp(argv[na], "-oP") == 0) pidfile = argv[++na];
212 printf("server: unknown option %s\n", argv[na]);
218 if (!use_ipv4 && !use_ipv6)
220 printf("server: -noipv4 and -noipv6 cannot both be given\n");
226 printf("server: no port number or socket name given\n");
230 if (argv[na][0] == '/')
233 unlink(sockname); /* in case left lying around */
235 else port = atoi(argv[na]);
238 if (na < argc) connection_count = atoi(argv[na]);
241 /* Initial pause (before creating listen sockets */
242 if (initial_pause > 0)
245 printf("%d: Inital pause of %d seconds\n", time(NULL), initial_pause);
247 printf("Inital pause of %d seconds\n", initial_pause);
248 while (initial_pause > 0)
249 initial_pause = sleep(initial_pause);
254 if (port == 0) /* Unix domain */
256 if (debug) printf("%d: Creating Unix domain socket\n", time(NULL));
257 listen_socket[udn] = socket(PF_UNIX, SOCK_STREAM, 0);
258 if (listen_socket[udn] < 0)
260 printf("Unix domain socket creation failed: %s\n", strerror(errno));
269 if (debug) printf("Creating IPv6 socket\n");
270 listen_socket[v6n] = socket(AF_INET6, SOCK_STREAM, 0);
271 if (listen_socket[v6n] < 0)
273 printf("IPv6 socket creation failed: %s\n", strerror(errno));
277 /* If this is an IPv6 wildcard socket, set IPV6_V6ONLY if that option is
281 if (setsockopt(listen_socket[v6n], IPPROTO_IPV6, IPV6_V6ONLY, (char *)(&on),
283 printf("Setting IPV6_V6ONLY on IPv6 wildcard "
284 "socket failed (%s): carrying on without it\n", strerror(errno));
285 #endif /* IPV6_V6ONLY */
287 #endif /* HAVE_IPV6 */
289 /* Create an IPv4 socket if required */
293 if (debug) printf("Creating IPv4 socket\n");
294 listen_socket[v4n] = socket(AF_INET, SOCK_STREAM, 0);
295 if (listen_socket[v4n] < 0)
297 printf("IPv4 socket creation failed: %s\n", strerror(errno));
304 /* Set SO_REUSEADDR on the IP sockets so that the program can be restarted
305 while a connection is being handled - this can happen as old connections lie
306 around for a bit while crashed processes are tidied away. Without this, a
307 connection will prevent reuse of the smtp port for listening. */
309 for (i = v6n; i <= v4n; i++)
311 if (listen_socket[i] >= 0 &&
312 setsockopt(listen_socket[i], SOL_SOCKET, SO_REUSEADDR, (char *)(&on),
315 printf("setting SO_REUSEADDR on socket failed: %s\n", strerror(errno));
321 /* Now bind the sockets to the required port or path. If a path, ensure
322 anyone can write to it. */
327 sockun.sun_family = AF_UNIX;
328 if (debug) printf("Binding Unix domain socket\n");
329 sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1), sockname);
330 if (bind(listen_socket[udn], (struct sockaddr *)&sockun, sizeof(sockun)) < 0)
332 printf("Unix domain socket bind() failed: %s\n", strerror(errno));
335 (void)stat(sockname, &statbuf);
336 if (debug) printf("Setting Unix domain socket mode: %0x\n",
337 statbuf.st_mode | 0777);
338 if (chmod(sockname, statbuf.st_mode | 0777) < 0)
340 printf("Unix domain socket chmod() failed: %s\n", strerror(errno));
347 for (i = 0; i < skn; i++)
349 if (listen_socket[i] < 0) continue;
351 /* For an IPv6 listen, use an IPv6 socket */
356 memset(&sin6, 0, sizeof(sin6));
357 sin6.sin6_family = AF_INET6;
358 sin6.sin6_port = htons(port);
359 sin6.sin6_addr = anyaddr6;
360 if (bind(listen_socket[i], (struct sockaddr *)&sin6, sizeof(sin6)) < 0)
362 printf("IPv6 socket bind() failed: %s\n", strerror(errno));
369 /* For an IPv4 bind, use an IPv4 socket, even in an IPv6 world. If an IPv4
370 bind fails EADDRINUSE after IPv6 success, carry on, because it means the
371 IPv6 socket will handle IPv4 connections. */
374 memset(&sin4, 0, sizeof(sin4));
375 sin4.sin_family = AF_INET;
376 sin4.sin_addr.s_addr = (S_ADDR_TYPE)INADDR_ANY;
377 sin4.sin_port = htons(port);
378 if (bind(listen_socket[i], (struct sockaddr *)&sin4, sizeof(sin4)) < 0)
380 if (listen_socket[v6n] < 0 || errno != EADDRINUSE)
382 printf("IPv4 socket bind() failed: %s\n", strerror(errno));
387 close(listen_socket[i]);
388 listen_socket[i] = -1;
396 /* Start listening. If IPv4 fails EADDRINUSE after IPv6 succeeds, ignore the
397 error because it means that the IPv6 socket will handle IPv4 connections. Don't
398 output anything, because it will mess up the test output, which will be
399 different for systems that do this and those that don't. */
401 for (i = 0; i <= skn; i++)
403 if (listen_socket[i] >= 0 && listen(listen_socket[i], 5) < 0)
405 if (i != v4n || listen_socket[v6n] < 0 || errno != EADDRINUSE)
407 printf("listen() failed: %s\n", strerror(errno));
417 if (!(p = fopen(pidfile, "w")))
419 fprintf(stderr, "pidfile create failed: %s\n", strerror(errno));
422 fprintf(p, "%ld\n", (long)getpid());
426 /* This program handles only a fixed number of connections, in sequence. Before
427 waiting for the first connection, read the standard input, which contains the
428 script of things to do. A line containing "++++" is treated as end of file.
429 This is so that the Perl driving script doesn't have to close the pipe -
430 because that would cause it to wait for this process, which it doesn't yet want
431 to do. The driving script adds the "++++" automatically - it doesn't actually
432 appear in the test script. Within lines we interpret \xNN and \\ groups */
434 while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
438 int n = (int)strlen(CS buffer);
440 if (n > 1 && buffer[0] == '>' && buffer[1] == '>')
442 while (n > 0 && isspace(buffer[n-1])) n--;
444 if (strcmp(CS buffer, "++++") == 0) break;
445 next = malloc(sizeof(line) + n);
449 char * s = CS buffer;
454 if (cl == '\\' && (cl = *++s) == 'x')
456 if ((ch = *++s - '0') > 9 && (ch -= 'A'-'9'-1) > 15) ch -= 'a'-'A';
457 if ((cl = *++s - '0') > 9 && (cl -= 'A'-'9'-1) > 15) cl -= 'a'-'A';
464 next->len = d - next->line - 1;
465 if (last == NULL) script = last = next;
466 else last->next = next;
472 /* SIGALRM handler crashes out */
474 signal(SIGALRM, sigalrm_handler);
476 /* s points to the current place in the script */
480 for (count = 0; count < connection_count; count++)
485 printf("Listening on %s ... ", sockname);
487 accept_socket = accept(listen_socket[udn],
488 (struct sockaddr *)&sockun_accepted, &sockun_len);
495 fd_set select_listen;
497 printf("Listening on port %d ... ", port);
500 FD_ZERO(&select_listen);
501 for (i = 0; i < skn; i++)
503 if (listen_socket[i] >= 0) FD_SET(listen_socket[i], &select_listen);
504 if (listen_socket[i] > max_socket) max_socket = listen_socket[i];
507 lcount = select(max_socket + 1, &select_listen, NULL, NULL, NULL);
510 printf("Select failed\n");
516 for (i = 0; i < skn; i++)
518 if (listen_socket[i] > 0 && FD_ISSET(listen_socket[i], &select_listen))
520 accept_socket = accept(listen_socket[i],
521 (struct sockaddr *)&accepted, &len);
522 FD_CLR(listen_socket[i], &select_listen);
529 if (accept_socket < 0)
531 printf("accept() failed: %s\n", strerror(errno));
535 out = fdopen(accept_socket, "w");
537 dup_accept_socket = dup(accept_socket);
540 printf("\nConnection request from [%s]\n", host_ntoa(&accepted, CS buffer));
543 printf("\nConnection request\n");
545 /* Linux supports a feature for acquiring the peer's credentials, but it
546 appears to be Linux-specific. This code is untested and unused, just
547 saved here for reference. */
549 /**********--------------------
553 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl)==0) {
554 printf("Peer's pid=%d, uid=%d, gid=%d\n",
555 cr.pid, cr.uid, cr.gid);
556 --------------*****************/
559 if (dup_accept_socket < 0)
561 printf("Couldn't dup socket descriptor\n");
562 printf("421 Connection refused: %s\n", strerror(errno));
563 fprintf(out, "421 Connection refused: %s\r\n", strerror(errno));
568 in = fdopen(dup_accept_socket, "r");
570 /* Loop for handling the conversation(s). For use in SMTP sessions, there are
571 default rules for determining input and output lines: the latter start with
572 digits. This means that the input looks like SMTP dialog. However, this
573 doesn't work for other tests (e.g. ident tests) so we have explicit '<' and
574 '>' flags for input and output as well as the defaults. */
576 for (; s != NULL; s = s->next)
580 /* Output lines either start with '>' or a digit. In the '>' case we can
581 fudge the sending of \r\n as required. Default is \r\n, ">>" send nothing,
582 ">CR>" sends \r only, and ">LF>" sends \n only. We can also force a
583 connection closedown by ">*eof". */
588 unsigned len = s->len;
589 printit(ss++, len--);
591 if (strncmp(ss, "*eof", 4) == 0)
598 { end = ""; ss++; len--; }
599 else if (strncmp(ss, "CR>", 3) == 0)
600 { end = "\r"; ss += 3; len -= 3; }
601 else if (strncmp(ss, "LF>", 3) == 0)
602 { end = "\n"; ss += 3; len -= 3; }
604 fwrite(ss, 1, len, out);
605 if (*end) fprintf(out, end);
608 else if (isdigit((unsigned char)ss[0]))
611 fprintf(out, "%s\r\n", ss);
614 /* If the script line starts with "*sleep" we just sleep for a while
615 before continuing. */
617 else if (strncmp(ss, "*sleep ", 7) == 0)
619 int sleepfor = atoi(ss+7);
625 /* Otherwise the script line is the start of an input line we are expecting
626 from the client, or "*eof" indicating we expect the client to close the
627 connection. Read command line or data lines; the latter are indicated
628 by the expected line being just ".". If the line starts with '<', that
629 doesn't form part of the expected input. (This allows for incoming data
630 starting with a digit.) If the line starts with '<<' we operate in
631 unbuffered rather than line mode and assume that a single read gets the
637 int data = strcmp(ss, ".") == 0;
661 n = read(dup_accept_socket, CS buffer+offset, s->len - offset);
664 printf("%sxpected EOF read from client\n",
665 (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
670 while (read(dup_accept_socket, &c, 1) == 1 && c != '\n') ;
678 n = (read(dup_accept_socket, &c, 1) == 1 && c == '.');
679 while (c != '\n' && read(dup_accept_socket, &c, 1) == 1)
682 else if (memcmp(ss, buffer, n) != 0)
684 printf("Comparison failed - bailing out\nExpected: ");
695 if (fgets(CS buffer+offset, sizeof(buffer)-offset, in) == NULL)
697 printf("%sxpected EOF read from client\n",
698 (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
703 n = (int)strlen(CS buffer);
704 while (n > 0 && isspace(buffer[n-1])) n--;
706 printf("%s\n", buffer);
707 if (!data || strcmp(CS buffer, ".") == 0) break;
710 if (strncmp(ss, CS buffer, (int)strlen(ss)) != 0)
712 printf("Comparison failed - bailing out\n");
713 printf("Expected: %s\n", ss);
725 if (s == NULL) printf("End of script\n");
727 if (sockname != NULL) unlink(sockname);
731 /* End of server.c */