]>
diplodocus.org Git - nmh/blob - test/fakesmtp.c
2 * fakesmtp - A fake SMTP server used by the nmh test suite
4 * This code is Copyright (c) 2012, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
15 #include <sys/socket.h>
16 #include <netinet/in.h>
17 #include <sys/types.h>
18 #include <sys/select.h>
23 #define PIDFILE "/tmp/fakesmtp.pid"
27 static void killpidfile(void);
28 static void handleterm(int);
29 static void putsmtp(int, char *);
30 static int getsmtp(int, char *);
33 main(int argc
, char *argv
[])
35 struct addrinfo hints
, *res
;
36 int rc
, l
, conn
, on
, datamode
;
44 fprintf(stderr
, "Usage: %s output-filename port\n", argv
[0]);
48 if (!(f
= fopen(argv
[1], "w"))) {
49 fprintf(stderr
, "Unable to open output file \"%s\": %s\n",
50 argv
[1], strerror(errno
));
55 * If there is a pid file already around, kill the previously running
56 * fakesmtp process. Hopefully this will reduce the race conditions
57 * that crop up when running the test suite.
60 if (stat(PIDFILE
, &st
) == 0) {
63 if (!(pid
= fopen(PIDFILE
, "r"))) {
64 fprintf(stderr
, "Cannot open " PIDFILE
65 " (%s), continuing ...\n", strerror(errno
));
67 rc
= fscanf(pid
, "%ld", &oldpid
);
71 fprintf(stderr
, "Unable to parse pid in "
72 PIDFILE
", continuing ...\n");
74 kill((pid_t
) oldpid
, SIGTERM
);
81 memset(&hints
, 0, sizeof(hints
));
83 hints
.ai_family
= PF_INET
;
84 hints
.ai_socktype
= SOCK_STREAM
;
85 hints
.ai_protocol
= IPPROTO_TCP
;
86 hints
.ai_flags
= AI_PASSIVE
;
88 rc
= getaddrinfo("127.0.0.1", argv
[2], &hints
, &res
);
91 fprintf(stderr
, "Unable to resolve localhost/%s: %s\n",
92 argv
[2], gai_strerror(rc
));
96 l
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
99 fprintf(stderr
, "Unable to create listening socket: %s\n",
106 if (setsockopt(l
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) == -1) {
107 fprintf(stderr
, "Unable to set SO_REUSEADDR: %s\n",
112 if (bind(l
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
113 fprintf(stderr
, "Unable to bind socket: %s\n", strerror(errno
));
117 if (listen(l
, 1) == -1) {
118 fprintf(stderr
, "Unable to listen on socket: %s\n",
124 * Now we fork() and print out the process ID of our child
125 * for scripts to use. Once we do that, then exit.
132 fprintf(stderr
, "Unable to fork child: %s\n", strerror(errno
));
137 * Close stdin & stdout, otherwise people can
138 * think we're still doing stuff. For now leave stderr
145 printf("%ld\n", (long) child
);
150 * Now that our socket & files are set up, wait 30 seconds for
151 * a connection. If there isn't one, then exit.
154 if (!(pid
= fopen(PIDFILE
, "w"))) {
155 fprintf(stderr
, "Cannot open " PIDFILE
": %s\n",
160 fprintf(pid
, "%ld\n", (long) getpid());
163 signal(SIGTERM
, handleterm
);
171 rc
= select(l
+ 1, &readfd
, NULL
, NULL
, &tv
);
174 fprintf(stderr
, "select() failed: %s\n", strerror(errno
));
179 * I think if we get a timeout, we should just exit quietly.
187 * Alright, got a connection! Accept it.
190 if ((conn
= accept(l
, NULL
, NULL
)) == -1) {
191 fprintf(stderr
, "Unable to accept connection: %s\n",
199 * Pretend to be an SMTP server.
202 putsmtp(conn
, "220 Not really an ESMTP server");
208 rc
= getsmtp(conn
, line
);
213 fprintf(f
, "%s\n", line
);
216 * If we're in DATA mode, then check to see if we've got
217 * a "."; otherwise, continue
221 if (strcmp(line
, ".") == 0) {
223 putsmtp(conn
, "250 Thanks for the info!");
229 * Most commands we ignore and send the same response to.
232 if (strcmp(line
, "QUIT") == 0) {
235 putsmtp(conn
, "221 Later alligator!");
238 } else if (strcmp(line
, "DATA") == 0) {
239 putsmtp(conn
, "354 Go ahead");
242 putsmtp(conn
, "250 I'll buy that for a dollar!");
253 * Write a line to the SMTP client on the other end
257 putsmtp(int socket
, char *data
)
261 iov
[0].iov_base
= data
;
262 iov
[0].iov_len
= strlen(data
);
263 iov
[1].iov_base
= "\r\n";
266 if (writev(socket
, iov
, 2) < 0) {
272 * Read a line (up to the \r\n)
276 getsmtp(int socket
, char *data
)
279 static unsigned int bytesinbuf
= 0;
280 static char buffer
[LINESIZE
* 2], *p
;
287 if (bytesinbuf
> 0 && (p
= strchr(buffer
, '\r')) &&
290 strncpy(data
, buffer
, LINESIZE
);
291 data
[LINESIZE
- 1] = '\0';
295 * Shuffle leftover bytes back to the beginning
298 bytesinbuf
-= cc
+ 2; /* Don't forget \r\n */
299 if (bytesinbuf
> 0) {
300 memmove(buffer
, buffer
+ cc
+ 2, bytesinbuf
);
305 if (bytesinbuf
>= sizeof(buffer
)) {
306 fprintf(stderr
, "Buffer overflow in getsmtp()!\n");
310 memset(buffer
+ bytesinbuf
, 0, sizeof(buffer
) - bytesinbuf
);
311 cc
= read(socket
, buffer
+ bytesinbuf
,
312 sizeof(buffer
) - bytesinbuf
);
315 fprintf(stderr
, "Read failed: %s\n", strerror(errno
));
331 handleterm(int signal
)
341 * Get rid of our pid file