]> source.dussan.org Git - rspamd.git/commitdiff
* Start dkim support implementation.
authorVsevolod Stakhov <vsevolod@rambler-co.ru>
Thu, 24 May 2012 17:35:03 +0000 (21:35 +0400)
committerVsevolod Stakhov <vsevolod@rambler-co.ru>
Thu, 24 May 2012 17:35:03 +0000 (21:35 +0400)
lib/CMakeLists.txt
src/dkim.c [new file with mode: 0644]
src/dkim.h [new file with mode: 0644]

index feeab47ca79a9acb88b96f4baefaefe605e21bbb..a0350e0125606a7a4de971bdf3ab59b6eec8d71b 100644 (file)
@@ -43,6 +43,7 @@ SET(RSPAMDLIBSRC
                                ../src/cfg_utils.c
                                ../src/cfg_xml.c
                                ../src/diff.c
+                               ../src/dkim.c
                                ../src/dns.c
                                ../src/events.c
                                ../src/expressions.c
diff --git a/src/dkim.c b/src/dkim.c
new file mode 100644 (file)
index 0000000..8a25a08
--- /dev/null
@@ -0,0 +1,536 @@
+/* Copyright (c) 2010-2011, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "main.h"
+#include "dkim.h"
+
+/* Parser of dkim params */
+typedef gboolean (*dkim_parse_param_f) (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err);
+
+static gboolean rspamd_dkim_parse_signature (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err);
+static gboolean rspamd_dkim_parse_signalg (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err);
+static gboolean rspamd_dkim_parse_domain (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err);
+static gboolean rspamd_dkim_parse_canonalg (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err);
+static gboolean rspamd_dkim_parse_ignore (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err);
+static gboolean rspamd_dkim_parse_selector (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err);
+static gboolean rspamd_dkim_parse_hdrlist (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err);
+static gboolean rspamd_dkim_parse_version (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err);
+static gboolean rspamd_dkim_parse_timestamp (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err);
+static gboolean rspamd_dkim_parse_expiration (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err);
+static gboolean rspamd_dkim_parse_bodyhash (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err);
+static gboolean rspamd_dkim_parse_bodylength (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err);
+
+
+static const dkim_parse_param_f parser_funcs[] = {
+       [DKIM_PARAM_SIGNATURE] = rspamd_dkim_parse_signature,
+       [DKIM_PARAM_SIGNALG] = rspamd_dkim_parse_signalg,
+       [DKIM_PARAM_DOMAIN] = rspamd_dkim_parse_domain,
+       [DKIM_PARAM_CANONALG] = rspamd_dkim_parse_canonalg,
+       [DKIM_PARAM_QUERYMETHOD] = rspamd_dkim_parse_ignore,
+       [DKIM_PARAM_SELECTOR] = rspamd_dkim_parse_selector,
+       [DKIM_PARAM_HDRLIST] = rspamd_dkim_parse_hdrlist,
+       [DKIM_PARAM_VERSION] = rspamd_dkim_parse_version,
+       [DKIM_PARAM_IDENTITY] = rspamd_dkim_parse_ignore,
+       [DKIM_PARAM_TIMESTAMP] = rspamd_dkim_parse_timestamp,
+       [DKIM_PARAM_EXPIRATION] = rspamd_dkim_parse_expiration,
+       [DKIM_PARAM_COPIEDHDRS] = rspamd_dkim_parse_ignore,
+       [DKIM_PARAM_BODYHASH] = rspamd_dkim_parse_bodyhash,
+       [DKIM_PARAM_BODYLENGTH] = rspamd_dkim_parse_bodylength
+};
+
+#define DKIM_ERROR dkim_error_quark ()
+GQuark
+dkim_error_quark (void)
+{
+       return g_quark_from_static_string ("dkim-error-quark");
+}
+
+/* Parsers implementation */
+static gboolean
+rspamd_dkim_parse_signature (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err)
+{
+       ctx->b = memory_pool_alloc (ctx->pool, len + 1);
+       rspamd_strlcpy (ctx->b, param, len + 1);
+       g_base64_decode_inplace (ctx->b, &len);
+       return TRUE;
+}
+
+static gboolean
+rspamd_dkim_parse_signalg (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err)
+{
+       if (len == 8) {
+               if (memcmp (param, "rsa-sha1", len) == 0) {
+                       ctx->sig_alg = DKIM_SIGN_RSASHA1;
+                       return TRUE;
+               }
+       }
+       else if (len == 10) {
+               if (memcmp (param, "rsa-sha256", len) == 0) {
+                       ctx->sig_alg = DKIM_SIGN_RSASHA256;
+                       return TRUE;
+               }
+       }
+
+       g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_INVALID_A, "invalid dkim sign algorithm");
+       return FALSE;
+}
+
+static gboolean
+rspamd_dkim_parse_domain (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err)
+{
+       ctx->domain = memory_pool_alloc (ctx->pool, len + 1);
+       rspamd_strlcpy (ctx->domain, param, len + 1);
+       return TRUE;
+}
+
+static gboolean
+rspamd_dkim_parse_canonalg (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err)
+{
+       const gchar                                             *p, *slash = NULL, *end = param + len;
+       gsize                                                    sl = 0;
+
+       p = param;
+       while (p != end) {
+               if (*p == '/') {
+                       slash = p;
+                       break;
+               }
+               p ++;
+               sl ++;
+       }
+
+       if (slash == NULL) {
+               /* Only check header */
+               if (len == 6 && memcmp (param, "simple", len) == 0) {
+                       ctx->header_canon_type = DKIM_CANON_SIMPLE;
+                       return TRUE;
+               }
+               else if (len == 7 && memcmp (param, "relaxed", len) == 0) {
+                       ctx->header_canon_type = DKIM_CANON_RELAXED;
+                       return TRUE;
+               }
+       }
+       else {
+               /* First check header */
+               if (sl == 6 && memcmp (param, "simple", len) == 0) {
+                       ctx->header_canon_type = DKIM_CANON_SIMPLE;
+               }
+               else if (sl == 7 && memcmp (param, "relaxed", len) == 0) {
+                       ctx->header_canon_type = DKIM_CANON_RELAXED;
+               }
+               else {
+                       goto err;
+               }
+               /* Check body */
+               len = len - sl - 1;
+               slash ++;
+               if (len == 6 && memcmp (slash, "simple", len) == 0) {
+                       ctx->body_canon_type = DKIM_CANON_SIMPLE;
+                       return TRUE;
+               }
+               else if (len == 7 && memcmp (slash, "relaxed", len) == 0) {
+                       ctx->body_canon_type = DKIM_CANON_RELAXED;
+                       return TRUE;
+               }
+       }
+
+err:
+       g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_INVALID_A, "invalid dkim sign algorithm");
+       return FALSE;
+}
+
+static gboolean
+rspamd_dkim_parse_ignore (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err)
+{
+       /* Just ignore unused params */
+       return TRUE;
+}
+
+static gboolean
+rspamd_dkim_parse_selector (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err)
+{
+       ctx->selector = memory_pool_alloc (ctx->pool, len + 1);
+       rspamd_strlcpy (ctx->selector, param, len + 1);
+       return TRUE;
+}
+
+static gboolean
+rspamd_dkim_parse_hdrlist (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err)
+{
+       const gchar                                             *c, *p, *end = param + len;
+       gchar                                                   *h;
+       gboolean                                                 from_found = FALSE;
+
+       c = param;
+       p = param;
+       while (p <= end) {
+               if ((*p == ':' || p == end) && p - c > 0) {
+                       /* Insert new header to the list */
+                       if (p == end) {
+                               h = memory_pool_alloc (ctx->pool, p - c + 1);
+                               rspamd_strlcpy (h, c, p - c + 1);
+                       }
+                       else {
+                               h = memory_pool_alloc (ctx->pool, p - c);
+                               rspamd_strlcpy (h, c, p - c);
+                       }
+                       /* Check mandatory from */
+                       if (!from_found && g_ascii_strcasecmp (h, "from") == 0) {
+                               from_found = TRUE;
+                       }
+                       ctx->hlist = g_list_prepend (ctx->hlist, h);
+                       c = p + 1;
+                       p ++;
+               }
+               else {
+                       p ++;
+               }
+       }
+
+       if (!ctx->hlist) {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_INVALID_H, "invalid dkim header list");
+               return FALSE;
+       }
+       else {
+               if (!from_found) {
+                       g_list_free (ctx->hlist);
+                       g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_INVALID_H, "invalid dkim header list, from header is missing");
+                       return FALSE;
+               }
+               /* Reverse list */
+               ctx->hlist = g_list_reverse (ctx->hlist);
+               memory_pool_add_destructor (ctx->pool, (pool_destruct_func)g_list_free, ctx->hlist);
+       }
+
+       return TRUE;
+}
+
+static gboolean
+rspamd_dkim_parse_version (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err)
+{
+       if (len != 1 || *param != '1') {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_VERSION, "invalid dkim version");
+               return FALSE;
+       }
+
+       ctx->ver = 1;
+       return TRUE;
+}
+
+static gboolean
+rspamd_dkim_parse_timestamp (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err)
+{
+       gulong                                                   val;
+
+       if (!rspamd_strtoul (param, len, &val)) {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_UNKNOWN, "invalid dkim timestamp");
+               return FALSE;
+       }
+       ctx->timestamp = val;
+
+       return TRUE;
+}
+
+static gboolean
+rspamd_dkim_parse_expiration (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err)
+{
+       gulong                                                   val;
+
+       if (!rspamd_strtoul (param, len, &val)) {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_UNKNOWN, "invalid dkim expiration");
+               return FALSE;
+       }
+       ctx->expiration = val;
+
+       return TRUE;
+}
+
+static gboolean
+rspamd_dkim_parse_bodyhash (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err)
+{
+       ctx->bh = memory_pool_alloc (ctx->pool, len + 1);
+       rspamd_strlcpy (ctx->bh, param, len + 1);
+       g_base64_decode_inplace (ctx->bh, &len);
+       return TRUE;
+}
+
+static gboolean
+rspamd_dkim_parse_bodylength (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err)
+{
+       gulong                                                   val;
+
+       if (!rspamd_strtoul (param, len, &val)) {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_INVALID_L, "invalid dkim body length");
+               return FALSE;
+       }
+       ctx->len = val;
+
+       return TRUE;
+}
+
+/**
+ * Create new dkim context from signature
+ * @param sig message's signature
+ * @param pool pool to allocate memory from
+ * @param err pointer to error object
+ * @return new context or NULL
+ */
+rspamd_dkim_context_t*
+rspamd_create_dkim_context (const gchar *sig, memory_pool_t *pool, GError **err)
+{
+       const gchar                                             *p, *c, *tag;
+       gsize                                                    taglen;
+       gint                                                     param = DKIM_PARAM_UNKNOWN;
+       time_t                                                   now;
+       rspamd_dkim_context_t                   *new;
+       enum {
+               DKIM_STATE_TAG = 0,
+               DKIM_STATE_AFTER_TAG,
+               DKIM_STATE_VALUE,
+               DKIM_STATE_SKIP_SPACES = 99,
+               DKIM_STATE_ERROR = 100
+       }                                                                state, next_state;
+
+
+       new = memory_pool_alloc0 (pool, sizeof (rspamd_dkim_context_t));
+       new->pool = pool;
+       new->header_canon_type = DKIM_CANON_DEFAULT;
+       new->body_canon_type = DKIM_CANON_DEFAULT;
+       new->sig_alg = DKIM_SIGN_UNKNOWN;
+       /* A simple state machine of parsing tags */
+       state = DKIM_STATE_SKIP_SPACES;
+       next_state = DKIM_STATE_TAG;
+       taglen = 0;
+       while (*p) {
+               switch (state) {
+               case DKIM_STATE_TAG:
+                       if (g_ascii_isspace (*p)) {
+                               taglen = p - c;
+                               while (*p && g_ascii_isspace (*p)) {
+                                       /* Skip spaces before '=' sign */
+                                       p ++;
+                               }
+                               if (*p != '=') {
+                                       g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_UNKNOWN, "invalid dkim param");
+                                       state = DKIM_STATE_ERROR;
+                               }
+                               else {
+                                       state = DKIM_STATE_SKIP_SPACES;
+                                       next_state = DKIM_STATE_AFTER_TAG;
+                                       param = DKIM_PARAM_UNKNOWN;
+                                       p ++;
+                                       tag = c;
+                               }
+                       }
+                       else if (*p == '=') {
+                               state = DKIM_STATE_SKIP_SPACES;
+                               next_state = DKIM_STATE_AFTER_TAG;
+                               param = DKIM_PARAM_UNKNOWN;
+                               p ++;
+                               tag = c;
+                       }
+                       else {
+                               p ++;
+                               taglen ++;
+                       }
+                       break;
+               case DKIM_STATE_AFTER_TAG:
+                       /* We got tag at tag and len at taglen */
+                       switch (taglen) {
+                       case 0:
+                               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_UNKNOWN, "zero length dkim param");
+                               state = DKIM_STATE_ERROR;
+                               break;
+                       case 1:
+                               /* Simple tags */
+                               switch (*tag) {
+                               case 'v':
+                                       param = DKIM_PARAM_VERSION;
+                                       break;
+                               case 'a':
+                                       param = DKIM_PARAM_SIGNALG;
+                                       break;
+                               case 'b':
+                                       param = DKIM_PARAM_SIGNATURE;
+                                       break;
+                               case 'c':
+                                       param = DKIM_PARAM_CANONALG;
+                                       break;
+                               case 'd':
+                                       param = DKIM_PARAM_DOMAIN;
+                                       break;
+                               case 'h':
+                                       param = DKIM_PARAM_HDRLIST;
+                                       break;
+                               case 'i':
+                                       param = DKIM_PARAM_IDENTITY;
+                                       break;
+                               case 'l':
+                                       param = DKIM_PARAM_BODYLENGTH;
+                                       break;
+                               case 'q':
+                                       param = DKIM_PARAM_QUERYMETHOD;
+                                       break;
+                               case 's':
+                                       param = DKIM_PARAM_SELECTOR;
+                                       break;
+                               case 't':
+                                       param = DKIM_PARAM_TIMESTAMP;
+                                       break;
+                               case 'x':
+                                       param = DKIM_PARAM_EXPIRATION;
+                                       break;
+                               case 'z':
+                                       param = DKIM_PARAM_COPIEDHDRS;
+                                       break;
+                               default:
+                                       g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_UNKNOWN, "invalid dkim param: %c", *tag);
+                                       state = DKIM_STATE_ERROR;
+                                       break;
+                               }
+                               break;
+                       case 2:
+                               if (tag[0] == 'b' && tag[1] == 'h') {
+                                       param = DKIM_PARAM_BODYHASH;
+                               }
+                               else {
+                                       g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_UNKNOWN, "invalid dkim param: %c%c", tag[0], tag[1]);
+                                       state = DKIM_STATE_ERROR;
+                               }
+                               break;
+                       default:
+                               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_UNKNOWN, "invalid dkim param lenght: %zd", taglen);
+                               state = DKIM_STATE_ERROR;
+                               break;
+                       }
+                       if (state != DKIM_STATE_ERROR) {
+                               /* Skip spaces */
+                               p ++;
+                               state = DKIM_STATE_SKIP_SPACES;
+                               next_state = DKIM_STATE_VALUE;
+                       }
+                       break;
+               case DKIM_STATE_VALUE:
+                       if (*p == ';') {
+                               if (param == DKIM_PARAM_UNKNOWN || !parser_funcs[param](new, c, p - c - 1, err)) {
+                                       state = DKIM_STATE_ERROR;
+                               }
+                       }
+                       else {
+                               p ++;
+                       }
+                       break;
+               case DKIM_STATE_SKIP_SPACES:
+                       if (g_ascii_isspace (*p)) {
+                               p ++;
+                       }
+                       else {
+                               c = p;
+                               state = next_state;
+                       }
+                       break;
+               case DKIM_STATE_ERROR:
+                       if (err) {
+                               msg_info ("dkim parse failed: %s", (*err)->message);
+                               return NULL;
+                       }
+                       else {
+                               msg_info ("dkim parse failed: unknown error");
+                               return NULL;
+                       }
+                       break;
+               }
+       }
+
+       /* Now check validity of signature */
+       if (new->b == NULL) {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EMPTY_B, "b parameter missing");
+               return NULL;
+       }
+       if (new->bh == NULL) {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EMPTY_BH, "bh parameter missing");
+               return NULL;
+       }
+       if (new->domain == NULL) {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EMPTY_D, "domain parameter missing");
+               return NULL;
+       }
+       if (new->selector == NULL) {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EMPTY_S, "selector parameter missing");
+               return NULL;
+       }
+       if (new->ver == 0) {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EMPTY_V, "v parameter missing");
+               return NULL;
+       }
+       if (new->hlist == NULL) {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EMPTY_H, "h parameter missing");
+               return NULL;
+       }
+       if (new->sig_alg == DKIM_SIGN_UNKNOWN) {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EMPTY_S, "s parameter missing");
+               return NULL;
+       }
+       /* Check expiration */
+       now = time (NULL);
+       if (new->timestamp && new->timestamp > now) {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_FUTURE, "signature was made in future, ignoring");
+               return NULL;
+       }
+       if (new->expiration && new->expiration < now) {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EXPIRED, "signature has expired");
+               return NULL;
+       }
+
+       return new;
+}
+
+/**
+ * Make DNS request for specified context and obtain and parse key
+ * @param ctx dkim context from signature
+ * @param resolver dns resolver object
+ * @param s async session to make request
+ * @return
+ */
+rspamd_dkim_key_t*
+rspamd_get_dkim_key (rspamd_dkim_context_t *ctx, struct rspamd_dns_resolver *resolver,
+               struct rspamd_async_session *s)
+{
+       /* TODO: add this parser as well */
+       return NULL;
+}
+
+/**
+ * Check task for dkim context using dkim key
+ * @param ctx dkim verify context
+ * @param key dkim key (from cache or from dns request)
+ * @param task task to check
+ * @return
+ */
+gint
+rspamd_dkim_check (rspamd_dkim_context_t *ctx, rspamd_dkim_key_t *key, struct worker_task *task)
+{
+       /* TODO: this check must be implemented */
+       return DKIM_CONTINUE;
+}
diff --git a/src/dkim.h b/src/dkim.h
new file mode 100644 (file)
index 0000000..a7ad514
--- /dev/null
@@ -0,0 +1,180 @@
+/* Copyright (c) 2010-2011, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#ifndef DKIM_H_
+#define DKIM_H_
+
+#include "config.h"
+#include "event.h"
+#include "dns.h"
+
+/* Main types and definitions */
+
+#define        DKIM_SIGNHEADER         "DKIM-Signature"
+                                       /* DKIM signature header */
+
+/* special DNS tokens */
+#define        DKIM_DNSKEYNAME         "_domainkey"
+                                       /* reserved DNS sub-zone */
+#define        DKIM_DNSPOLICYNAME      "_adsp" /* reserved DNS sub-zone */
+
+/* Canonization methods */
+#define DKIM_CANON_UNKNOWN     (-1)    /* unknown method */
+#define DKIM_CANON_SIMPLE      0       /* as specified in DKIM spec */
+#define DKIM_CANON_RELAXED     1       /* as specified in DKIM spec */
+
+#define DKIM_CANON_DEFAULT     DKIM_CANON_SIMPLE
+
+/* Signature methods */
+#define DKIM_SIGN_UNKNOWN      (-2)    /* unknown method */
+#define DKIM_SIGN_DEFAULT      (-1)    /* use internal default */
+#define DKIM_SIGN_RSASHA1      0       /* an RSA-signed SHA1 digest */
+#define DKIM_SIGN_RSASHA256    1       /* an RSA-signed SHA256 digest */
+
+/* Params */
+#define DKIM_PARAM_UNKNOWN     (-1)    /* unknown */
+#define DKIM_PARAM_SIGNATURE   0       /* b */
+#define DKIM_PARAM_SIGNALG     1       /* a */
+#define DKIM_PARAM_DOMAIN      2       /* d */
+#define DKIM_PARAM_CANONALG    3       /* c */
+#define DKIM_PARAM_QUERYMETHOD 4       /* q */
+#define DKIM_PARAM_SELECTOR    5       /* s */
+#define DKIM_PARAM_HDRLIST     6       /* h */
+#define DKIM_PARAM_VERSION     7       /* v */
+#define DKIM_PARAM_IDENTITY    8       /* i */
+#define DKIM_PARAM_TIMESTAMP   9       /* t */
+#define DKIM_PARAM_EXPIRATION  10      /* x */
+#define DKIM_PARAM_COPIEDHDRS  11      /* z */
+#define DKIM_PARAM_BODYHASH    12      /* bh */
+#define DKIM_PARAM_BODYLENGTH  13      /* l */
+
+/* Errors (from OpenDKIM) */
+
+#define DKIM_SIGERROR_UNKNOWN          (-1)    /* unknown error */
+#define DKIM_SIGERROR_OK               0       /* no error */
+#define DKIM_SIGERROR_VERSION          1       /* unsupported version */
+#define DKIM_SIGERROR_DOMAIN           2       /* invalid domain (d=/i=) */
+#define DKIM_SIGERROR_EXPIRED          3       /* signature expired */
+#define DKIM_SIGERROR_FUTURE           4       /* signature in the future */
+#define DKIM_SIGERROR_TIMESTAMPS       5       /* x= < t= */
+#define DKIM_SIGERROR_UNUSED           6       /* OBSOLETE */
+#define DKIM_SIGERROR_INVALID_HC       7       /* c= invalid (header) */
+#define DKIM_SIGERROR_INVALID_BC       8       /* c= invalid (body) */
+#define DKIM_SIGERROR_MISSING_A                9       /* a= missing */
+#define DKIM_SIGERROR_INVALID_A                10      /* a= invalid */
+#define DKIM_SIGERROR_MISSING_H                11      /* h= missing */
+#define DKIM_SIGERROR_INVALID_L                12      /* l= invalid */
+#define DKIM_SIGERROR_INVALID_Q                13      /* q= invalid */
+#define DKIM_SIGERROR_INVALID_QO       14      /* q= option invalid */
+#define DKIM_SIGERROR_MISSING_D                15      /* d= missing */
+#define DKIM_SIGERROR_EMPTY_D          16      /* d= empty */
+#define DKIM_SIGERROR_MISSING_S                17      /* s= missing */
+#define DKIM_SIGERROR_EMPTY_S          18      /* s= empty */
+#define DKIM_SIGERROR_MISSING_B                19      /* b= missing */
+#define DKIM_SIGERROR_EMPTY_B          20      /* b= empty */
+#define DKIM_SIGERROR_CORRUPT_B                21      /* b= corrupt */
+#define DKIM_SIGERROR_NOKEY            22      /* no key found in DNS */
+#define DKIM_SIGERROR_DNSSYNTAX                23      /* DNS reply corrupt */
+#define DKIM_SIGERROR_KEYFAIL          24      /* DNS query failed */
+#define DKIM_SIGERROR_MISSING_BH       25      /* bh= missing */
+#define DKIM_SIGERROR_EMPTY_BH         26      /* bh= empty */
+#define DKIM_SIGERROR_CORRUPT_BH       27      /* bh= corrupt */
+#define DKIM_SIGERROR_BADSIG           28      /* signature mismatch */
+#define DKIM_SIGERROR_SUBDOMAIN                29      /* unauthorized subdomain */
+#define DKIM_SIGERROR_MULTIREPLY       30      /* multiple records returned */
+#define DKIM_SIGERROR_EMPTY_H          31      /* h= empty */
+#define DKIM_SIGERROR_INVALID_H                32      /* h= missing req'd entries */
+#define DKIM_SIGERROR_TOOLARGE_L       33      /* l= value exceeds body size */
+#define DKIM_SIGERROR_MBSFAILED                34      /* "must be signed" failure */
+#define        DKIM_SIGERROR_KEYVERSION        35      /* unknown key version */
+#define        DKIM_SIGERROR_KEYUNKNOWNHASH    36      /* unknown key hash */
+#define        DKIM_SIGERROR_KEYHASHMISMATCH   37      /* sig-key hash mismatch */
+#define        DKIM_SIGERROR_NOTEMAILKEY       38      /* not an e-mail key */
+#define        DKIM_SIGERROR_UNUSED2           39      /* OBSOLETE */
+#define        DKIM_SIGERROR_KEYTYPEMISSING    40      /* key type missing */
+#define        DKIM_SIGERROR_KEYTYPEUNKNOWN    41      /* key type unknown */
+#define        DKIM_SIGERROR_KEYREVOKED        42      /* key revoked */
+#define        DKIM_SIGERROR_KEYDECODE         43      /* key couldn't be decoded */
+#define        DKIM_SIGERROR_MISSING_V         44      /* v= tag missing */
+#define        DKIM_SIGERROR_EMPTY_V           45      /* v= tag empty */
+
+/* Check results */
+#define        DKIM_CONTINUE   0       /* continue */
+#define        DKIM_REJECT     1       /* reject */
+#define        DKIM_TRYAGAIN   2       /* try again later */
+#define        DKIM_NOTFOUND   3       /* requested record not found */
+#define        DKIM_RECORD_ERROR       4       /* error requesting record */
+
+typedef struct rspamd_dkim_context_s {
+       memory_pool_t *pool;
+       gint sig_alg;
+       gint header_canon_type;
+       gint body_canon_type;
+       gsize len;
+       gchar *domain;
+       gchar *selector;
+       time_t timestamp;
+       time_t expiration;
+       gchar *b;
+       gchar *bh;
+       GList *hlist;
+       guint ver;
+} rspamd_dkim_context_t;
+
+typedef struct rspamd_dkim_key_s {
+       memory_pool_t *pool;
+       /* TODO: make this structure */
+} rspamd_dkim_key_t;
+
+struct worker_task;
+
+/**
+ * Create new dkim context from signature
+ * @param sig message's signature
+ * @param pool pool to allocate memory from
+ * @param err pointer to error object
+ * @return new context or NULL
+ */
+rspamd_dkim_context_t* rspamd_create_dkim_context (const gchar *sig, memory_pool_t *pool, GError **err);
+
+/**
+ * Make DNS request for specified context and obtain and parse key
+ * @param ctx dkim context from signature
+ * @param resolver dns resolver object
+ * @param s async session to make request
+ * @return
+ */
+rspamd_dkim_key_t* rspamd_get_dkim_key (rspamd_dkim_context_t *ctx, struct rspamd_dns_resolver *resolver,
+               struct rspamd_async_session *s);
+
+/**
+ * Check task for dkim context using dkim key
+ * @param ctx dkim verify context
+ * @param key dkim key (from cache or from dns request)
+ * @param task task to check
+ * @return
+ */
+gint rspamd_dkim_check (rspamd_dkim_context_t *ctx, rspamd_dkim_key_t *key, struct worker_task *task);
+
+#endif /* DKIM_H_ */