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;
177 char *sockname = NULL;
178 unsigned char buffer[10240];
180 struct sockaddr_un sockun; /* don't use "sun" */
181 struct sockaddr_un sockun_accepted;
182 int sockun_len = sizeof(sockun_accepted);
185 struct sockaddr_in6 sin6;
186 struct sockaddr_in6 accepted;
187 struct in6_addr anyaddr6 = IN6ADDR_ANY_INIT ;
189 struct sockaddr_in accepted;
192 /* Always need an IPv4 structure */
194 struct sockaddr_in sin4;
196 int len = sizeof(accepted);
199 /* Sort out the arguments */
201 while (na < argc && argv[na][0] == '-')
203 if (strcmp(argv[na], "-d") == 0) debug = 1;
204 else if (strcmp(argv[na], "-t") == 0) timeout = atoi(argv[++na]);
205 else if (strcmp(argv[na], "-i") == 0) initial_pause = atoi(argv[++na]);
206 else if (strcmp(argv[na], "-noipv4") == 0) use_ipv4 = 0;
207 else if (strcmp(argv[na], "-noipv6") == 0) use_ipv6 = 0;
210 printf("server: unknown option %s\n", argv[na]);
216 if (!use_ipv4 && !use_ipv6)
218 printf("server: -noipv4 and -noipv6 cannot both be given\n");
224 printf("server: no port number or socket name given\n");
228 if (argv[na][0] == '/')
231 unlink(sockname); /* in case left lying around */
233 else port = atoi(argv[na]);
236 if (na < argc) connection_count = atoi(argv[na]);
239 /* Initial pause (before creating listen sockets */
240 if (initial_pause > 0)
243 printf("%d: Inital pause of %d seconds\n", time(NULL), initial_pause);
245 printf("Inital pause of %d seconds\n", initial_pause);
246 while (initial_pause > 0)
247 initial_pause = sleep(initial_pause);
252 if (port == 0) /* Unix domain */
254 if (debug) printf("%d: Creating Unix domain socket\n", time(NULL));
255 listen_socket[udn] = socket(PF_UNIX, SOCK_STREAM, 0);
256 if (listen_socket[udn] < 0)
258 printf("Unix domain socket creation failed: %s\n", strerror(errno));
267 if (debug) printf("Creating IPv6 socket\n");
268 listen_socket[v6n] = socket(AF_INET6, SOCK_STREAM, 0);
269 if (listen_socket[v6n] < 0)
271 printf("IPv6 socket creation failed: %s\n", strerror(errno));
275 /* If this is an IPv6 wildcard socket, set IPV6_V6ONLY if that option is
279 if (setsockopt(listen_socket[v6n], IPPROTO_IPV6, IPV6_V6ONLY, (char *)(&on),
281 printf("Setting IPV6_V6ONLY on IPv6 wildcard "
282 "socket failed (%s): carrying on without it\n", strerror(errno));
283 #endif /* IPV6_V6ONLY */
285 #endif /* HAVE_IPV6 */
287 /* Create an IPv4 socket if required */
291 if (debug) printf("Creating IPv4 socket\n");
292 listen_socket[v4n] = socket(AF_INET, SOCK_STREAM, 0);
293 if (listen_socket[v4n] < 0)
295 printf("IPv4 socket creation failed: %s\n", strerror(errno));
302 /* Set SO_REUSEADDR on the IP sockets so that the program can be restarted
303 while a connection is being handled - this can happen as old connections lie
304 around for a bit while crashed processes are tidied away. Without this, a
305 connection will prevent reuse of the smtp port for listening. */
307 for (i = v6n; i <= v4n; i++)
309 if (listen_socket[i] >= 0 &&
310 setsockopt(listen_socket[i], SOL_SOCKET, SO_REUSEADDR, (char *)(&on),
313 printf("setting SO_REUSEADDR on socket failed: %s\n", strerror(errno));
319 /* Now bind the sockets to the required port or path. If a path, ensure
320 anyone can write to it. */
325 sockun.sun_family = AF_UNIX;
326 if (debug) printf("Binding Unix domain socket\n");
327 sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1), sockname);
328 if (bind(listen_socket[udn], (struct sockaddr *)&sockun, sizeof(sockun)) < 0)
330 printf("Unix domain socket bind() failed: %s\n", strerror(errno));
333 (void)stat(sockname, &statbuf);
334 if (debug) printf("Setting Unix domain socket mode: %0x\n",
335 statbuf.st_mode | 0777);
336 if (chmod(sockname, statbuf.st_mode | 0777) < 0)
338 printf("Unix domain socket chmod() failed: %s\n", strerror(errno));
345 for (i = 0; i < skn; i++)
347 if (listen_socket[i] < 0) continue;
349 /* For an IPv6 listen, use an IPv6 socket */
354 memset(&sin6, 0, sizeof(sin6));
355 sin6.sin6_family = AF_INET6;
356 sin6.sin6_port = htons(port);
357 sin6.sin6_addr = anyaddr6;
358 if (bind(listen_socket[i], (struct sockaddr *)&sin6, sizeof(sin6)) < 0)
360 printf("IPv6 socket bind() failed: %s\n", strerror(errno));
367 /* For an IPv4 bind, use an IPv4 socket, even in an IPv6 world. If an IPv4
368 bind fails EADDRINUSE after IPv6 success, carry on, because it means the
369 IPv6 socket will handle IPv4 connections. */
372 memset(&sin4, 0, sizeof(sin4));
373 sin4.sin_family = AF_INET;
374 sin4.sin_addr.s_addr = (S_ADDR_TYPE)INADDR_ANY;
375 sin4.sin_port = htons(port);
376 if (bind(listen_socket[i], (struct sockaddr *)&sin4, sizeof(sin4)) < 0)
378 if (listen_socket[v6n] < 0 || errno != EADDRINUSE)
380 printf("IPv4 socket bind() failed: %s\n", strerror(errno));
385 close(listen_socket[i]);
386 listen_socket[i] = -1;
394 /* Start listening. If IPv4 fails EADDRINUSE after IPv6 succeeds, ignore the
395 error because it means that the IPv6 socket will handle IPv4 connections. Don't
396 output anything, because it will mess up the test output, which will be
397 different for systems that do this and those that don't. */
399 for (i = 0; i <= skn; i++)
401 if (listen_socket[i] >= 0 && listen(listen_socket[i], 5) < 0)
403 if (i != v4n || listen_socket[v6n] < 0 || errno != EADDRINUSE)
405 printf("listen() failed: %s\n", strerror(errno));
412 /* This program handles only a fixed number of connections, in sequence. Before
413 waiting for the first connection, read the standard input, which contains the
414 script of things to do. A line containing "++++" is treated as end of file.
415 This is so that the Perl driving script doesn't have to close the pipe -
416 because that would cause it to wait for this process, which it doesn't yet want
417 to do. The driving script adds the "++++" automatically - it doesn't actually
418 appear in the test script. Within lines we interpret \xNN and \\ groups */
420 while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
424 int n = (int)strlen(CS buffer);
426 if (n > 1 && buffer[0] == '>' && buffer[1] == '>')
428 while (n > 0 && isspace(buffer[n-1])) n--;
430 if (strcmp(CS buffer, "++++") == 0) break;
431 next = malloc(sizeof(line) + n);
435 char * s = CS buffer;
440 if (cl == '\\' && (cl = *++s) == 'x')
442 if ((ch = *++s - '0') > 9 && (ch -= 'A'-'9'-1) > 15) ch -= 'a'-'A';
443 if ((cl = *++s - '0') > 9 && (cl -= 'A'-'9'-1) > 15) cl -= 'a'-'A';
450 next->len = d - next->line - 1;
451 if (last == NULL) script = last = next;
452 else last->next = next;
458 /* SIGALRM handler crashes out */
460 signal(SIGALRM, sigalrm_handler);
462 /* s points to the current place in the script */
466 for (count = 0; count < connection_count; count++)
471 printf("Listening on %s ... ", sockname);
473 accept_socket = accept(listen_socket[udn],
474 (struct sockaddr *)&sockun_accepted, &sockun_len);
481 fd_set select_listen;
483 printf("Listening on port %d ... ", port);
486 FD_ZERO(&select_listen);
487 for (i = 0; i < skn; i++)
489 if (listen_socket[i] >= 0) FD_SET(listen_socket[i], &select_listen);
490 if (listen_socket[i] > max_socket) max_socket = listen_socket[i];
493 lcount = select(max_socket + 1, &select_listen, NULL, NULL, NULL);
496 printf("Select failed\n");
502 for (i = 0; i < skn; i++)
504 if (listen_socket[i] > 0 && FD_ISSET(listen_socket[i], &select_listen))
506 accept_socket = accept(listen_socket[i],
507 (struct sockaddr *)&accepted, &len);
508 FD_CLR(listen_socket[i], &select_listen);
515 if (accept_socket < 0)
517 printf("accept() failed: %s\n", strerror(errno));
521 out = fdopen(accept_socket, "w");
523 dup_accept_socket = dup(accept_socket);
526 printf("\nConnection request from [%s]\n", host_ntoa(&accepted, CS buffer));
529 printf("\nConnection request\n");
531 /* Linux supports a feature for acquiring the peer's credentials, but it
532 appears to be Linux-specific. This code is untested and unused, just
533 saved here for reference. */
535 /**********--------------------
539 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl)==0) {
540 printf("Peer's pid=%d, uid=%d, gid=%d\n",
541 cr.pid, cr.uid, cr.gid);
542 --------------*****************/
545 if (dup_accept_socket < 0)
547 printf("Couldn't dup socket descriptor\n");
548 printf("421 Connection refused: %s\n", strerror(errno));
549 fprintf(out, "421 Connection refused: %s\r\n", strerror(errno));
554 in = fdopen(dup_accept_socket, "r");
556 /* Loop for handling the conversation(s). For use in SMTP sessions, there are
557 default rules for determining input and output lines: the latter start with
558 digits. This means that the input looks like SMTP dialog. However, this
559 doesn't work for other tests (e.g. ident tests) so we have explicit '<' and
560 '>' flags for input and output as well as the defaults. */
562 for (; s != NULL; s = s->next)
566 /* Output lines either start with '>' or a digit. In the '>' case we can
567 fudge the sending of \r\n as required. Default is \r\n, ">>" send nothing,
568 ">CR>" sends \r only, and ">LF>" sends \n only. We can also force a
569 connection closedown by ">*eof". */
574 unsigned len = s->len;
575 printit(ss++, len--);
577 if (strncmp(ss, "*eof", 4) == 0)
584 { end = ""; ss++; len--; }
585 else if (strncmp(ss, "CR>", 3) == 0)
586 { end = "\r"; ss += 3; len -= 3; }
587 else if (strncmp(ss, "LF>", 3) == 0)
588 { end = "\n"; ss += 3; len -= 3; }
590 fwrite(ss, 1, len, out);
591 if (*end) fprintf(out, end);
594 else if (isdigit((unsigned char)ss[0]))
597 fprintf(out, "%s\r\n", ss);
600 /* If the script line starts with "*sleep" we just sleep for a while
601 before continuing. */
603 else if (strncmp(ss, "*sleep ", 7) == 0)
605 int sleepfor = atoi(ss+7);
611 /* Otherwise the script line is the start of an input line we are expecting
612 from the client, or "*eof" indicating we expect the client to close the
613 connection. Read command line or data lines; the latter are indicated
614 by the expected line being just ".". If the line starts with '<', that
615 doesn't form part of the expected input. (This allows for incoming data
616 starting with a digit.) If the line starts with '<<' we operate in
617 unbuffered rather than line mode and assume that a single read gets the
623 int data = strcmp(ss, ".") == 0;
647 n = read(dup_accept_socket, CS buffer+offset, s->len - offset);
650 printf("%sxpected EOF read from client\n",
651 (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
656 while (read(dup_accept_socket, &c, 1) == 1 && c != '\n') ;
664 n = (read(dup_accept_socket, &c, 1) == 1 && c == '.');
665 while (c != '\n' && read(dup_accept_socket, &c, 1) == 1)
668 else if (memcmp(ss, buffer, n) != 0)
670 printf("Comparison failed - bailing out\nExpected: ");
681 if (fgets(CS buffer+offset, sizeof(buffer)-offset, in) == NULL)
683 printf("%sxpected EOF read from client\n",
684 (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
689 n = (int)strlen(CS buffer);
690 while (n > 0 && isspace(buffer[n-1])) n--;
692 printf("%s\n", buffer);
693 if (!data || strcmp(CS buffer, ".") == 0) break;
696 if (strncmp(ss, CS buffer, (int)strlen(ss)) != 0)
698 printf("Comparison failed - bailing out\n");
699 printf("Expected: %s\n", ss);
711 if (s == NULL) printf("End of script\n");
713 if (sockname != NULL) unlink(sockname);
717 /* End of server.c */