diff options
author | 2020-04-30 02:01:35 +0200 | |
---|---|---|
committer | 2020-04-30 02:01:35 +0200 | |
commit | 6fb3965e81d9e4278ee5d96c2d68c014df1f3802 (patch) | |
tree | c3c895a4a39d4888e0ff978c97587120712a6b9d /smtpscript | |
parent | plug ubuntu-gcc10 to CI (diff) | |
download | OpenSMTPD-6fb3965e81d9e4278ee5d96c2d68c014df1f3802.tar.xz OpenSMTPD-6fb3965e81d9e4278ee5d96c2d68c014df1f3802.zip |
move
Diffstat (limited to 'smtpscript')
-rw-r--r-- | smtpscript/LICENSE | 15 | ||||
-rw-r--r-- | smtpscript/Makefile | 3 | ||||
-rw-r--r-- | smtpscript/Makefile.inc | 3 | ||||
-rw-r--r-- | smtpscript/README.md | 40 | ||||
-rw-r--r-- | smtpscript/iobuf.c | 466 | ||||
-rw-r--r-- | smtpscript/iobuf.h | 71 | ||||
-rw-r--r-- | smtpscript/parse.y | 905 | ||||
-rw-r--r-- | smtpscript/smtpscript.c | 1009 | ||||
-rw-r--r-- | smtpscript/smtpscript.h | 79 | ||||
-rw-r--r-- | smtpscript/smtpscript/Makefile | 12 | ||||
-rw-r--r-- | smtpscript/ssl.c | 167 |
11 files changed, 0 insertions, 2770 deletions
diff --git a/smtpscript/LICENSE b/smtpscript/LICENSE deleted file mode 100644 index 92859014..00000000 --- a/smtpscript/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2013-2014 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ diff --git a/smtpscript/Makefile b/smtpscript/Makefile deleted file mode 100644 index 6a628f2a..00000000 --- a/smtpscript/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -SUBDIR+= smtpscript - -.include <bsd.subdir.mk> diff --git a/smtpscript/Makefile.inc b/smtpscript/Makefile.inc deleted file mode 100644 index be93057b..00000000 --- a/smtpscript/Makefile.inc +++ /dev/null @@ -1,3 +0,0 @@ -CFLAGS= -Wall -W - -BINDIR?= /usr/bin diff --git a/smtpscript/README.md b/smtpscript/README.md deleted file mode 100644 index 18f5fe9e..00000000 --- a/smtpscript/README.md +++ /dev/null @@ -1,40 +0,0 @@ -smtpscript -========== - -smtpscript is a tool to write SMTP scenarios and easily implement regression tests for SMTP server-side implementations. - -A smtpscript will look like: - - - # this is a function init-helo that we want to call in all our regress tests - proc init-helo { - expect smtp ok - writeln "HELO regress" - expect smtp helo - } - - # each of the test-case will be called sequentially - test-case name "mailfrom.empty" { - call init-helo - writeln "MAIL FROM:<>" - expect smtp ok - } - - test-case name "mailfrom.broken" { - call init-helo - writeln "MAIL FROM:< @bleh>" - expect smtp permfail - } - - -which once executed, produces the output: - - $ smtpscript foo - ===> running test-case "mailfrom.empty" ok - ===> running test-case "mailfrom.broken" ok - ===> all run - passed: 2/2 (skipped: 0, failed: 0, error: 0) - $ - - -The scripting language also supports TLS, randomization and loops, so fairly complex scenarios can be achieved. diff --git a/smtpscript/iobuf.c b/smtpscript/iobuf.c deleted file mode 100644 index 05a9cd59..00000000 --- a/smtpscript/iobuf.c +++ /dev/null @@ -1,466 +0,0 @@ -/* $OpenBSD$ */ -/* - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/uio.h> - -#include <errno.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#ifdef IO_TLS -#include <openssl/err.h> -#include <openssl/ssl.h> -#endif - -#include "iobuf.h" - -#define IOBUF_MAX 65536 -#define IOBUFQ_MIN 4096 - -struct ioqbuf *ioqbuf_alloc(struct iobuf *, size_t); -void iobuf_drain(struct iobuf *, size_t); - -int -iobuf_init(struct iobuf *io, size_t size, size_t max) -{ - memset(io, 0, sizeof *io); - - if (max == 0) - max = IOBUF_MAX; - - if (size == 0) - size = max; - - if (size > max) - return (-1); - - if ((io->buf = malloc(size)) == NULL) - return (-1); - - io->size = size; - io->max = max; - - return (0); -} - -void -iobuf_clear(struct iobuf *io) -{ - struct ioqbuf *q; - - if (io->buf) - free(io->buf); - - while ((q = io->outq)) { - io->outq = q->next; - free(q); - } - - memset(io, 0, sizeof (*io)); -} - -void -iobuf_drain(struct iobuf *io, size_t n) -{ - struct ioqbuf *q; - size_t left = n; - - while ((q = io->outq) && left) { - if ((q->wpos - q->rpos) > left) { - q->rpos += left; - left = 0; - } else { - left -= q->wpos - q->rpos; - io->outq = q->next; - free(q); - } - } - - io->queued -= (n - left); - if (io->outq == NULL) - io->outqlast = NULL; -} - -int -iobuf_extend(struct iobuf *io, size_t n) -{ - char *t; - - if (n > io->max) - return (-1); - - if (io->max - io->size < n) - return (-1); - - t = realloc(io->buf, io->size + n); - if (t == NULL) - return (-1); - - io->size += n; - io->buf = t; - - return (0); -} - -size_t -iobuf_left(struct iobuf *io) -{ - return io->size - io->wpos; -} - -size_t -iobuf_space(struct iobuf *io) -{ - return io->size - (io->wpos - io->rpos); -} - -size_t -iobuf_len(struct iobuf *io) -{ - return io->wpos - io->rpos; -} - -char * -iobuf_data(struct iobuf *io) -{ - return io->buf + io->rpos; -} - -void -iobuf_drop(struct iobuf *io, size_t n) -{ - if (n >= iobuf_len(io)) { - io->rpos = io->wpos = 0; - return; - } - - io->rpos += n; -} - -char * -iobuf_getline(struct iobuf *iobuf, size_t *rlen) -{ - char *buf; - size_t len, i; - - buf = iobuf_data(iobuf); - len = iobuf_len(iobuf); - - for (i = 0; i + 1 <= len; i++) - if (buf[i] == '\n') { - /* Note: the returned address points into the iobuf - * buffer. We NUL-end it for convenience, and discard - * the data from the iobuf, so that the caller doesn't - * have to do it. The data remains "valid" as long - * as the iobuf does not overwrite it, that is until - * the next call to iobuf_normalize() or iobuf_extend(). - */ - iobuf_drop(iobuf, i + 1); - len = (i && buf[i - 1] == '\r') ? i - 1 : i; - buf[len] = '\0'; - if (rlen) - *rlen = len; - return (buf); - } - - return (NULL); -} - -void -iobuf_normalize(struct iobuf *io) -{ - if (io->rpos == 0) - return; - - if (io->rpos == io->wpos) { - io->rpos = io->wpos = 0; - return; - } - - memmove(io->buf, io->buf + io->rpos, io->wpos - io->rpos); - io->wpos -= io->rpos; - io->rpos = 0; -} - -ssize_t -iobuf_read(struct iobuf *io, int fd) -{ - ssize_t n; - - n = read(fd, io->buf + io->wpos, iobuf_left(io)); - if (n == -1) { - /* XXX is this really what we want? */ - if (errno == EAGAIN || errno == EINTR) - return (IOBUF_WANT_READ); - return (IOBUF_ERROR); - } - if (n == 0) - return (IOBUF_CLOSED); - - io->wpos += n; - - return (n); -} - -struct ioqbuf * -ioqbuf_alloc(struct iobuf *io, size_t len) -{ - struct ioqbuf *q; - - if (len < IOBUFQ_MIN) - len = IOBUFQ_MIN; - - if ((q = malloc(sizeof(*q) + len)) == NULL) - return (NULL); - - q->rpos = 0; - q->wpos = 0; - q->size = len; - q->next = NULL; - q->buf = (char *)(q) + sizeof(*q); - - if (io->outqlast == NULL) - io->outq = q; - else - io->outqlast->next = q; - io->outqlast = q; - - return (q); -} - -size_t -iobuf_queued(struct iobuf *io) -{ - return io->queued; -} - -void * -iobuf_reserve(struct iobuf *io, size_t len) -{ - struct ioqbuf *q; - void *r; - - if (len == 0) - return (NULL); - - if (((q = io->outqlast) == NULL) || q->size - q->wpos <= len) { - if ((q = ioqbuf_alloc(io, len)) == NULL) - return (NULL); - } - - r = q->buf + q->wpos; - q->wpos += len; - io->queued += len; - - return (r); -} - -int -iobuf_queue(struct iobuf *io, const void *data, size_t len) -{ - void *buf; - - if (len == 0) - return (0); - - if ((buf = iobuf_reserve(io, len)) == NULL) - return (-1); - - memmove(buf, data, len); - - return (0); -} - -int -iobuf_queuev(struct iobuf *io, const struct iovec *iov, int iovcnt) -{ - int i; - size_t len = 0; - char *buf; - - for (i = 0; i < iovcnt; i++) - len += iov[i].iov_len; - - if ((buf = iobuf_reserve(io, len)) == NULL) - return (-1); - - for (i = 0; i < iovcnt; i++) { - if (iov[i].iov_len == 0) - continue; - memmove(buf, iov[i].iov_base, iov[i].iov_len); - buf += iov[i].iov_len; - } - - return (0); - -} - -int -iobuf_fqueue(struct iobuf *io, const char *fmt, ...) -{ - va_list ap; - int len; - - va_start(ap, fmt); - len = iobuf_vfqueue(io, fmt, ap); - va_end(ap); - - return (len); -} - -int -iobuf_vfqueue(struct iobuf *io, const char *fmt, va_list ap) -{ - char *buf; - int len; - - len = vasprintf(&buf, fmt, ap); - - if (len == -1) - return (-1); - - len = iobuf_queue(io, buf, len); - free(buf); - - return (len); -} - -ssize_t -iobuf_write(struct iobuf *io, int fd) -{ - struct iovec iov[IOV_MAX]; - struct ioqbuf *q; - int i; - ssize_t n; - - i = 0; - for (q = io->outq; q ; q = q->next) { - if (i >= IOV_MAX) - break; - iov[i].iov_base = q->buf + q->rpos; - iov[i].iov_len = q->wpos - q->rpos; - i++; - } - - n = writev(fd, iov, i); - if (n == -1) { - if (errno == EAGAIN || errno == EINTR) - return (IOBUF_WANT_WRITE); - if (errno == EPIPE) - return (IOBUF_CLOSED); - return (IOBUF_ERROR); - } - - iobuf_drain(io, n); - - return (n); -} - -int -iobuf_flush(struct iobuf *io, int fd) -{ - ssize_t s; - - while (io->queued) - if ((s = iobuf_write(io, fd)) < 0) - return (s); - - return (0); -} - -#ifdef IO_TLS - -int -iobuf_flush_ssl(struct iobuf *io, void *ssl) -{ - ssize_t s; - - while (io->queued) - if ((s = iobuf_write_ssl(io, ssl) < 0)) - return (s); - - return (0); -} - -ssize_t -iobuf_write_ssl(struct iobuf *io, void *ssl) -{ - struct ioqbuf *q; - int r; - ssize_t n; - - q = io->outq; - n = SSL_write(ssl, q->buf + q->rpos, q->wpos - q->rpos); - if (n <= 0) { - switch ((r = SSL_get_error(ssl, n))) { - case SSL_ERROR_WANT_READ: - return (IOBUF_WANT_READ); - case SSL_ERROR_WANT_WRITE: - return (IOBUF_WANT_WRITE); - case SSL_ERROR_ZERO_RETURN: /* connection closed */ - return (IOBUF_CLOSED); - case SSL_ERROR_SYSCALL: - if (ERR_peek_last_error()) - return (IOBUF_SSLERROR); - if (r == 0) - errno = EPIPE; - return (IOBUF_ERROR); - default: - return (IOBUF_SSLERROR); - } - } - iobuf_drain(io, n); - - return (n); -} - -ssize_t -iobuf_read_ssl(struct iobuf *io, void *ssl) -{ - ssize_t n; - int r; - - n = SSL_read(ssl, io->buf + io->wpos, iobuf_left(io)); - if (n < 0) { - switch ((r = SSL_get_error(ssl, n))) { - case SSL_ERROR_WANT_READ: - return (IOBUF_WANT_READ); - case SSL_ERROR_WANT_WRITE: - return (IOBUF_WANT_WRITE); - case SSL_ERROR_SYSCALL: - if (ERR_peek_last_error()) - return (IOBUF_SSLERROR); - if (r == 0) - errno = EPIPE; - return (IOBUF_ERROR); - default: - return (IOBUF_SSLERROR); - } - } else if (n == 0) - return (IOBUF_CLOSED); - - io->wpos += n; - - return (n); -} - -#endif /* IO_TLS */ diff --git a/smtpscript/iobuf.h b/smtpscript/iobuf.h deleted file mode 100644 index ee4690c8..00000000 --- a/smtpscript/iobuf.h +++ /dev/null @@ -1,71 +0,0 @@ -/* $OpenBSD$ */ -/* - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> - -#include <stdarg.h> - -struct ioqbuf { - struct ioqbuf *next; - char *buf; - size_t size; - size_t wpos; - size_t rpos; -}; - -struct iobuf { - char *buf; - size_t max; - size_t size; - size_t wpos; - size_t rpos; - - size_t queued; - struct ioqbuf *outq; - struct ioqbuf *outqlast; -}; - -#define IOBUF_WANT_READ -1 -#define IOBUF_WANT_WRITE -2 -#define IOBUF_CLOSED -3 -#define IOBUF_ERROR -4 -#define IOBUF_SSLERROR -5 - -int iobuf_init(struct iobuf *, size_t, size_t); -void iobuf_clear(struct iobuf *); - -int iobuf_extend(struct iobuf *, size_t); -void iobuf_normalize(struct iobuf *); -void iobuf_drop(struct iobuf *, size_t); -size_t iobuf_space(struct iobuf *); -size_t iobuf_len(struct iobuf *); -size_t iobuf_left(struct iobuf *); -char *iobuf_data(struct iobuf *); -char *iobuf_getline(struct iobuf *, size_t *); -ssize_t iobuf_read(struct iobuf *, int); -ssize_t iobuf_read_ssl(struct iobuf *, void *); - -size_t iobuf_queued(struct iobuf *); -void* iobuf_reserve(struct iobuf *, size_t); -int iobuf_queue(struct iobuf *, const void*, size_t); -int iobuf_queuev(struct iobuf *, const struct iovec *, int); -int iobuf_fqueue(struct iobuf *, const char *, ...); -int iobuf_vfqueue(struct iobuf *, const char *, va_list); -int iobuf_flush(struct iobuf *, int); -int iobuf_flush_ssl(struct iobuf *, void *); -ssize_t iobuf_write(struct iobuf *, int); -ssize_t iobuf_write_ssl(struct iobuf *, void *); diff --git a/smtpscript/parse.y b/smtpscript/parse.y deleted file mode 100644 index 47a0f35c..00000000 --- a/smtpscript/parse.y +++ /dev/null @@ -1,905 +0,0 @@ -/* $OpenBSD: parse.y,v 1.109 2012/10/14 11:58:23 gilles Exp $ */ - -/* - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> - * Copyright (c) 2001 Markus Friedl. All rights reserved. - * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. - * Copyright (c) 2001 Theo de Raadt. All rights reserved. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -%{ -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/param.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/ioctl.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <inttypes.h> -#include <netdb.h> -#include <paths.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <util.h> - -#include "smtpscript.h" - -TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); -static struct file { - TAILQ_ENTRY(file) entry; - FILE *stream; - char *name; - int lineno; - int errors; -} *file, *topfile; -struct file *pushfile(const char *, int); -int popfile(void); -int check_file_secrecy(int, const char *); -int yyparse(void); -int yylex(void); -int kw_cmp(const void *, const void *); -int lookup(char *); -int lgetc(int); -int lungetc(int); -int findeol(void); -int yyerror(const char *, ...) - __attribute__ ((format (printf, 1, 2))); - -TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); -struct sym { - TAILQ_ENTRY(sym) entry; - int used; - int persist; - char *nam; - char *val; -}; -int symset(const char *, const char *, int); -char *symget(const char *); - -void push_op(struct op *); -struct op *peek_op(void); -struct op *pop_op(void); - -#define MAXDEPTH 50 - -static struct op * opstack[MAXDEPTH]; -static int opstackidx; - -static int errors = 0; - -static struct script *currscript; -static struct procedure *currproc; - -int delaytonum(char *); - -typedef struct { - union { - int64_t number; - char *string; - struct op *op; - } v; - int lineno; -} YYSTYPE; - -%} - -%token INCLUDE PORT REPEAT RANDOM NOOP -%token PROC TESTCASE NAME NO_AUTOCONNECT EXPECT FAIL SKIP -%token CALL CONNECT DISCONNECT STARTTLS SLEEP WRITE WRITELN -%token SMTP OK TEMPFAIL PERMFAIL HELO -%token ERROR ARROW -%token <v.string> STRING -%token <v.number> NUMBER -%type <v.number> quantifier port duration -%type <v.op> statement block -%% - -grammar : /* empty */ - | grammar '\n' - | grammar include '\n' - | grammar varset '\n' - | grammar proc '\n' - | grammar testcase '\n' - | grammar error '\n' { file->errors++; } - ; - -include : INCLUDE STRING { - struct file *nfile; - - if ((nfile = pushfile($2, 0)) == NULL) { - yyerror("failed to include file %s", $2); - free($2); - YYERROR; - } - free($2); - - file = nfile; - lungetc('\n'); - } - ; - -varset : STRING '=' STRING { - if (symset($1, $3, 0) == -1) - errx(1, "cannot store variable"); - free($1); - free($3); - } - ; - -optnl : '\n' optnl - | - ; - -nl : '\n' optnl - ; - -quantifier : /* empty */ { $$ = 1; } - | 's' { $$ = 1000; } - | 'm' { $$ = 60 * 1000; } - | 'h' { $$ = 3600 * 1000; } - ; - -duration : NUMBER quantifier { - if ($1 < 0) { - yyerror("invalid duration: %" PRId64, $1); - YYERROR; - } - $$ = $1 * $2; - } - ; - -port : PORT STRING { - struct servent *servent; - - servent = getservbyname($2, "tcp"); - if (servent == NULL) { - yyerror("port %s is invalid", $2); - free($2); - YYERROR; - } - $$ = ntohs(servent->s_port); - free($2); - } - | PORT NUMBER { - if ($2 <= 0 || $2 >= (int)USHRT_MAX) { - yyerror("invalid port: %" PRId64, $2); - YYERROR; - } - $$ = $2; - } - | /* empty */ { - $$ = 25; - } - ; - -statement : block - | REPEAT NUMBER { push_op(NULL); } statement { - pop_op(); - $$ = op_repeat(peek_op(), $2, $4); - } - | RANDOM { push_op(NULL); } block { - pop_op(); - $$ = op_random(peek_op(), $3); - } - | CALL STRING { - struct procedure *p; - p = procedure_get_by_name(currscript, $2); - if (p == NULL) { - yyerror("call to undefined proc \"%s\"", $2); - file->errors++; - } else if (p == currproc) { - yyerror("recursive call to proc \"%s\"", $2); - file->errors++; - } else { - $$ = op_call(peek_op(), p); - } - free($2); - } - | NOOP { - $$ = op_noop(peek_op()); - } - | SLEEP duration { - $$ = op_sleep(peek_op(), $2); - } - | FAIL STRING { - $$ = op_fail(peek_op(), $2); - } - | CONNECT STRING port { - $$ = op_connect(peek_op(), $2, $3); - } - | DISCONNECT { - $$ = op_disconnect(peek_op()); - } - | STARTTLS { - $$ = op_starttls(peek_op()); - } - | WRITE STRING { - $$ = op_write(peek_op(), $2, strlen($2)); - } - | WRITELN STRING { - $$ = op_printf(peek_op(), "%s\r\n", $2); - free($2); - } - | EXPECT DISCONNECT { - $$ = op_expect_disconnect(peek_op()); - } - | EXPECT SMTP { - $$ = op_expect_smtp_response(peek_op(), - RESP_SMTP_ANY | RESP_SMTP_MULTILINE); - } - | EXPECT SMTP OK { - $$ = op_expect_smtp_response(peek_op(), - RESP_SMTP_OK); - } - | EXPECT SMTP HELO { - $$ = op_expect_smtp_response(peek_op(), - RESP_SMTP_OK | RESP_SMTP_MULTILINE); - } - | EXPECT SMTP TEMPFAIL { - $$ = op_expect_smtp_response(peek_op(), - RESP_SMTP_TEMPFAIL); - } - | EXPECT SMTP PERMFAIL { - $$ = op_expect_smtp_response(peek_op(), - RESP_SMTP_PERMFAIL); - } - ; - -statement_list : statement nl statement_list - | statement - | /* EMPTY */ - ; - -block : '{' { - push_op(op_block(peek_op())); - } optnl statement_list '}' { - $$ = pop_op(); - } - ; - -procparam : '%' STRING { - if (proc_addvar(currproc, $2) == -1) { - yyerror("cannot add parameter %s", $2); - file->errors++; - } - } - ; - -procparams : procparam procparams - | /* EMPTY */ - ; - -proc : PROC STRING { - printf("# proc %s\n", $2); - currproc = procedure_create(currscript, $2); - if (currproc == NULL) - file->errors++; - } procparams block { - if (currproc) - currproc->root = $5; - } - ; - -testopt_name : NAME STRING { - if (procedure_get_by_name(currscript, $2)) { - file->errors++; - } else { - free(currproc->name); - currproc->name = ($2); - } - } - | /* EMPTY */ - ; - -testopt_cnx : NO_AUTOCONNECT { - currproc->flags |= PROC_NOCONNECT; - } - | /* EMPTY */ - ; -testopt_fail : EXPECT FAIL { - currproc->flags |= PROC_EXPECTFAIL; - } - | /* EMPTY */ - ; - -testopt_skip : SKIP { - currproc->flags |= PROC_SKIP; - } - | /* EMPTY */ - ; - -testcaseopts : testopt_name testopt_cnx testopt_fail testopt_skip; - -testcase : TESTCASE { - char buf[1024]; - snprintf(buf, sizeof buf, "<%s:%i>", - file->name, file->lineno); - currproc = procedure_create(currscript, strdup(buf)); - if (currproc) { - currproc->flags |= PROC_TESTCASE; - } else { - file->errors++; - } - } testcaseopts block { - currproc->root = $4; - } - ; -%% - -struct keywords { - const char *k_name; - int k_val; -}; - -int -yyerror(const char *fmt, ...) -{ - va_list ap; - - file->errors++; - va_start(ap, fmt); - fprintf(stderr, "%s:%d: ", file->name, yylval.lineno); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - va_end(ap); - return (0); -} - -int -kw_cmp(const void *k, const void *e) -{ - return (strcmp(k, ((const struct keywords *)e)->k_name)); -} - -int -lookup(char *s) -{ - /* this has to be sorted always */ - static const struct keywords keywords[] = { - { "call", CALL }, - { "connect", CONNECT }, - { "disconnect", DISCONNECT }, - { "expect", EXPECT }, - { "fail", FAIL }, - { "helo", HELO }, - { "name", NAME }, - { "no-autoconnect", NO_AUTOCONNECT }, - { "noop", NOOP }, - { "ok", OK }, - { "permfail", PERMFAIL }, - { "port", PORT }, - { "proc", PROC }, - { "random", RANDOM }, - { "repeat", REPEAT }, - { "skip", SKIP }, - { "sleep", SLEEP }, - { "smtp", SMTP }, - { "starttls", STARTTLS }, - { "tempfail", TEMPFAIL }, - { "test-case", TESTCASE }, - { "write", WRITE }, - { "writeln", WRITELN }, - }; - const struct keywords *p; - - p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), - sizeof(keywords[0]), kw_cmp); - - if (p) - return (p->k_val); - else - return (STRING); -} - -#define MAXPUSHBACK 128 - -char *parsebuf; -int parseindex; -char pushback_buffer[MAXPUSHBACK]; -int pushback_index = 0; - -int -lgetc(int quotec) -{ - int c, next; - - if (parsebuf) { - /* Read character from the parsebuffer instead of input. */ - if (parseindex >= 0) { - c = parsebuf[parseindex++]; - if (c != '\0') - return (c); - parsebuf = NULL; - } else - parseindex++; - } - - if (pushback_index) - return (pushback_buffer[--pushback_index]); - - if (quotec) { - if ((c = getc(file->stream)) == EOF) { - yyerror("reached end of file while parsing " - "quoted string"); - if (file == topfile || popfile() == EOF) - return (EOF); - return (quotec); - } - return (c); - } - - while ((c = getc(file->stream)) == '\\') { - next = getc(file->stream); - if (next != '\n') { - c = next; - break; - } - yylval.lineno = file->lineno; - file->lineno++; - } - - while (c == EOF) { - if (file == topfile || popfile() == EOF) - return (EOF); - c = getc(file->stream); - } - return (c); -} - -int -lungetc(int c) -{ - if (c == EOF) - return (EOF); - if (parsebuf) { - parseindex--; - if (parseindex >= 0) - return (c); - } - if (pushback_index < MAXPUSHBACK-1) - return (pushback_buffer[pushback_index++] = c); - else - return (EOF); -} - -int -findeol(void) -{ - int c; - - parsebuf = NULL; - pushback_index = 0; - - /* skip to either EOF or the first real EOL */ - while (1) { - c = lgetc(0); - if (c == '\n') { - file->lineno++; - break; - } - if (c == EOF) - break; - } - return (ERROR); -} - -int -yylex(void) -{ - char buf[8096]; - char *p, *val; - int quotec, next, c; - int token; - -top: - p = buf; - while ((c = lgetc(0)) == ' ' || c == '\t') - ; /* nothing */ - - yylval.lineno = file->lineno; - if (c == '#') - while ((c = lgetc(0)) != '\n' && c != EOF) - ; /* nothing */ - if (c == '$' && parsebuf == NULL) { - while (1) { - if ((c = lgetc(0)) == EOF) - return (0); - - if (p + 1 >= buf + sizeof(buf) - 1) { - yyerror("string too long"); - return (findeol()); - } - if (isalnum(c) || c == '_') { - *p++ = (char)c; - continue; - } - *p = '\0'; - lungetc(c); - break; - } - val = symget(buf); - if (val == NULL) { - yyerror("macro '%s' not defined", buf); - return (findeol()); - } - parsebuf = val; - parseindex = 0; - goto top; - } - - switch (c) { - case '\'': - case '"': - quotec = c; - while (1) { - if ((c = lgetc(quotec)) == EOF) - return (0); - if (c == '\n') { - file->lineno++; - continue; - } else if (c == '\\') { - if ((next = lgetc(quotec)) == EOF) - return (0); - if (next == quotec || c == ' ' || c == '\t') - c = next; - else if (next == '\n') { - file->lineno++; - continue; - } else - lungetc(next); - } else if (c == quotec) { - *p = '\0'; - break; - } - if (p + 1 >= buf + sizeof(buf) - 1) { - yyerror("string too long"); - return (findeol()); - } - *p++ = (char)c; - } - yylval.v.string = strdup(buf); - if (yylval.v.string == NULL) - err(1, "yylex: strdup"); - return (STRING); - } - -#define allowed_to_end_number(x) \ - (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') - - if (c == '-' || isdigit(c)) { - do { - *p++ = c; - if ((unsigned)(p-buf) >= sizeof(buf)) { - yyerror("string too long"); - return (findeol()); - } - } while ((c = lgetc(0)) != EOF && isdigit(c)); - lungetc(c); - if (p == buf + 1 && buf[0] == '-') - goto nodigits; - if (c == EOF || allowed_to_end_number(c)) { - const char *errstr = NULL; - - *p = '\0'; - yylval.v.number = strtonum(buf, LLONG_MIN, - LLONG_MAX, &errstr); - if (errstr) { - yyerror("\"%s\" invalid number: %s", - buf, errstr); - return (findeol()); - } - return (NUMBER); - } else { -nodigits: - while (p > buf + 1) - lungetc(*--p); - c = *--p; - if (c == '-') - return (c); - } - } - - if (c == '=') { - if ((c = lgetc(0)) != EOF && c == '>') - return (ARROW); - lungetc(c); - c = '='; - } - -#define allowed_in_string(x) \ - (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ - x != '{' && x != '}' && x != '<' && x != '>' && \ - x != '!' && x != '=' && x != '#' && \ - x != ',')) - - if (isalnum(c) || c == ':' || c == '_') { - do { - *p++ = c; - if ((unsigned)(p-buf) >= sizeof(buf)) { - yyerror("string too long"); - return (findeol()); - } - } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); - lungetc(c); - *p = '\0'; - if ((token = lookup(buf)) == STRING) - if ((yylval.v.string = strdup(buf)) == NULL) - err(1, "yylex: strdup"); - return (token); - } - if (c == '\n') { - yylval.lineno = file->lineno; - file->lineno++; - } - if (c == EOF) - return (0); - return (c); -} - -int -check_file_secrecy(int fd, const char *fname) -{ - struct stat st; - - if (fstat(fd, &st)) { - warn("cannot stat %s", fname); - return (-1); - } - if (st.st_uid != 0 && st.st_uid != getuid()) { - warnx("%s: owner not root or current user", fname); - return (-1); - } - if (st.st_mode & (S_IRWXG | S_IRWXO)) { - warnx("%s: group/world readable/writeable", fname); - return (-1); - } - return (0); -} - -struct file * -pushfile(const char *name, int secret) -{ - struct file *nfile; - - if ((nfile = calloc(1, sizeof(struct file))) == NULL) { - warn("malloc"); - return (NULL); - } - if ((nfile->name = strdup(name)) == NULL) { - warn("malloc"); - free(nfile); - return (NULL); - } - if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { - warn("%s", nfile->name); - free(nfile->name); - free(nfile); - return (NULL); - } else if (secret && - check_file_secrecy(fileno(nfile->stream), nfile->name)) { - fclose(nfile->stream); - free(nfile->name); - free(nfile); - return (NULL); - } - nfile->lineno = 1; - TAILQ_INSERT_TAIL(&files, nfile, entry); - return (nfile); -} - -int -popfile(void) -{ - struct file *prev; - - if ((prev = TAILQ_PREV(file, files, entry)) != NULL) - prev->errors += file->errors; - - TAILQ_REMOVE(&files, file, entry); - fclose(file->stream); - free(file->name); - free(file); - file = prev; - return (file ? 0 : EOF); -} - -struct script * -parse_script(const char *filename) -{ - errors = 0; - - currscript = calloc(1, sizeof *currscript); - TAILQ_INIT(&currscript->procs); - currproc = NULL; - - opstackidx = 0; - - if ((file = pushfile(filename, 0)) == NULL) - return (NULL); - - topfile = file; - - /* - * parse configuration - */ - setservent(1); - yyparse(); - errors = file->errors; - popfile(); - endservent(); - - if (errors) - return (NULL); - - return (currscript); -} - -int -symset(const char *nam, const char *val, int persist) -{ - struct sym *sym; - - for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); - sym = TAILQ_NEXT(sym, entry)) - ; /* nothing */ - - if (sym != NULL) { - if (sym->persist == 1) - return (0); - else { - free(sym->nam); - free(sym->val); - TAILQ_REMOVE(&symhead, sym, entry); - free(sym); - } - } - if ((sym = calloc(1, sizeof(*sym))) == NULL) - return (-1); - - sym->nam = strdup(nam); - if (sym->nam == NULL) { - free(sym); - return (-1); - } - sym->val = strdup(val); - if (sym->val == NULL) { - free(sym->nam); - free(sym); - return (-1); - } - sym->used = 0; - sym->persist = persist; - TAILQ_INSERT_TAIL(&symhead, sym, entry); - return (0); -} - -int -cmdline_symset(char *s) -{ - char *sym, *val; - int ret; - size_t len; - - if ((val = strrchr(s, '=')) == NULL) - return (-1); - - len = strlen(s) - strlen(val) + 1; - if ((sym = malloc(len)) == NULL) - errx(1, "cmdline_symset: malloc"); - - (void)strlcpy(sym, s, len); - - ret = symset(sym, val + 1, 1); - free(sym); - - return (ret); -} - -char * -symget(const char *nam) -{ - struct sym *sym; - - TAILQ_FOREACH(sym, &symhead, entry) - if (strcmp(nam, sym->nam) == 0) { - sym->used = 1; - return (sym->val); - } - return (NULL); -} - -int -delaytonum(char *str) -{ - unsigned int factor; - size_t len; - const char *errstr = NULL; - int delay; - - /* we need at least 1 digit and 1 unit */ - len = strlen(str); - if (len < 2) - goto bad; - - switch(str[len - 1]) { - - case 's': - factor = 1; - break; - - case 'm': - factor = 60; - break; - - case 'h': - factor = 60 * 60; - break; - - case 'd': - factor = 24 * 60 * 60; - break; - - default: - goto bad; - } - - str[len - 1] = '\0'; - delay = strtonum(str, 1, INT_MAX / factor, &errstr); - if (errstr) - goto bad; - - return (delay * factor); - -bad: - return (-1); -} - - -void -push_op(struct op *op) -{ - if (opstackidx == MAXDEPTH) { - yyerror("too deep"); - return; - } - opstack[opstackidx++] = op; -} - -struct op * -pop_op(void) -{ - if (opstackidx == 0) - return (NULL); - return (opstack[--opstackidx]); -} - -struct op * -peek_op(void) -{ - if (opstackidx == 0) - return (NULL); - return (opstack[opstackidx - 1]); -} diff --git a/smtpscript/smtpscript.c b/smtpscript/smtpscript.c deleted file mode 100644 index a75c4249..00000000 --- a/smtpscript/smtpscript.c +++ /dev/null @@ -1,1009 +0,0 @@ -/* $OpenBSD: iobuf.h,v 1.1 2012/01/29 00:32:51 eric Exp $ */ -/* - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/queue.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <getopt.h> -#include <netdb.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <vis.h> - -#include "iobuf.h" - -#include "smtpscript.h" - -void *ssl_connect(int); -void ssl_close(void *); - -/* XXX */ -#define SMTP_LINE_MAX 4096 - -enum { - OP_BLOCK, - OP_REPEAT, - OP_RANDOM, - - OP_NOOP, - - OP_FAIL, - OP_CALL, - OP_CONNECT, - OP_DISCONNECT, - OP_STARTTLS, - OP_SLEEP, - OP_WRITE, - - OP_EXPECT_DISCONNECT, - OP_EXPECT_SMTP_RESPONSE, -}; - -struct op { - struct op *next; - int type; - union { - struct { - int count; - struct op *start; - struct op *last; - } block; - struct { - struct op *op; - int count; - } repeat; - struct { - struct op *block; - } random; - struct { - char *reason; - } fail; - struct { - struct procedure *proc; - } call; - struct { - char *hostname; - int portno; - } connect; - struct { - unsigned int ms; - } sleep; - struct { - const void *buf; - size_t len; - } write; - struct { - int flags; - } expect_smtp; - } u; -}; - -#define RES_OK 0 -#define RES_SKIP 1 -#define RES_FAIL 2 -#define RES_ERROR 3 - -struct ctx { - int sock; - void *ssl; - struct iobuf iobuf; - int lvl; - - int result; - char *reason; -}; - -static struct op * _op_connect; - -int verbose; -int randomdelay; /* between each testcase */ -int tapout; -size_t rundelay; /* between each testcase */ - -static size_t test_pass; -static size_t test_skip; -static size_t test_fail; -static size_t test_error; -static size_t test_total = 0; - -static struct op *op_add_child(struct op *, const struct op *); -static void run_testcase(struct procedure *); -static void print_testcase(char *status, char *name, char *reason, char *directive, size_t number); -static void process_op(struct ctx *, struct op *); -static const char * parse_smtp_response(char *, size_t, char **, int *); - -struct procedure * -procedure_create(struct script *scr, char *name) -{ - struct procedure *p; - - if (procedure_get_by_name(scr, name)) { - warnx("procedure \"%s\" already exists", name); - return (NULL); - } - - p = calloc(1, sizeof *p); - TAILQ_INIT(&p->vars); - p->name = strdup(name); - - TAILQ_INSERT_TAIL(&scr->procs, p, entry); - - return (p); -} - -struct procedure * -procedure_get_by_name(struct script *scr, const char *name) -{ - struct procedure *p; - - TAILQ_FOREACH(p, &scr->procs, entry) - if (!strcmp(name, p->name)) - return (p); - - return (NULL); -} - -int -proc_getvaridx(struct procedure *proc, char *name) -{ - struct variable *v; - int n; - - n = 0; - TAILQ_FOREACH(v, &proc->vars, entry) { - if (!strcmp(name, v->name)) - return (n); - n++; - } - - return (-1); -} - -int -proc_addvar(struct procedure *proc, char *name) -{ - struct variable *v; - - printf("adding variable \"%s\"\n", name); - - if (proc_getvaridx(proc, name) != -1) - return (-1); - v = calloc(1, sizeof *v); - v->name = name; - TAILQ_INSERT_TAIL(&proc->vars, v, entry); - - return (proc->varcount++); -} - -struct op * -op_block(struct op *parent) -{ - struct op o; - - bzero(&o, sizeof o); - o.type = OP_BLOCK; - - return (op_add_child(parent, &o)); -} - -struct op * -op_repeat(struct op *parent, int count, struct op *op) -{ - struct op o; - - bzero(&o, sizeof o); - o.type = OP_REPEAT; - o.u.repeat.count = count; - o.u.repeat.op = op; - - return (op_add_child(parent, &o)); -} - -struct op * -op_random(struct op *parent, struct op *op) -{ - struct op o; - - bzero(&o, sizeof o); - o.type = OP_RANDOM; - o.u.random.block = op; - - return (op_add_child(parent, &o)); -} - -struct op * -op_noop(struct op *parent) -{ - struct op o; - - bzero(&o, sizeof o); - o.type = OP_NOOP; - - return (op_add_child(parent, &o)); -} - -struct op * -op_call(struct op *parent, struct procedure *proc) -{ - struct op o; - - bzero(&o, sizeof o); - o.type = OP_CALL; - o.u.call.proc = proc; - - return (op_add_child(parent, &o)); -} - -struct op * -op_fail(struct op *parent, char *reason) -{ - struct op o; - - bzero(&o, sizeof o); - o.type = OP_FAIL; - o.u.fail.reason = reason; - - return (op_add_child(parent, &o)); -} - -struct op * -op_connect(struct op *parent, const char *hostname, int portno) -{ - struct op o; - - bzero(&o, sizeof o); - o.type = OP_CONNECT; - o.u.connect.hostname = strdup(hostname); - o.u.connect.portno = portno; - return (op_add_child(parent, &o)); -} - -struct op * -op_disconnect(struct op *parent) -{ - struct op o; - - bzero(&o, sizeof o); - o.type = OP_DISCONNECT; - return (op_add_child(parent, &o)); -} - -struct op * -op_starttls(struct op *parent) -{ - struct op o; - - bzero(&o, sizeof o); - o.type = OP_STARTTLS; - return (op_add_child(parent, &o)); -} - -struct op * -op_sleep(struct op *parent, unsigned int ms) -{ - struct op o; - - bzero(&o, sizeof o); - o.type = OP_SLEEP; - o.u.sleep.ms = ms; - return (op_add_child(parent, &o)); -} - -struct op * -op_write(struct op *parent, const void *buf, size_t len) -{ - struct op o; - - bzero(&o, sizeof o); - o.type = OP_WRITE; - o.u.write.buf = buf; - o.u.write.len = len; - return (op_add_child(parent, &o)); -} - -struct op * -op_printf(struct op *parent, const char *fmt, ...) -{ - va_list ap; - char *buf; - int len; - - va_start(ap, fmt); - if ((len = vasprintf(&buf, fmt, ap)) == -1) - err(1, "vasprintf"); - va_end(ap); - - return op_write(parent, buf, len); -} - -struct op * -op_expect_disconnect(struct op *parent) -{ - struct op o; - - bzero(&o, sizeof o); - o.type = OP_EXPECT_DISCONNECT; - return (op_add_child(parent, &o)); -} - -struct op * -op_expect_smtp_response(struct op *parent, int flags) -{ - struct op o; - - bzero(&o, sizeof o); - o.type = OP_EXPECT_SMTP_RESPONSE; - o.u.expect_smtp.flags = flags; - return (op_add_child(parent, &o)); -} - -static void -usage(void) -{ - extern const char *__progname; - errx(1, "Usage: %s [-rvt] [-d delay] script", __progname); -} - -int -main(int argc, char **argv) -{ - struct script *s; - struct procedure *p; - int ch; - - while ((ch = getopt(argc, argv, "d:rvt")) != -1) { - switch(ch) { - case 'v': - verbose += 1; - break; - case 'd': - rundelay = atoi(optarg) * 1000; - break; - case 'r': - randomdelay = 1; - break; - case 't': - tapout = 1; - break; - default: - usage(); - /* NOTREACHED */ - } - } - argc -= optind; - argv += optind; - - if (argc != 1) - usage(); - - s = parse_script(argv[0]); - if (s == NULL) - errx(1, "error reading script file"); - - _op_connect = op_connect(NULL, "127.0.0.1", 25); - - if (tapout) { - printf("# smtpscript is an SMTP testing framework\n\n"); - printf("TAP version 13\n"); - } - - TAILQ_FOREACH(p, &s->procs, entry) - if (p->flags & PROC_TESTCASE) - run_testcase(p); - - if (tapout) - printf("1..%zu\n", test_total); - else { - printf("passed: %zu/%zu (skipped: %zu, failed: %zu, error: %zu)\n", - test_pass, - test_total, - test_skip, - test_fail, - test_error); - } - return (0); -} - -static struct op * -op_add_child(struct op *parent, const struct op *op) -{ - struct op *n; - - n = malloc(sizeof *n); - if (n == NULL) - err(1, "malloc"); - - memmove(n, op, sizeof *n); - n->next = NULL; - - /* printf("op:%p type:%i parent: %p\n", n, n->type, parent); */ - - if (parent) { - if (parent->u.block.start == NULL) - parent->u.block.start = n; - if (parent->u.block.last) - parent->u.block.last->next = n; - parent->u.block.last = n; - parent->u.block.count += 1; - } - - return (n); -} - -static void -run_testcase(struct procedure *proc) -{ - struct ctx c; - uint32_t rdelay; - - bzero(&c, sizeof c); - c.sock = -1; - c.lvl = 1; - - if (rundelay) { - if (randomdelay) - rdelay = arc4random_uniform(rundelay); - else - rdelay = rundelay; - usleep(rdelay); - } - - fflush(stdout); - - if (verbose > 1) - printf("\n"); - - if (!(proc->flags & PROC_NOCONNECT)) - process_op(&c, _op_connect); - process_op(&c, proc->root); - - if (c.sock != -1) - close(c.sock); - if (c.ssl) - ssl_close(c.ssl); - iobuf_clear(&c.iobuf); - - if (verbose > 1) { - printf("# Done with test-case \"%s\": ", proc->name); - } - - switch (c.result) { - case RES_OK: - test_total += 1; - if (proc->flags & PROC_EXPECTFAIL) { - print_testcase("not ok", proc->name, c.reason, "TODO", test_total); // XPass - test_fail += 1; - } else if (proc->flags & PROC_SKIP) { - test_skip += 1; - print_testcase("ok", proc->name, c.reason, "SKIP", test_total); - } - else { - print_testcase("ok", proc->name, c.reason, NULL, test_total); - test_pass += 1; - } - - break; - - case RES_SKIP: - test_skip += 1; - test_total += 1; - print_testcase("not ok", proc->name, c.reason, "SKIP", test_total); - break; - - case RES_FAIL: - test_total += 1; - if (proc->flags & PROC_EXPECTFAIL) { - print_testcase("not ok", proc->name, c.reason, "TODO", test_total); // XFail - test_pass += 1; - } else if (proc->flags & PROC_SKIP) { - test_skip += 1; - print_testcase("ok", proc->name, c.reason, "SKIP", test_total); - } - else { - print_testcase("not ok", proc->name, c.reason, NULL, test_total); - test_fail += 1; - } - - break; - - case RES_ERROR: - test_error += 1; - test_total += 1; - print_testcase("not ok", proc->name, c.reason, NULL, test_total); - break; - } - - if (verbose > 1) { - printf("\n"); - } - -} - -void print_testcase(char *status, char *name, char *reason, char *directive, size_t number) -{ - printf("%s %zu", status, number); - if (directive) - printf(" - %s # %s\n", name, directive); - else - if (reason) - printf(" - %s # %s\n", name, reason); - else - printf(" - %s\n", name); -} - -static size_t -strvisx2(char *dst, const char *src, size_t srclen, int flag) -{ - size_t n, r, i; - - n = strvisx(dst, src, srclen, flag); - if (n == 0) - return (0); - - r = n; - for (i = n - 1; i; i--) { - if (dst[i] == '\r') { - memmove(dst + i + 2, dst + i + 1, n + 1 - i); - dst[i+1] = 'r'; - dst[i] = '\\'; - r++; - } else if (dst[i] == '"') { - memmove(dst + i + 2, dst + i + 1, n + 1 - i); - dst[i+1] = '"'; - dst[i] = '\\'; - r++; - } - } - - return (r); -} - -static const char * -show_data(const char *src, size_t len, size_t max) -{ - static char buf[8192 + 3]; - char tmp[256]; - size_t l, n; - - l = len; - if (len > 2048) - l = 2048; - - buf[0] = '"'; - n = strvisx2(&buf[1], src, l, VIS_SAFE | VIS_NL | VIS_TAB | VIS_CSTYLE); - if (n >= max) { - snprintf(tmp, sizeof tmp, "...\" [%zu]", l); - buf[max - strlen(tmp)] = '\0'; - strlcat(buf, tmp, sizeof(buf)); - } else { - strlcat(buf, "\"", sizeof(buf)); - } - - return (buf); -} - -static void -print_op(struct op *op, int lvl) -{ - - - if (op->type == OP_BLOCK) - return; - - while (lvl--) - printf(" "); - - switch(op->type) { - - case OP_REPEAT: - printf("=> repeat: %i\n", op->u.repeat.count); - break; - - case OP_RANDOM: - printf("=> random: %i\n", op->u.random.block->u.block.count); - break; - - case OP_NOOP: - printf("=> noop\n"); - break; - - case OP_FAIL: - printf("=> fail: %s\n", op->u.fail.reason); - break; - - case OP_CALL: - printf("=> call: %s\n", op->u.call.proc->name); - break; - - case OP_CONNECT: - printf("=> connect %s:%i\n", - op->u.connect.hostname, - op->u.connect.portno); - break; - - case OP_DISCONNECT: - printf("=> disconnect\n"); - break; - - case OP_STARTTLS: - printf("=> starttls\n"); - break; - - case OP_SLEEP: - printf("=> sleep %ims\n", op->u.sleep.ms); - break; - - case OP_WRITE: - printf("=> write %s\n", - show_data(op->u.write.buf, op->u.write.len, 70)); - break; - - case OP_EXPECT_DISCONNECT: - printf("<= disconnect\n"); - break; - - case OP_EXPECT_SMTP_RESPONSE: - printf("<= smtp-response 0x%04x\n", op->u.expect_smtp.flags); - break; - - default: - printf("<> ??? %i;\n", op->type); - break; - } -} - - -static void -set_failure(struct ctx *ctx, int res, const char *fmt, ...) -{ - va_list ap; - int len; - - ctx->result = res; - va_start(ap, fmt); - if ((len = vasprintf(&ctx->reason, fmt, ap)) == -1) - err(1, "vasprintf"); - va_end(ap); -} - -static void -process_op(struct ctx *ctx, struct op *op) -{ - struct addrinfo hints, *a, *ai; - struct op *o; - struct iobuf *iobuf; - int i, r, s, save_errno, cont; - const char *cause; - char buf[16], *servname, *line; - ssize_t n; - size_t len; - const char *e; - - if (verbose > 1) - print_op(op, ctx->lvl); - - iobuf = &ctx->iobuf; - - switch(op->type) { - - case OP_BLOCK: - ctx->lvl += 1; - for (o = op->u.block.start; o; o = o->next) { - process_op(ctx, o); - if (ctx->result) - break; - } - ctx->lvl -= 1; - break; - - case OP_REPEAT: - ctx->lvl += 1; - for (i = 0; i < op->u.repeat.count; i++) { - process_op(ctx, op->u.repeat.op); - if (ctx->result) - break; - } - ctx->lvl -= 1; - break; - - case OP_RANDOM: - - if (op->u.random.block->u.block.count == 0) - return; - - ctx->lvl += 1; - - i = arc4random_uniform(op->u.random.block->u.block.count); - for (o = op->u.random.block->u.block.start; i; i--, o = o->next) - ; - process_op(ctx, o); - if (ctx->result) - break; - ctx->lvl -= 1; - break; - - case OP_NOOP: - break; - - case OP_FAIL: - set_failure(ctx, RES_FAIL, op->u.fail.reason); - break; - - case OP_CALL: - process_op(ctx, op->u.call.proc->root); - break; - - case OP_CONNECT: - if (ctx->sock != -1) - close(ctx->sock); - ctx->sock = -1; - iobuf_clear(iobuf); - - servname = NULL; - if (op->u.connect.portno) { - snprintf(buf, sizeof buf, "%i", op->u.connect.portno); - servname = buf; - } - bzero(&hints, sizeof hints); - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - r = getaddrinfo(op->u.connect.hostname, servname, &hints, &ai); - if (r) { - set_failure(ctx, RES_ERROR, - "failed to connect to %s:%s: %s", - op->u.connect.hostname, servname, gai_strerror(r)); - return; - } - - s = -1; - for(a = ai; a; a = a->ai_next) { - s = socket(a->ai_family, a->ai_socktype, a->ai_protocol); - if (s == -1) { - cause = "socket"; - continue; - } - if (connect(s, a->ai_addr, a->ai_addrlen) == -1) { - cause = "connect"; - save_errno = errno; - close(s); - errno = save_errno; - s = -1; - continue; - } - break; /* okay we got one */ - } - freeaddrinfo(ai); - if (s == -1) { - set_failure(ctx, RES_ERROR, - "failed to connect to %s:%s: %s", - op->u.connect.hostname, servname, cause); - } else { - ctx->sock = s; - iobuf_init(iobuf, 0, 0); - } - break; - - case OP_DISCONNECT: - if (ctx->sock != -1) - close(ctx->sock); - ctx->sock = -1; - iobuf_clear(iobuf); - break; - - case OP_STARTTLS: - if (ctx->ssl) - set_failure(ctx, RES_ERROR, "SSL context already here"); - else if ((ctx->ssl = ssl_connect(ctx->sock)) == NULL) - set_failure(ctx, RES_ERROR, "SSL connection failed"); - break; - - case OP_SLEEP: - usleep(op->u.sleep.ms * 1000); - break; - - case OP_WRITE: - iobuf_queue(iobuf, op->u.write.buf, op->u.write.len); - if (ctx->ssl) - r = iobuf_flush_ssl(iobuf, ctx->ssl); - else - r = iobuf_flush(iobuf, ctx->sock); - switch (r) { - case 0: - break; - case IOBUF_CLOSED: - set_failure(ctx, RES_FAIL, "connection closed"); - break; - case IOBUF_WANT_WRITE: - set_failure(ctx, RES_ERROR, "iobuf_write(): WANT_WRITE"); - break; - case IOBUF_ERROR: - set_failure(ctx, RES_ERROR, "IO error"); - break; - case IOBUF_SSLERROR: - set_failure(ctx, RES_ERROR, "SSL error"); - break; - default: - set_failure(ctx, RES_ERROR, "iobuf_write(): bad value"); - break; - } - break; - - case OP_EXPECT_DISCONNECT: - if (iobuf_len(iobuf)) { - set_failure(ctx, RES_ERROR, "%zu bytes of input left", - iobuf_len(iobuf)); - break; - } - if (ctx->ssl) - n = iobuf_read_ssl(iobuf, ctx->ssl); - else - n = iobuf_read(iobuf, ctx->sock); - switch (n) { - case IOBUF_CLOSED: - close(ctx->sock); - ctx->sock = -1; - if (ctx->ssl) - ssl_close(ctx->ssl); - break; - case IOBUF_WANT_READ: - set_failure(ctx, RES_ERROR, "iobuf_read(): WANT_READ"); - break; - case IOBUF_ERROR: - set_failure(ctx, RES_ERROR, "IO error"); - break; - case IOBUF_SSLERROR: - set_failure(ctx, RES_ERROR, "SSL error"); - break; - default: - set_failure(ctx, RES_FAIL, "data read: %s", - show_data(iobuf_data(iobuf), iobuf_len(iobuf), 70)); - break; - } - break; - - case OP_EXPECT_SMTP_RESPONSE: - line = NULL; - while (1) { - line = iobuf_getline(iobuf, &len); - if (line) { - e = parse_smtp_response(line, len, NULL, &cont); - if (e) { - set_failure(ctx, RES_FAIL, e); - return; - } - if (!cont) { - iobuf_normalize(iobuf); - break; - } - if (!(op->u.expect_smtp.flags - & RESP_SMTP_MULTILINE)) { - set_failure(ctx, RES_FAIL, - "single line response expected"); - return; - } - continue; - } - - if (iobuf_len(iobuf) >= SMTP_LINE_MAX) { - set_failure(ctx, RES_FAIL, "line too long"); - return; - } - - iobuf_normalize(iobuf); - - again: - if (ctx->ssl) - n = iobuf_read_ssl(iobuf, ctx->ssl); - else - n = iobuf_read(iobuf, ctx->sock); - switch (n) { - case IOBUF_CLOSED: - set_failure(ctx, RES_FAIL, "connection closed"); - return; - case IOBUF_WANT_READ: - goto again; - case IOBUF_ERROR: - set_failure(ctx, RES_ERROR, "io error"); - return; - case IOBUF_SSLERROR: - set_failure(ctx, RES_ERROR, "SSL error"); - return; - default: - break; - } - } - - /* got our response */ - - if (verbose > 1) { - len = ctx->lvl; - while (len--) - printf(" "); - printf(" >>> %s\n", show_data(line, strlen(line), 70)); - } - - switch (line[0]) { - case '2': - case '3': - if (!(op->u.expect_smtp.flags & RESP_SMTP_OK)) - set_failure(ctx, RES_FAIL, - "unexpected response code0: %s", line); - break; - case '4': - if (!(op->u.expect_smtp.flags & RESP_SMTP_TEMPFAIL)) - set_failure(ctx, RES_FAIL, - "unexpected response code1: %s", line); - break; - case '5': - if (!(op->u.expect_smtp.flags & RESP_SMTP_PERMFAIL)) - set_failure(ctx, RES_FAIL, - "unexpected response code2: %s", line); - break; - default: - set_failure(ctx, RES_FAIL, - "unexpected response code???: %s", line); - break; - } - break; - - default: - ctx->result = RES_ERROR; - ctx->reason = "invalid operator"; - break; - } -} - -static const char * -parse_smtp_response(char *line, size_t len, char **msg, int *cont) -{ - size_t i; - - if (len >= SMTP_LINE_MAX) - return "line too long"; - - if (len > 3) { - if (msg) - *msg = line + 4; - if (cont) - *cont = (line[3] == '-'); - } else if (len == 3) { - if (msg) - *msg = line + 3; - if (cont) - *cont = 0; - } else - return "line too short"; - - /* validate reply code */ - if (line[0] < '2' || line[0] > '5' || !isdigit(line[1]) || - !isdigit(line[2])) - return "reply code out of range"; - - /* validate reply message */ - for (i = 0; i < len; i++) - if (!isprint(line[i])) - return "non-printable character in reply"; - - return NULL; -} diff --git a/smtpscript/smtpscript.h b/smtpscript/smtpscript.h deleted file mode 100644 index ba90b240..00000000 --- a/smtpscript/smtpscript.h +++ /dev/null @@ -1,79 +0,0 @@ -/* $OpenBSD: iobuf.h,v 1.1 2012/01/29 00:32:51 eric Exp $ */ -/* - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -struct op; - -#define PROC_TESTCASE 0x0001 -#define PROC_SKIP 0x0002 -#define PROC_EXPECTFAIL 0x0004 -#define PROC_NOCONNECT 0x0008 - - -struct variable { - TAILQ_ENTRY(variable) entry; - char *name; -}; - -struct procedure { - int flags; - - TAILQ_ENTRY(procedure) entry; - char *name; - - TAILQ_HEAD(, variable) vars; - int varcount; - - struct op *root; - - int skip; - int expect_fail; -}; - -#define RESP_SMTP_OK 0x0001 -#define RESP_SMTP_TEMPFAIL 0x0002 -#define RESP_SMTP_PERMFAIL 0x0004 -#define RESP_SMTP_ANY 0x0007 - -#define RESP_SMTP_MULTILINE 0x0100 - -struct script { - TAILQ_HEAD(, procedure) procs; -}; - -int proc_addvar(struct procedure *, char *name); -int proc_getvaridx(struct procedure *, char *name); - -struct op *op_block(struct op *); -struct op *op_repeat(struct op *, int, struct op *); -struct op *op_random(struct op *, struct op *); -struct op *op_noop(struct op *); -struct op *op_fail(struct op *, char *); -struct op *op_call(struct op *, struct procedure *); -struct op *op_connect(struct op *, const char *, int); -struct op *op_disconnect(struct op *); -struct op *op_starttls(struct op *); -struct op *op_sleep(struct op *, unsigned int); -struct op *op_write(struct op *, const void *, size_t); -struct op *op_printf(struct op *, const char *, ...); - -struct op *op_expect_disconnect(struct op *); -struct op *op_expect_smtp_response(struct op *, int); - -struct procedure *procedure_create(struct script *, char *); -struct procedure *procedure_get_by_name(struct script *, const char *); - -struct script * parse_script(const char *); diff --git a/smtpscript/smtpscript/Makefile b/smtpscript/smtpscript/Makefile deleted file mode 100644 index 0520606f..00000000 --- a/smtpscript/smtpscript/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -.PATH: ${.CURDIR}/.. - -PROG= smtpscript -SRCS= smtpscript.c iobuf.c parse.y ssl.c -NOMAN= noman - -LDADD+= -lutil -lssl -lcrypto -DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO} -CPPFLAGS+= -I${.CURDIR}/.. -CPPFLAGS+= -DIO_TLS - -.include <bsd.prog.mk> diff --git a/smtpscript/ssl.c b/smtpscript/ssl.c deleted file mode 100644 index 54f0993d..00000000 --- a/smtpscript/ssl.c +++ /dev/null @@ -1,167 +0,0 @@ -/* $OpenBSD: ssl.c,v 1.50 2012/11/12 14:58:53 eric Exp $ */ - -/* - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/param.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <err.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <openssl/ssl.h> -#include <openssl/engine.h> -#include <openssl/err.h> - -#define SSL_CIPHERS "HIGH" - -void ssl_error(const char *); - -static void ssl_init(void); -static SSL_CTX *ssl_ctx_create(void); -static void *ssl_client_ctx(void); - -static void -ssl_init(void) -{ - static int init = 0; - - if (init) - return; - - init = 1; - - SSL_library_init(); - SSL_load_error_strings(); - - OpenSSL_add_all_algorithms(); - - /* Init hardware crypto engines. */ - ENGINE_load_builtin_engines(); - ENGINE_register_all_complete(); -} - -void -ssl_error(const char *where) -{ - unsigned long code; - char errbuf[128]; - - for (; (code = ERR_get_error()) != 0 ;) { - ERR_error_string_n(code, errbuf, sizeof(errbuf)); - fprintf(stderr, "debug: SSL library error: %s: %s", - where, errbuf); - } -} - -void * -ssl_connect(int sock) -{ - SSL *ssl; - - ssl = ssl_client_ctx(); - - if (SSL_set_fd(ssl, sock) == 0) { - ssl_error("ssl_connect:SSL_set_fd"); - SSL_free(ssl); - return (NULL); - } - - if (SSL_connect(ssl) != 1) { - ssl_error("ssl_connect:SSL_connect"); - SSL_free(ssl); - return (NULL); - } - - return ((void*)ssl); -} - -void -ssl_close(void *a) -{ - SSL *ssl = a; - - SSL_free(ssl); -} - -static SSL_CTX * -ssl_ctx_create(void) -{ - SSL_CTX *ctx; - - ssl_init(); - - ctx = SSL_CTX_new(SSLv23_method()); - if (ctx == NULL) { - ssl_error("ssl_ctx_create"); - errx(1, "ssl_ctx_create: could not create SSL context"); - } - - SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); - SSL_CTX_set_timeout(ctx, 30); - SSL_CTX_set_options(ctx, - SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_TICKET); - SSL_CTX_set_options(ctx, - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - - if (!SSL_CTX_set_cipher_list(ctx, SSL_CIPHERS)) { - ssl_error("ssl_ctx_create"); - errx(1, "ssl_ctx_create: could not set cipher list"); - } - - return (ctx); -} - -static void * -ssl_client_ctx(void) -{ - SSL_CTX *ctx; - SSL *ssl = NULL; - int rv = -1; - - ctx = ssl_ctx_create(); - - if ((ssl = SSL_new(ctx)) == NULL) - goto done; - SSL_CTX_free(ctx); - - if (!SSL_set_ssl_method(ssl, SSLv23_client_method())) - goto done; - - rv = 0; -done: - if (rv) { - if (ssl) - SSL_free(ssl); - else if (ctx) - SSL_CTX_free(ctx); - ssl = NULL; - } - return (void*)(ssl); -} |