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
64 typedef unsigned BOOL;
69 /*************************************************
70 * SIGALRM handler - crash out *
71 *************************************************/
75 sigalrm_handler(int sig)
77 sig = sig; /* Keep picky compilers happy */
78 printf("\nServer timed out\n");
79 exit(tmo_noerror ? 0 : 99);
83 /*************************************************
84 * Get textual IP address *
85 *************************************************/
87 /* This function is copied from Exim */
90 host_ntoa(const void *arg, char *buffer)
94 /* The new world. It is annoying that we have to fish out the address from
95 different places in the block, depending on what kind of address it is. It
96 is also a pain that inet_ntop() returns a const char *, whereas the IPv4
97 function inet_ntoa() returns just char *, and some picky compilers insist
98 on warning if one assigns a const char * to a char *. Hence the casts. */
101 char addr_buffer[46];
102 int family = ((struct sockaddr *)arg)->sa_family;
103 if (family == AF_INET6)
105 struct sockaddr_in6 *sk = (struct sockaddr_in6 *)arg;
106 yield = (char *)inet_ntop(family, &(sk->sin6_addr), addr_buffer,
107 sizeof(addr_buffer));
111 struct sockaddr_in *sk = (struct sockaddr_in *)arg;
112 yield = (char *)inet_ntop(family, &(sk->sin_addr), addr_buffer,
113 sizeof(addr_buffer));
116 /* If the result is a mapped IPv4 address, show it in V4 format. */
118 if (strncmp(yield, "::ffff:", 7) == 0) yield += 7;
120 #else /* HAVE_IPV6 */
124 yield = inet_ntoa(((struct sockaddr_in *)arg)->sin_addr);
127 strcpy(buffer, yield);
134 printit(char * s, int n)
138 unsigned char c = *s++;
141 else if (c >= ' ' && c <= '~') /* assumes ascii */
144 printf("\\x%02x", c);
151 /*************************************************
153 *************************************************/
155 #define v6n 0 /* IPv6 socket number */
156 #define v4n 1 /* IPv4 socket number */
157 #define udn 2 /* Unix domain socket number */
158 #define skn 2 /* Potential number of sockets */
160 int main(int argc, char **argv)
164 int listen_socket[3] = { -1, -1, -1 };
166 int dup_accept_socket;
167 int connection_count = 1;
171 int initial_pause = 0;
181 char *pidfile = NULL;
183 char *sockname = NULL;
184 unsigned char buffer[10240];
186 struct sockaddr_un sockun; /* don't use "sun" */
187 struct sockaddr_un sockun_accepted;
188 int sockun_len = sizeof(sockun_accepted);
191 struct sockaddr_in6 sin6;
192 struct sockaddr_in6 accepted;
193 struct in6_addr anyaddr6 = IN6ADDR_ANY_INIT ;
195 struct sockaddr_in accepted;
198 /* Always need an IPv4 structure */
200 struct sockaddr_in sin4;
202 int len = sizeof(accepted);
205 /* Sort out the arguments */
206 if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
208 printf("Usage: %s [options] port|socket [connection count]\n", argv[0]);
211 "\n\t-i n n seconds initial delay"
212 "\n\t-noipv4 disable ipv4"
213 "\n\t-noipv6 disable ipv6"
214 "\n\t-oP file write PID to file"
215 "\n\t-t n n seconds timeout"
220 while (na < argc && argv[na][0] == '-')
222 if (strcmp(argv[na], "-d") == 0) debug = 1;
223 else if (strcmp(argv[na], "-t") == 0)
225 if (tmo_noerror = ((timeout = atoi(argv[++na])) < 0)) timeout = -timeout;
227 else if (strcmp(argv[na], "-i") == 0) initial_pause = atoi(argv[++na]);
228 else if (strcmp(argv[na], "-noipv4") == 0) use_ipv4 = 0;
229 else if (strcmp(argv[na], "-noipv6") == 0) use_ipv6 = 0;
230 else if (strcmp(argv[na], "-oP") == 0) pidfile = argv[++na];
233 printf("server: unknown option %s, try -h or --help\n", argv[na]);
239 if (!use_ipv4 && !use_ipv6)
241 printf("server: -noipv4 and -noipv6 cannot both be given\n");
247 printf("server: no port number or socket name given\n");
251 if (argv[na][0] == '/')
254 unlink(sockname); /* in case left lying around */
256 else port = atoi(argv[na]);
259 if (na < argc) connection_count = atoi(argv[na]);
262 /* Initial pause (before creating listen sockets */
263 if (initial_pause > 0)
266 printf("%d: Inital pause of %d seconds\n", time(NULL), initial_pause);
268 printf("Inital pause of %d seconds\n", initial_pause);
269 while (initial_pause > 0)
270 initial_pause = sleep(initial_pause);
275 if (port == 0) /* Unix domain */
277 if (debug) printf("%d: Creating Unix domain socket\n", time(NULL));
278 listen_socket[udn] = socket(PF_UNIX, SOCK_STREAM, 0);
279 if (listen_socket[udn] < 0)
281 printf("Unix domain socket creation failed: %s\n", strerror(errno));
290 if (debug) printf("Creating IPv6 socket\n");
291 listen_socket[v6n] = socket(AF_INET6, SOCK_STREAM, 0);
292 if (listen_socket[v6n] < 0)
294 printf("IPv6 socket creation failed: %s\n", strerror(errno));
298 /* If this is an IPv6 wildcard socket, set IPV6_V6ONLY if that option is
302 if (setsockopt(listen_socket[v6n], IPPROTO_IPV6, IPV6_V6ONLY, (char *)(&on),
304 printf("Setting IPV6_V6ONLY on IPv6 wildcard "
305 "socket failed (%s): carrying on without it\n", strerror(errno));
306 #endif /* IPV6_V6ONLY */
308 #endif /* HAVE_IPV6 */
310 /* Create an IPv4 socket if required */
314 if (debug) printf("Creating IPv4 socket\n");
315 listen_socket[v4n] = socket(AF_INET, SOCK_STREAM, 0);
316 if (listen_socket[v4n] < 0)
318 printf("IPv4 socket creation failed: %s\n", strerror(errno));
325 /* Set SO_REUSEADDR on the IP sockets so that the program can be restarted
326 while a connection is being handled - this can happen as old connections lie
327 around for a bit while crashed processes are tidied away. Without this, a
328 connection will prevent reuse of the smtp port for listening. */
330 for (i = v6n; i <= v4n; i++)
332 if (listen_socket[i] >= 0 &&
333 setsockopt(listen_socket[i], SOL_SOCKET, SO_REUSEADDR, (char *)(&on),
336 printf("setting SO_REUSEADDR on socket failed: %s\n", strerror(errno));
342 /* Now bind the sockets to the required port or path. If a path, ensure
343 anyone can write to it. */
348 sockun.sun_family = AF_UNIX;
349 if (debug) printf("Binding Unix domain socket\n");
350 sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1), sockname);
351 if (bind(listen_socket[udn], (struct sockaddr *)&sockun, sizeof(sockun)) < 0)
353 printf("Unix domain socket bind() failed: %s\n", strerror(errno));
356 (void)stat(sockname, &statbuf);
357 if (debug) printf("Setting Unix domain socket mode: %0x\n",
358 statbuf.st_mode | 0777);
359 if (chmod(sockname, statbuf.st_mode | 0777) < 0)
361 printf("Unix domain socket chmod() failed: %s\n", strerror(errno));
368 for (i = 0; i < skn; i++)
370 if (listen_socket[i] < 0) continue;
372 /* For an IPv6 listen, use an IPv6 socket */
377 memset(&sin6, 0, sizeof(sin6));
378 sin6.sin6_family = AF_INET6;
379 sin6.sin6_port = htons(port);
380 sin6.sin6_addr = anyaddr6;
381 if (bind(listen_socket[i], (struct sockaddr *)&sin6, sizeof(sin6)) < 0)
383 printf("IPv6 socket bind() failed: %s\n", strerror(errno));
390 /* For an IPv4 bind, use an IPv4 socket, even in an IPv6 world. If an IPv4
391 bind fails EADDRINUSE after IPv6 success, carry on, because it means the
392 IPv6 socket will handle IPv4 connections. */
395 memset(&sin4, 0, sizeof(sin4));
396 sin4.sin_family = AF_INET;
397 sin4.sin_addr.s_addr = (S_ADDR_TYPE)INADDR_ANY;
398 sin4.sin_port = htons(port);
399 if (bind(listen_socket[i], (struct sockaddr *)&sin4, sizeof(sin4)) < 0)
400 if (listen_socket[v6n] < 0 || errno != EADDRINUSE)
402 printf("IPv4 socket bind() failed: %s\n", strerror(errno));
407 close(listen_socket[i]);
408 listen_socket[i] = -1;
415 /* Start listening. If IPv4 fails EADDRINUSE after IPv6 succeeds, ignore the
416 error because it means that the IPv6 socket will handle IPv4 connections. Don't
417 output anything, because it will mess up the test output, which will be
418 different for systems that do this and those that don't. */
420 for (i = 0; i <= skn; i++)
422 if (listen_socket[i] >= 0 && listen(listen_socket[i], 5) < 0)
424 if (i != v4n || listen_socket[v6n] < 0 || errno != EADDRINUSE)
426 printf("listen() failed: %s\n", strerror(errno));
436 if (!(p = fopen(pidfile, "w")))
438 fprintf(stderr, "pidfile create failed: %s\n", strerror(errno));
441 fprintf(p, "%ld\n", (long)getpid());
445 /* This program handles only a fixed number of connections, in sequence. Before
446 waiting for the first connection, read the standard input, which contains the
447 script of things to do. A line containing "++++" is treated as end of file.
448 This is so that the Perl driving script doesn't have to close the pipe -
449 because that would cause it to wait for this process, which it doesn't yet want
450 to do. The driving script adds the "++++" automatically - it doesn't actually
451 appear in the test script. Within lines we interpret \xNN and \\ groups */
453 while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
457 int n = (int)strlen(CS buffer);
459 if (n > 1 && buffer[0] == '>' && buffer[1] == '>')
461 while (n > 0 && isspace(buffer[n-1])) n--;
463 if (strcmp(CS buffer, "++++") == 0) break;
464 next = malloc(sizeof(line) + n);
468 char * s = CS buffer;
473 if (cl == '\\' && (cl = *++s) == 'x')
475 if ((ch = *++s - '0') > 9 && (ch -= 'A'-'9'-1) > 15) ch -= 'a'-'A';
476 if ((cl = *++s - '0') > 9 && (cl -= 'A'-'9'-1) > 15) cl -= 'a'-'A';
483 next->len = d - next->line - 1;
484 if (last == NULL) script = last = next;
485 else last->next = next;
491 /* SIGALRM handler crashes out */
493 signal(SIGALRM, sigalrm_handler);
495 /* s points to the current place in the script */
499 for (count = 0; count < connection_count; count++)
504 } content_length = { 0, FALSE };
509 printf("Listening on %s ... ", sockname);
511 accept_socket = accept(listen_socket[udn],
512 (struct sockaddr *)&sockun_accepted, &sockun_len);
519 fd_set select_listen;
521 printf("Listening on port %d ... ", port);
524 FD_ZERO(&select_listen);
525 for (i = 0; i < skn; i++)
527 if (listen_socket[i] >= 0) FD_SET(listen_socket[i], &select_listen);
528 if (listen_socket[i] > max_socket) max_socket = listen_socket[i];
531 if ((lcount = select(max_socket + 1, &select_listen, NULL, NULL, NULL)) < 0)
533 printf("Select failed\n");
539 for (i = 0; i < skn; i++)
540 if (listen_socket[i] > 0 && FD_ISSET(listen_socket[i], &select_listen))
542 accept_socket = accept(listen_socket[i],
543 (struct sockaddr *)&accepted, &len);
544 FD_CLR(listen_socket[i], &select_listen);
550 if (accept_socket < 0)
552 printf("accept() failed: %s\n", strerror(errno));
556 out = fdopen(accept_socket, "w");
558 dup_accept_socket = dup(accept_socket);
561 printf("\nConnection request from [%s]\n", host_ntoa(&accepted, CS buffer));
564 printf("\nConnection request\n");
566 /* Linux supports a feature for acquiring the peer's credentials, but it
567 appears to be Linux-specific. This code is untested and unused, just
568 saved here for reference. */
570 /**********--------------------
574 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl)==0) {
575 printf("Peer's pid=%d, uid=%d, gid=%d\n",
576 cr.pid, cr.uid, cr.gid);
577 --------------*****************/
581 if (dup_accept_socket < 0)
583 printf("Couldn't dup socket descriptor\n");
584 printf("421 Connection refused: %s\n", strerror(errno));
585 fprintf(out, "421 Connection refused: %s\r\n", strerror(errno));
590 in = fdopen(dup_accept_socket, "r");
592 /* Loop for handling the conversation(s). For use in SMTP sessions, there are
593 default rules for determining input and output lines: the latter start with
594 digits. This means that the input looks like SMTP dialog. However, this
595 doesn't work for other tests (e.g. ident tests) so we have explicit '<' and
596 '>' flags for input and output as well as the defaults. */
598 for (; s; s = s->next)
602 /* Output lines either start with '>' or a digit. In the '>' case we can
603 fudge the sending of \r\n as required. Default is \r\n, ">>" send nothing,
604 ">CR>" sends \r only, and ">LF>" sends \n only. We can also force a
605 connection closedown by ">*eof". */
610 unsigned len = s->len;
611 printit(ss++, len--);
613 if (strncmp(ss, "*eof", 4) == 0)
620 { end = ""; ss++; len--; }
621 else if (strncmp(ss, "CR>", 3) == 0)
622 { end = "\r"; ss += 3; len -= 3; }
623 else if (strncmp(ss, "LF>", 3) == 0)
624 { end = "\n"; ss += 3; len -= 3; }
626 fwrite(ss, 1, len, out);
627 if (*end) fprintf(out, end);
630 else if (isdigit((unsigned char)ss[0]))
633 fprintf(out, "%s\r\n", ss);
636 /* If the script line starts with "*sleep" we just sleep for a while
637 before continuing. */
639 else if (strncmp(ss, "*sleep ", 7) == 0)
641 int sleepfor = atoi(ss+7);
647 /* If the script line starts with "*data " we expect a numeric argument,
648 and we expect to read (and discard) that many data bytes from the input. */
650 else if (strncmp(ss, "*data ", 6) == 0)
652 int dlen = atoi(ss+6);
660 n = dlen < sizeof(buffer) ? dlen : sizeof(buffer);
661 if ((n = read(dup_accept_socket, CS buffer, n)) == 0)
663 printf("Unxpected EOF read from client\n");
671 if (fgetc(in) == EOF)
673 printf("Unxpected EOF read from client\n");
679 /* Otherwise the script line is the start of an input line we are expecting
680 from the client, or "*eof" indicating we expect the client to close the
681 connection. Read command line or data lines; the latter are indicated
682 by the expected line being just ".". If the line starts with '<', that
683 doesn't form part of the expected input. (This allows for incoming data
684 starting with a digit.) If the line starts with '<<' we operate in
685 unbuffered rather than line mode and assume that a single read gets the
691 int data = strcmp(ss, ".") == 0;
715 n = read(dup_accept_socket, CS buffer+offset, s->len - offset);
716 if (content_length.in_use) content_length.left -= n;
719 printf("%sxpected EOF read from client\n",
720 (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
725 while (read(dup_accept_socket, &c, 1) == 1 && c != '\n') ;
733 n = (read(dup_accept_socket, &c, 1) == 1 && c == '.');
734 if (content_length.in_use) content_length.left--;
735 while (c != '\n' && read(dup_accept_socket, &c, 1) == 1)
736 if (content_length.in_use) content_length.left--;
738 else if (memcmp(ss, buffer, n) != 0)
740 printf("Comparison failed - bailing out\nExpected: ");
751 if (fgets(CS buffer+offset, sizeof(buffer)-offset, in) == NULL)
753 printf("%sxpected EOF read from client\n",
754 (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
759 n = strlen(CS buffer);
760 if (content_length.in_use) content_length.left -= (n - offset);
761 while (n > 0 && isspace(buffer[n-1])) n--;
763 printf("%s\n", buffer);
764 if (!data || strcmp(CS buffer, ".") == 0) break;
767 if (strncmp(ss, CS buffer, (int)strlen(ss)) != 0)
769 printf("Comparison failed - bailing out\n");
770 printf("Expected: %s\n", ss);
775 if (sscanf(buffer, "<Content-length: %d", &content_length.left)) content_length.in_use = TRUE;
776 if (content_length.in_use && content_length.left <= 0) shutdown(dup_accept_socket, SHUT_RD);
785 if (s == NULL) printf("End of script\n");
787 if (sockname) unlink(sockname);
791 /* End of server.c */