]>
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 <sys/types.h>
17 #include <sys/select.h>
22 #define PIDFILE "/tmp/fakesmtp.pid"
26 static void killpidfile(void);
27 static void handleterm(int);
28 static void putsmtp(int, char *);
29 static int getsmtp(int, char *);
32 main(int argc
, char *argv
[])
34 struct addrinfo hints
, *res
;
35 int rc
, l
, conn
, on
, datamode
;
43 fprintf(stderr
, "Usage: %s output-filename port\n", argv
[0]);
47 if (!(f
= fopen(argv
[1], "w"))) {
48 fprintf(stderr
, "Unable to open output file \"%s\": %s\n",
49 argv
[1], strerror(errno
));
54 * If there is a pid file already around, kill the previously running
55 * fakesmtp process. Hopefully this will reduce the race conditions
56 * that crop up when running the test suite.
59 if (stat(PIDFILE
, &st
) == 0) {
62 if (!(pid
= fopen(PIDFILE
, "r"))) {
63 fprintf(stderr
, "Cannot open " PIDFILE
64 " (%s), continuing ...\n", strerror(errno
));
66 rc
= fscanf(pid
, "%ld", &oldpid
);
70 fprintf(stderr
, "Unable to parse pid in "
71 PIDFILE
", continuing ...\n");
73 kill((pid_t
) oldpid
, SIGTERM
);
80 memset(&hints
, 0, sizeof(hints
));
82 hints
.ai_family
= PF_INET
;
83 hints
.ai_socktype
= SOCK_STREAM
;
84 hints
.ai_protocol
= IPPROTO_TCP
;
85 hints
.ai_flags
= AI_PASSIVE
;
87 rc
= getaddrinfo("127.0.0.1", argv
[2], &hints
, &res
);
90 fprintf(stderr
, "Unable to resolve localhost/%s: %s\n",
91 argv
[2], gai_strerror(rc
));
95 l
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
98 fprintf(stderr
, "Unable to create listening socket: %s\n",
105 if (setsockopt(l
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) == -1) {
106 fprintf(stderr
, "Unable to set SO_REUSEADDR: %s\n",
111 if (bind(l
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
112 fprintf(stderr
, "Unable to bind socket: %s\n", strerror(errno
));
116 if (listen(l
, 1) == -1) {
117 fprintf(stderr
, "Unable to listen on socket: %s\n",
123 * Now we fork() and print out the process ID of our child
124 * for scripts to use. Once we do that, then exit.
131 fprintf(stderr
, "Unable to fork child: %s\n", strerror(errno
));
136 * Close stdin & stdout, otherwise people can
137 * think we're still doing stuff. For now leave stderr
144 printf("%ld\n", (long) child
);
149 * Now that our socket & files are set up, wait 30 seconds for
150 * a connection. If there isn't one, then exit.
153 if (!(pid
= fopen(PIDFILE
, "w"))) {
154 fprintf(stderr
, "Cannot open " PIDFILE
": %s\n",
159 fprintf(pid
, "%ld\n", (long) getpid());
162 signal(SIGTERM
, handleterm
);
170 rc
= select(l
+ 1, &readfd
, NULL
, NULL
, &tv
);
173 fprintf(stderr
, "select() failed: %s\n", strerror(errno
));
178 * I think if we get a timeout, we should just exit quietly.
186 * Alright, got a connection! Accept it.
189 if ((conn
= accept(l
, NULL
, NULL
)) == -1) {
190 fprintf(stderr
, "Unable to accept connection: %s\n",
198 * Pretend to be an SMTP server.
201 putsmtp(conn
, "220 Not really an ESMTP server");
207 rc
= getsmtp(conn
, line
);
212 fprintf(f
, "%s\n", line
);
215 * If we're in DATA mode, then check to see if we've got
216 * a "."; otherwise, continue
220 if (strcmp(line
, ".") == 0) {
222 putsmtp(conn
, "250 Thanks for the info!");
228 * Most commands we ignore and send the same response to.
231 if (strcmp(line
, "QUIT") == 0) {
234 putsmtp(conn
, "221 Later alligator!");
237 } else if (strcmp(line
, "DATA") == 0) {
238 putsmtp(conn
, "354 Go ahead");
241 putsmtp(conn
, "250 I'll buy that for a dollar!");
252 * Write a line to the SMTP client on the other end
256 putsmtp(int socket
, char *data
)
260 iov
[0].iov_base
= data
;
261 iov
[0].iov_len
= strlen(data
);
262 iov
[1].iov_base
= "\r\n";
265 writev(socket
, iov
, 2);
269 * Read a line (up to the \r\n)
273 getsmtp(int socket
, char *data
)
276 static unsigned int bytesinbuf
= 0;
277 static char buffer
[LINESIZE
* 2], *p
;
284 if (bytesinbuf
> 0 && (p
= strchr(buffer
, '\r')) &&
287 strncpy(data
, buffer
, LINESIZE
);
288 data
[LINESIZE
- 1] = '\0';
292 * Shuffle leftover bytes back to the beginning
295 bytesinbuf
-= cc
+ 2; /* Don't forget \r\n */
296 if (bytesinbuf
> 0) {
297 memmove(buffer
, buffer
+ cc
+ 2, bytesinbuf
);
302 if (bytesinbuf
>= sizeof(buffer
)) {
303 fprintf(stderr
, "Buffer overflow in getsmtp()!\n");
307 memset(buffer
+ bytesinbuf
, 0, sizeof(buffer
) - bytesinbuf
);
308 cc
= read(socket
, buffer
+ bytesinbuf
,
309 sizeof(buffer
) - bytesinbuf
);
312 fprintf(stderr
, "Read failed: %s\n", strerror(errno
));
328 handleterm(int signal
)
338 * Get rid of our pid file