SPF verification support is built into Exim if SUPPORT_SPF=yes is set in
&_Local/Makefile_&. The support uses the &_libspf2_& library
&url(https://www.libspf2.org/).
+.new
+.cindex "dynamic modules"
+The support can be built as a dynamic-load module if desired;
+see the comments in that Makefile.
+.wen
+
There is no Exim involvement in the transmission of messages;
publishing certain DNS records is all that is required.
3. Events smtp:fail:protocol and smtp:fail:syntax
- 4. JSON and LDAP lookup support, all the router and authenticator drivers,
- and all the transport drivers except smtp, can now be built as loadable
- modules
+ 4. JSON and LDAP lookup support, SPF support, all the router and authenticator
+ drivers, and all the transport drivers except smtp, can now be built as
+ loadable modules
Version 4.98
------------
.PHONY: all config utils \
buildauths buildlookups buildpdkim buildrouters \
- buildtransports dynmodules checklocalmake clean
+ buildtransports buildmisc dynmodules checklocalmake clean
utils: $(EXIM_MONITOR) exicyclog exinext exiwhat \
dcc.o \
dmarc.o \
imap_utf7.o \
- spf.o \
utf8.o \
xclient.o
$(OBJ_EXPERIMENTAL)
exim: buildlookups buildauths pdkim/pdkim.a \
- buildrouters buildtransports \
+ buildrouters buildtransports buildmisc \
$(OBJ_EXIM) version.o
@echo "$(LNCC) -o exim"
$(FE)$(PURIFY) $(LNCC) -o exim $(LFLAGS) $(OBJ_EXIM) version.o \
routers/routers.a transports/transports.a lookups/lookups.a \
- auths/auths.a pdkim/pdkim.a \
+ auths/auths.a pdkim/pdkim.a miscmods/miscmods.a \
$(LIBRESOLV) $(LIBS) $(LIBS_EXIM) $(IPV6_LIBS) $(EXTRALIBS) \
$(EXTRALIBS_EXIM) $(DBMLIB) $(LOOKUP_LIBS) $(AUTH_LIBS) \
$(PERL_LIBS) $(TLS_LIBS) $(PCRE_LIBS) $(LDFLAGS)
dcc.o: $(HDRS) dcc.h dcc.c
dmarc.o: $(HDRS) pdkim/pdkim.h dmarc.h dmarc.c
imap_utf7.o: $(HDRS) imap_utf7.c
-spf.o: $(HDRS) spf.h spf.c
utf8.o: $(HDRS) utf8.c
xclient.o: $(HDRS) xclient.c
# Copies of modules built as dynamic-load libraries
-dynmodules: buildlookups buildrouters buildtransports buildauths
+dynmodules: buildlookups buildrouters buildtransports buildauths \
+ buildmisc
rm -fr dynmodules
mkdir dynmodules
- for d in lookup router transport auth; do \
+ for d in lookup router transport auth miscmod; do \
for f in $${d}s/*.so; do \
[ -e $$f ] && ln $$f dynmodules/`basename $$f .so`_$$d.so; \
done; \
INCLUDE="$(INCLUDE) $(IPV6_INCLUDE) $(TLS_INCLUDE)"
@echo " "
+buildmisc: config
+ @cd miscmods && $(MAKE) SHELL=$(SHELL) AR="$(AR)" $(MFLAGS) \
+ CC="$(CC)" CFLAGS="$(CFLAGS)" \
+ CFLAGS_DYNAMIC="$(CFLAGS_DYNAMIC)" HDRS="../version.h $(PHDRS)" \
+ FE="$(FE)" RANLIB="$(RANLIB)" RM_COMMAND="$(RM_COMMAND)" \
+ INCLUDE="$(INCLUDE) $(IPV6_INCLUDE)"
+ @echo " "
+
# The "clean", "install", and "makefile" targets just pass themselves back to
# the main Exim makefile. These targets will be obeyed only if "make" is obeyed
# for them in the build directory.
routers ROUTER ACCEPT DNSLOOKUP IPLITERAL IPLOOKUP MANUALROUTE QUERYPROGRAM REDIRECT
transports TRANSPORT APPENDFILE AUTOREPLY LMTP PIPE QUEUEFILE SMTP
auths AUTH CRAM_MD5 CYRUS_SASL DOVECOT EXTERNAL GSASL HEIMDAL_GSSAPI PLAINTEXT SPA TLS
+ miscmods SUPPORT SPF
END
# See if there is a definition of EXIM_PERL in what we have built so far.
done
cd ..
+# miscellaneous modules
+d="miscmods"
+mkdir $d
+cd $d
+# Makefile is generated
+for f in spf.c spf.h
+do
+ ln -s ../../src/$d/$f $f
+done
+cd ..
+
# and the hintsdb implementations
d="hintsdb"
mkdir $d
emit_module_rule $name_mod
done
-# Because the variable is EXPERIMENTAL_SPF and not LOOKUP_SPF we
-# always include spf.o and compile a dummy if EXPERIMENTAL_SPF is not
+# Because the variable is SUPPORT_SPF and not LOOKUP_SPF we
+# always include spf.o and compile a dummy if SUPPORT_SPF is not
# defined.
OBJ="${OBJ} spf.o"
# is historic).
# You need to add -export-dynamic -rdynamic to EXTRALIBS. You may also need to
# add -ldl to EXTRALIBS so that dlopen() is available to Exim. You need to
-# define CFLAGS_DYNAIC and LOOKUP_MODULE_DIR below so the builds are done right,
-# and so the exim binary actually loads dynamic lookup modules.
+# define CFLAGS_DYNAMIC and LOOKUP_MODULE_DIR below so the builds are done
+# right and so the exim binary actually loads dynamic lookup modules.
#
# Libraries being built as modules should be added to respective
# LOOKUP_*_INCLUDE and LOOKUP_*_LIBS rather than the the ones for the
# Uncomment the following lines to add SPF support. You need to have libspf2
# installed on your system (www.libspf2.org). Depending on where it is installed
# you may have to edit the CFLAGS and LDFLAGS lines.
+#
+# If set to "2" instead of "yes" then the support will be
+# built as a module and must be installed into LOOKUP_MODULE_DIR (the name
+# is historic). The same rules as for other module builds apply; use
+# SUPPORT_SPF_{INCLUDE,LIBS}.
# SUPPORT_SPF=yes
# CFLAGS += -I/usr/local/include
typedef struct condition_def {
uschar *name;
+ /* Flags for actions or checks to do during readconf for this condition */
unsigned flags;
#define ACD_EXP BIT(0) /* do expansion at outer level*/
#define ACD_MOD BIT(1) /* is a modifier */
+#define ACD_LOAD BIT(2) /* supported by a dynamic-load module */
-/* Bit map vector of which conditions and modifiers are not allowed at certain
-times. For each condition and modifier, there's a bitmap of dis-allowed times.
-For some, it is easier to specify the negation of a small number of allowed
-times. */
+ /* Bit map vector of which conditions and modifiers are not allowed at certain
+ times. For each condition and modifier, there's a bitmap of dis-allowed times.
+ For some, it is easier to specify the negation of a small number of allowed
+ times. */
unsigned forbids;
#define FORBIDDEN(times) (times)
#define PERMITTED(times) ((unsigned) ~(times))
},
#endif
#ifdef SUPPORT_SPF
- [ACLC_SPF] = { US"spf", ACD_EXP,
+ [ACLC_SPF] = { US"spf",
+# if SUPPORT_SPF==2
+ ACD_LOAD |
+# endif
+ ACD_EXP,
FORBIDDEN(ACL_BIT_AUTH | ACL_BIT_CONNECT |
ACL_BIT_HELO | ACL_BIT_MAILAUTH |
ACL_BIT_ETRN | ACL_BIT_EXPN |
ACL_BIT_STARTTLS | ACL_BIT_VRFY |
ACL_BIT_NOTSMTP | ACL_BIT_NOTSMTP_START),
},
- [ACLC_SPF_GUESS] = { US"spf_guess", ACD_EXP,
+ [ACLC_SPF_GUESS] = { US"spf_guess",
+# if SUPPORT_SPF==2
+ ACD_LOAD |
+# endif
+ ACD_EXP,
FORBIDDEN(ACL_BIT_AUTH | ACL_BIT_CONNECT |
ACL_BIT_HELO | ACL_BIT_MAILAUTH |
ACL_BIT_ETRN | ACL_BIT_EXPN |
#ifndef MACRO_PREDEF
+# ifdef LOOKUP_MODULE_DIR
+typedef struct condition_module {
+ const uschar * mod_name; /* module for the givien conditions */
+ misc_module_info * info; /* NULL when not loaded */
+ const int * conditions; /* array of ACLC_*, -1 terminated */
+} condition_module;
+
+# if SUPPORT_SPF==2
+static int spf_condx[] = { ACLC_SPF, ACLC_SPF_GUESS, -1 };
+# endif
+
+static condition_module condition_modules[] = {
+# if SUPPORT_SPF==2
+ {.mod_name = US"spf", .conditions = spf_condx},
+# endif
+};
+
+# endif
+
/* Return values from decode_control() */
enum {
/* The modifiers may not be negated */
- if (negated && conditions[c].flags & ACD_MOD )
+ if (negated && conditions[c].flags & ACD_MOD)
{
*error = string_sprintf("ACL error: negation is not allowed with "
"\"%s\"", conditions[c].name);
return NULL;
}
+#ifdef LOOKUP_MODULE_DIR
+ if (conditions[c].flags & ACD_LOAD)
+ { /* a loadable module supports this condition */
+ condition_module * cm;
+ uschar * s = NULL;
+
+ for (cm = condition_modules;
+ cm < condition_modules + nelem(condition_modules); cm++)
+ for (const int * cond = cm->conditions; *cond != -1; cond++)
+ if (*cond == c) goto found;
+ found:
+
+ if (cm >= condition_modules + nelem(condition_modules))
+ { /* shouldn't happen */
+ *error = string_sprintf("ACL error: failed to locate support for '%s'",
+ conditions[c].name);
+ return NULL;
+ }
+ if ( !cm->info /* module not loaded */
+ && !(cm->info = misc_mod_find(cm->mod_name, &s)))
+ {
+ *error = string_sprintf("ACL error: failed to find module for '%s': %s",
+ conditions[c].name, s);
+ return NULL;
+ }
+ }
+#endif
+
cond = store_get(sizeof(acl_condition_block), GET_UNTAINTED);
cond->next = NULL;
cond->type = c;
#ifdef SUPPORT_SPF
case ACLC_SPF:
- rc = spf_process(&arg, sender_address, SPF_PROCESS_NORMAL);
- break;
-
case ACLC_SPF_GUESS:
- rc = spf_process(&arg, sender_address, SPF_PROCESS_GUESS);
+ /* Hardwire the offset of the function in the module functions table
+ for now. Work out a more general mech later. */
+ {
+ misc_module_info * mi = misc_mod_find(US"spf", &log_message);
+ typedef int (*fn_t)(const uschar **, const uschar *, int);
+ fn_t fn;
+
+ if (!mi)
+ { rc = DEFER; break; } /* shouldn't happen */
+
+ fn = ((fn_t *) mi->functions)[1];
+
+ rc = fn(&arg, sender_address,
+ cb->type == ACLC_SPF ? SPF_PROCESS_NORMAL : SPF_PROCESS_GUESS);
break;
+ }
#endif
case ACLC_UDPSEND:
#ifdef WITH_CONTENT_SCAN
malware_init();
#endif
-#ifdef SUPPORT_SPF
-spf_init();
+#ifdef SUPPORT_DMARC
+dmarc_init();
#endif
#ifndef DISABLE_TLS
tls_daemon_init();
BOOL dmarc_abort = FALSE;
uschar *dmarc_pass_fail = US"skipped";
header_line *from_header = NULL;
-extern SPF_response_t *spf_response;
+
+misc_module_info * spf_mod_info;
+SPF_response_t *spf_response_p;
int dmarc_spf_ares_result = 0;
uschar *spf_sender_domain = NULL;
uschar *spf_human_readable = NULL;
};
+int
+dmarc_init(void)
+{
+uschar * errstr;
+if (!(spf_mod_info = misc_mod_find(US"spf", &errstr)))
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "dmarc: failed to find SPF module: %s", errstr);
+return TRUE;
+}
+
gstring *
dmarc_version_report(gstring * g)
{
return eblock;
}
-/* dmarc_init sets up a context that can be re-used for several
+/* dmarc_conn_init sets up a context that can be re-used for several
messages on the same SMTP connection (that come from the
same host with the same HELO string) */
int
-dmarc_init(void)
+dmarc_conn_init(void)
{
int *netmask = NULL; /* Ignored */
int is_ipv6 = 0;
dmarc_used_domain = US"";
f.dmarc_has_been_checked = FALSE;
header_from_sender = NULL;
+spf_response_p = NULL;
spf_sender_domain = NULL;
spf_human_readable = NULL;
/* ACLs have "control=dmarc_disable_verify" */
-if (f.dmarc_disable_verify == TRUE)
+if (f.dmarc_disable_verify)
return OK;
(void) memset(&dmarc_ctx, '\0', sizeof dmarc_ctx);
message_id, primary_hostname, time(NULL), sender_host_address,
header_from_sender, expand_string(US"$sender_address_domain"));
-if (spf_response)
+if (spf_response_p)
g = string_fmt_append(g, "spf %d\n", dmarc_spf_ares_result);
if (dkim_history_buffer)
/* Use the envelope sender domain for this part of DMARC */
spf_sender_domain = expand_string(US"$sender_address_domain");
- if (!spf_response)
+
+ {
+ misc_module_info * mi = misc_mod_findonly(US"spf");
+ typedef SPF_response_t * (*fn_t)(void);
+ if (mi)
+ spf_response_p = ((fn_t *) mi->functions)[3](); /* spf_get_response */
+ }
+
+ if (!spf_response_p)
{
/* No spf data means null envelope sender so generate a domain name
from the sender_helo_name */
}
else
{
- sr = spf_response->result;
+ sr = spf_response_p->result;
dmarc_spf_result = sr == SPF_RESULT_NEUTRAL ? DMARC_POLICY_SPF_OUTCOME_NONE :
sr == SPF_RESULT_PASS ? DMARC_POLICY_SPF_OUTCOME_PASS :
sr == SPF_RESULT_FAIL ? DMARC_POLICY_SPF_OUTCOME_FAIL :
sr == SPF_RESULT_PERMERROR ? ARES_RESULT_PERMERROR :
ARES_RESULT_UNKNOWN;
origin = DMARC_POLICY_SPF_ORIGIN_MAILFROM;
- spf_human_readable = US spf_response->header_comment;
+ spf_human_readable = US spf_response_p->header_comment;
DEBUG(D_receive)
debug_printf_indent("DMARC using SPF sender domain = %s\n", spf_sender_domain);
}
/* prototypes */
gstring * dmarc_version_report(gstring *);
int dmarc_init(void);
+int dmarc_conn_init(void);
int dmarc_store_data(header_line *);
int dmarc_process(void);
uschar *dmarc_exim_expand_query(int);
extern lookup_module_info lmdb_lookup_module_info;
#endif
#if defined(SUPPORT_SPF)
-extern lookup_module_info spf_lookup_module_info;
+extern lookup_module_info spf_lookup_module_info; /* see below */
#endif
#if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2
extern lookup_module_info sqlite_lookup_module_info;
#ifdef LOOKUP_MODULE_DIR
+static void *
+mod_open(const uschar * name, const uschar * class, uschar ** errstr)
+{
+const uschar * path = string_sprintf(
+ LOOKUP_MODULE_DIR "/%s_%s." DYNLIB_FN_EXT, name, class);
+void * dl;
+if (!(dl = dlopen(CS path, RTLD_NOW)))
+ {
+ if (errstr)
+ *errstr = string_sprintf("Error loading %s: %s", name, dlerror());
+ else
+ (void) dlerror(); /* clear out error state */
+ return NULL;
+ }
+
+/* FreeBSD nsdispatch() can trigger dlerror() errors about
+_nss_cache_cycle_prevention_function; we need to clear the dlerror()
+state before calling dlsym(), so that any error afterwards only comes
+from dlsym(). */
+
+(void) dlerror();
+return dl;
+}
+
+
/* Try to load a lookup module with the given name.
Arguments:
static BOOL
lookup_mod_load(const uschar * name, uschar ** errstr)
{
-const uschar * path = string_sprintf(
- LOOKUP_MODULE_DIR "/%s_lookup." DYNLIB_FN_EXT, name);
void * dl;
struct lookup_module_info * info;
const char * errormsg;
-if (!(dl = dlopen(CS path, RTLD_NOW)))
- {
- if (errstr)
- *errstr = string_sprintf("Error loading %s: %s", name, dlerror());
- else
- (void) dlerror(); /* clear out error state */
+if (!(dl = mod_open(name, US"lookup", errstr)))
return FALSE;
- }
-
-/* FreeBSD nsdispatch() can trigger dlerror() errors about
-_nss_cache_cycle_prevention_function; we need to clear the dlerror()
-state before calling dlsym(), so that any error afterwards only comes
-from dlsym(). */
-
-errormsg = dlerror();
info = (struct lookup_module_info *) dlsym(dl, "_lookup_module_info");
if ((errormsg = dlerror()))
+misc_module_info * misc_module_list = NULL;
+
+static void
+misc_mod_add(misc_module_info * mi)
+{
+if (mi->init) mi->init(mi);
+DEBUG(D_lookup) if (mi->lib_vers_report)
+ debug_printf_indent("%Y\n", mi->lib_vers_report(NULL));
+
+mi->next = misc_module_list;
+misc_module_list = mi;
+}
+
+
+#ifdef LOOKUP_MODULE_DIR
+
+/* Load a "misc" module, and add to list */
+
+static misc_module_info *
+misc_mod_load(const uschar * name, uschar ** errstr)
+{
+void * dl;
+struct misc_module_info * mi;
+const char * errormsg;
+
+DEBUG(D_any) debug_printf_indent("loading module '%s'\n", name);
+if (!(dl = mod_open(name, US"miscmod", errstr)))
+ return NULL;
+
+mi = (struct misc_module_info *) dlsym(dl,
+ CS string_sprintf("%s_module_info", name));
+if ((errormsg = dlerror()))
+ {
+ fprintf(stderr, "%s does not appear to be an spf module (%s)\n", name, errormsg);
+ log_write(0, LOG_MAIN|LOG_PANIC, "%s does not appear to be an spf module (%s)", name, errormsg);
+ dlclose(dl);
+ return NULL;
+ }
+if (mi->dyn_magic != MISC_MODULE_MAGIC)
+ {
+ fprintf(stderr, "Module %s is not compatible with this version of Exim\n", name);
+ log_write(0, LOG_MAIN|LOG_PANIC, "Module %s is not compatible with this version of Exim", name);
+ dlclose(dl);
+ return FALSE;
+ }
+
+DEBUG(D_lookup) debug_printf_indent("Loaded \"%s\"\n", name);
+misc_mod_add(mi);
+return mi;
+}
+
+#endif /*LOOKUP_MODULE_DIR*/
+
+
+/* Find a "misc" module by name, if loaded.
+For now use a linear search down a linked list. If the number of
+modules gets large, we might consider a tree.
+*/
+
+misc_module_info *
+misc_mod_findonly(const uschar * name)
+{
+for (misc_module_info * mi = misc_module_list; mi; mi = mi->next)
+ if (Ustrcmp(name, mi->name) == 0)
+ return mi;
+}
+
+/* Find a "misc" module, possibly already loaded, by name. */
+
+misc_module_info *
+misc_mod_find(const uschar * name, uschar ** errstr)
+{
+misc_module_info * mi;
+if ((mi = misc_mod_findonly(name))) return mi;
+#ifdef LOOKUP_MODULE_DIR
+return misc_mod_load(name, errstr);
+#else
+return NULL;
+#endif /*LOOKUP_MODULE_DIR*/
+}
+
+
+
void
addlookupmodule(&lmdb_lookup_module_info);
#endif
-#ifdef SUPPORT_SPF
-addlookupmodule(&spf_lookup_module_info);
-#endif
-
#if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2
addlookupmodule(&sqlite_lookup_module_info);
#endif
addlookupmodule(&whoson_lookup_module_info);
#endif
+/* This is provided by the spf "misc" module, and the lookup aspect is always
+linked statically whether or not the "misc" module (and hence libspf2) is
+dynamic-load. */
+
+#if defined(SUPPORT_SPF)
+addlookupmodule(&spf_lookup_module_info);
+#endif
+
/* This is a custom expansion, and not available as either
a list-syntax lookup or a lookup expansion. However, it is
implemented by a lookup module. */
DEBUG(D_lookup) debug_printf("Loaded %d lookup modules\n", countmodules);
#endif
+}
+#if defined(SUPPORT_SPF) && SUPPORT_SPF!=2
+extern misc_module_info spf_module_info;
+#endif
+
+void
+init_misc_mod_list(void)
+{
+static BOOL onetime = FALSE;
+if (onetime) return;
+#if defined(SUPPORT_SPF) && SUPPORT_SPF!=2
+misc_mod_add(&spf_module_info);
+#endif
+onetime = TRUE;
}
#endif
extern void init_lookup_list(void);
+extern void init_misc_mod_list(void);
d = string_cat(d, US" redis");
# endif
#endif
+#ifdef SUPPORT_SPF
+# if SUPPORT_SPF!=2
+ b = string_cat(b, US" spf");
+# else
+ d = string_cat(d, US" spf");
+# endif
+#endif
#ifdef LOOKUP_SQLITE
# if LOOKUP_SQLITE!=2
b = string_cat(b, US" sqlite");
#ifdef SUPPORT_DMARC
g = dmarc_version_report(g);
#endif
-#ifdef SUPPORT_SPF
- g = spf_lib_version_report(g);
-#endif
show_string(is_stdout, g);
g = NULL;
tree_walk(lookups_tree, lookup_version_report_cb, &g);
show_string(is_stdout, g);
g = NULL;
+ init_misc_mod_list();
#ifdef WHITELIST_D_MACROS
g = string_fmt_append(g, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS);
This needs to happen before we read the main configuration. */
init_lookup_list();
+init_misc_mod_list();
/*XXX this excrescence could move to the testsuite standard config setup file */
#ifdef SUPPORT_I18N
# include "bmi_spam.h"
#endif
#ifdef SUPPORT_SPF
-# include "spf.h"
+# include "miscmods/spf.h"
#endif
#ifndef DISABLE_DKIM
# include "dkim.h"
};
-/* Types of table entry */
-
-enum vtypes {
- vtype_int, /* value is address of int */
- vtype_filter_int, /* ditto, but recognized only when filtering */
- vtype_ino, /* value is address of ino_t (not always an int) */
- vtype_uid, /* value is address of uid_t (not always an int) */
- vtype_gid, /* value is address of gid_t (not always an int) */
- vtype_bool, /* value is address of bool */
- vtype_stringptr, /* value is address of pointer to string */
- vtype_msgbody, /* as stringptr, but read when first required */
- vtype_msgbody_end, /* ditto, the end of the message */
- vtype_msgheaders, /* the message's headers, processed */
- vtype_msgheaders_raw, /* the message's headers, unprocessed */
- vtype_localpart, /* extract local part from string */
- vtype_domain, /* extract domain from string */
- vtype_string_func, /* value is string returned by given function */
- vtype_todbsdin, /* value not used; generate BSD inbox tod */
- vtype_tode, /* value not used; generate tod in epoch format */
- vtype_todel, /* value not used; generate tod in epoch/usec format */
- vtype_todf, /* value not used; generate full tod */
- vtype_todl, /* value not used; generate log tod */
- vtype_todlf, /* value not used; generate log file datestamp tod */
- vtype_todzone, /* value not used; generate time zone only */
- vtype_todzulu, /* value not used; generate zulu tod */
- vtype_reply, /* value not used; get reply from headers */
- vtype_pid, /* value not used; result is pid */
- vtype_host_lookup, /* value not used; get host name */
- vtype_load_avg, /* value not used; result is int from os_getloadavg */
- vtype_pspace, /* partition space; value is T/F for spool/log */
- vtype_pinodes, /* partition inodes; value is T/F for spool/log */
- vtype_cert /* SSL certificate */
-#ifndef DISABLE_DKIM
- ,vtype_dkim /* Lookup of value in DKIM signature */
-#endif
-};
-
-/* Type for main variable table */
-
-typedef struct {
- const char *name;
- enum vtypes type;
- void *value;
-} var_entry;
-
/* Type for entries pointing to address/length pairs. Not currently
in use. */
{ "spam_score_int", vtype_stringptr, &spam_score_int },
#endif
#ifdef SUPPORT_SPF
- { "spf_guess", vtype_stringptr, &spf_guess },
- { "spf_header_comment", vtype_stringptr, &spf_header_comment },
- { "spf_received", vtype_stringptr, &spf_received },
- { "spf_result", vtype_stringptr, &spf_result },
- { "spf_result_guessed", vtype_bool, &spf_result_guessed },
- { "spf_smtp_comment", vtype_stringptr, &spf_smtp_comment },
+ { "spf_guess", vtype_module, US"spf" },
+ { "spf_header_comment", vtype_module, US"spf" },
+ { "spf_received", vtype_module, US"spf" },
+ { "spf_result", vtype_module, US"spf" },
+ { "spf_result_guessed", vtype_module, US"spf" },
+ { "spf_smtp_comment", vtype_module, US"spf" },
#endif
{ "spool_directory", vtype_stringptr, &spool_directory },
{ "spool_inodes", vtype_pinodes, (void *)TRUE },
static var_entry *
-find_var_ent(uschar * name)
+find_var_ent(uschar * name, var_entry * table, unsigned nent)
{
int first = 0;
-int last = nelem(var_table);
+int last = nent;
while (last > first)
{
int middle = (first + last)/2;
- int c = Ustrcmp(name, var_table[middle].name);
+ int c = Ustrcmp(name, table[middle].name);
if (c > 0) { first = middle + 1; continue; }
if (c < 0) { last = middle; continue; }
- return &var_table[middle];
+ return &table[middle];
}
return NULL;
}
{
var_entry * vp;
-if (!(vp = find_var_ent(certvar)))
+if (!(vp = find_var_ent(certvar, var_table, nelem(var_table))))
{
expand_string_message =
string_sprintf("no variable named \"%s\"", certvar);
find_variable(uschar * name, esi_flags flags, int * newsize)
{
var_entry * vp;
-uschar *s, *domain;
-uschar **ss;
+uschar * s, * domain;
+uschar ** ss;
void * val;
+var_entry * table = var_table;
+unsigned table_count = nelem(var_table);
/* Handle ACL variables, whose names are of the form acl_cxxx or acl_mxxx.
Originally, xxx had to be a number in the range 0-9 (later 0-19), but from
}
#endif
+sublist:
+
/* For all other variables, search the table */
-if (!(vp = find_var_ent(name)))
+if (!(vp = find_var_ent(name, table, table_count)))
return NULL; /* Unknown variable name */
/* Found an existing variable. If in skipping state, the value isn't needed,
return dkim_exim_expand_query((int)(long)val);
#endif
+ case vtype_module:
+ {
+ uschar * errstr;
+ misc_module_info * mi = misc_mod_find(val, &errstr);
+ if (mi)
+ {
+ table = mi->variables;
+ table_count = mi->variables_count;
+ goto sublist;
+ }
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "failed to find %s module for %s: %s", US val, name, errstr);
+ return US"";
+ }
}
return NULL; /* Unknown variable. Silences static checkers. */
modify_variable(uschar *name, void * value)
{
var_entry * vp;
-if ((vp = find_var_ent(name))) vp->value = value;
+if ((vp = find_var_ent(name, var_table, nelem(var_table))))
+ vp->value = value;
return; /* Unknown variable name, fail silently */
}
yield = authres_iprev(yield);
yield = authres_smtpauth(yield);
#ifdef SUPPORT_SPF
- yield = authres_spf(yield);
+ {
+ misc_module_info * mi = misc_mod_findonly(US"spf");
+ if (mi)
+ {
+ typedef gstring * (*fn_t)(gstring *);
+ fn_t fn = ((fn_t *) mi->functions)[2]; /* authres_spf */
+ yield = fn(yield);
+ }
+ }
#endif
#ifndef DISABLE_DKIM
yield = authres_dkim(yield);
string_sprintf("missing '}' closing cert arg of %s", name);
goto EXPAND_FAILED_CURLY;
}
- if ((vp = find_var_ent(sub)) && vp->type == vtype_cert)
+ if ( (vp = find_var_ent(sub, var_table, nelem(var_table)))
+ && vp->type == vtype_cert)
{
s = s1+1;
break;
extern gstring *authres_dmarc(gstring *);
#endif
extern gstring *authres_smtpauth(gstring *);
-#ifdef SUPPORT_SPF
-extern gstring *authres_spf(gstring *);
-#endif
extern uschar *b64encode(const uschar *, int);
extern uschar *b64encode_taint(const uschar *, int, const void *);
extern int mime_regex(const uschar **, BOOL);
extern void mime_set_anomaly(int);
#endif
+extern misc_module_info * misc_mod_find(const uschar * modname, uschar **);
+extern misc_module_info * misc_mod_findonly(const uschar * modname);
extern uschar *moan_check_errorcopy(const uschar *);
extern BOOL moan_skipped_syntax_errors(uschar *, error_block *, uschar *,
BOOL, uschar *);
BOOL smtp_etrn_serialize = TRUE;
BOOL smtp_input = FALSE;
BOOL smtp_return_error_details = FALSE;
-#ifdef SUPPORT_SPF
-BOOL spf_result_guessed = FALSE;
-#endif
BOOL split_spool_directory = FALSE;
BOOL spool_wireformat = FALSE;
BOOL strict_acl_vars = FALSE;
uschar *spam_score = NULL;
uschar *spam_score_int = NULL;
#endif
-#ifdef SUPPORT_SPF
-uschar *spf_guess = US"v=spf1 a/24 mx/24 ptr ?all";
-uschar *spf_header_comment = NULL;
-uschar *spf_received = NULL;
-uschar *spf_result = NULL;
-uschar *spf_smtp_comment = NULL;
-uschar *spf_smtp_comment_template
- /* Used to be: "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}" */
- = US"Please%_see%_http://www.open-spf.org/Why";
-
-#endif
FILE *spool_data_file = NULL;
uschar *spool_directory = US SPOOL_DIRECTORY
extern uschar *spam_score; /* the spam score (float) */
extern uschar *spam_score_int; /* spam_score * 10 (int) */
#endif
-#ifdef SUPPORT_SPF
-extern uschar *spf_guess; /* spf best-guess record */
-extern uschar *spf_header_comment; /* spf header comment */
-extern uschar *spf_received; /* Received-SPF: header */
-extern uschar *spf_result; /* spf result in string form */
-extern BOOL spf_result_guessed; /* spf result is of best-guess operation */
-extern uschar *spf_smtp_comment; /* spf comment to include in SMTP reply */
-extern uschar *spf_smtp_comment_template;
- /* template to construct the spf comment by libspf2 */
-#endif
extern BOOL split_spool_directory; /* TRUE to use multiple subdirs */
extern FILE *spool_data_file; /* handle for -D file */
extern uschar *spool_directory; /* Name of spool directory */
# MAGIC-TAG-MODS-OBJ-RULES-GO-HERE
-OBJ += spf.o
-
all: lookups.a $(MODS)
lookups.a: $(OBJ)
#include <spf2/spf_dns_resolv.h>
#include <spf2/spf_dns_cache.h>
-extern SPF_dns_server_t * SPF_dns_exim_new(int);
-
static void *
spf_open(const uschar * filename, uschar ** errmsg)
{
-SPF_dns_server_t * dc;
-SPF_server_t *spf_server = NULL;
-int debug = 0;
-
-DEBUG(D_lookup) debug = 1;
-
-if ((dc = SPF_dns_exim_new(debug)))
- if ((dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
- spf_server = SPF_server_new_dns(dc, debug);
-
-if (!spf_server)
+misc_module_info * mi = misc_mod_find(US"spf", errmsg);
+if (mi)
{
- *errmsg = US"SPF_dns_exim_nnew() failed";
- return NULL;
+ typedef void * (*fn_t)(const uschar *, uschar **);
+ return (((fn_t *) mi->functions)[5]) (filename, errmsg);
}
-return (void *) spf_server;
+return NULL;
}
static void
-spf_close(void *handle)
+spf_close(void * handle)
{
-SPF_server_t *spf_server = handle;
-if (spf_server) SPF_server_free(spf_server);
+misc_module_info * mi = misc_mod_find(US"spf", NULL);
+if (mi)
+ {
+ typedef void (*fn_t)(void *);
+ return (((fn_t *) mi->functions)[6]) (handle);
+ }
}
+
static int
spf_find(void * handle, const uschar * filename, const uschar * keystring,
int key_len, uschar ** result, uschar ** errmsg, uint * do_cache,
const uschar * opts)
{
-SPF_server_t *spf_server = handle;
-SPF_request_t *spf_request;
-SPF_response_t *spf_response = NULL;
-
-if (!(spf_request = SPF_request_new(spf_server)))
- {
- *errmsg = US"SPF_request_new() failed";
- return FAIL;
- }
-
-#if HAVE_IPV6
-switch (string_is_ip_address(filename, NULL))
-#else
-switch (4)
-#endif
+misc_module_info * mi = misc_mod_find(US"spf", errmsg);
+if (mi)
{
- case 4:
- if (!SPF_request_set_ipv4_str(spf_request, CS filename))
- break;
- *errmsg = string_sprintf("invalid IPv4 address '%s'", filename);
- return FAIL;
-#if HAVE_IPV6
-
- case 6:
- if (!SPF_request_set_ipv6_str(spf_request, CS filename))
- break;
- *errmsg = string_sprintf("invalid IPv6 address '%s'", filename);
- return FAIL;
-
- default:
- *errmsg = string_sprintf("invalid IP address '%s'", filename);
- return FAIL;
-#endif
+ typedef int (*fn_t) (void *, const uschar *, const uschar *,
+ int, uschar **, uschar **, uint *, const uschar *);
+ return (((fn_t *) mi->functions)[7]) (handle, filename, keystring, key_len,
+ result, errmsg, do_cache, opts);
}
-
-if (SPF_request_set_env_from(spf_request, CS keystring))
- {
- *errmsg = string_sprintf("invalid envelope from address '%s'", keystring);
- return FAIL;
-}
-
-SPF_request_query_mailfrom(spf_request, &spf_response);
-*result = string_copy(US SPF_strresult(SPF_response_result(spf_response)));
-
-DEBUG(D_lookup) spf_response_debug(spf_response);
-
-SPF_response_free(spf_response);
-SPF_request_free(spf_request);
-return OK;
+return FAIL;
}
}
-static lookup_info _lookup_info = {
+static lookup_info spf_lookup_info = {
.name = US"spf", /* lookup name */
.type = 0, /* not absfile, not query style */
.open = spf_open, /* open function */
.version_report = spf_version_report /* version reporting */
};
-#ifdef DYNLOOKUP
+#ifdef notdef_DYNLOOKUP
#define spf_lookup_module_info _lookup_module_info
#endif
-static lookup_info *_lookup_list[] = { &_lookup_info };
+static lookup_info *_lookup_list[] = { &spf_lookup_info };
lookup_module_info spf_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
#endif /* SUPPORT_SPF */
enum { opt_bit = 32, opt_bool_verify, opt_bool_set, opt_expand_bool,
opt_bool_last,
opt_rewrite, opt_timelist, opt_uid, opt_gid, opt_uidlist, opt_gidlist,
- opt_expand_uid, opt_expand_gid, opt_func, opt_void };
+ opt_expand_uid, opt_expand_gid, opt_func, opt_void, opt_module };
/* There's a high-ish bit which is used to flag duplicate options, kept
for compatibility, which shouldn't be output. Also used for hidden options
--- /dev/null
+# Make file for building Exim's lookup modules.
+# This is called from the main make file, after cd'ing
+# to the misc_modulessubdirectory.
+#
+# Copyright (c) The Exim Maintainers 2024
+
+# nb: at build time, the version of this file used will have had some
+# extra variable definitions and prepended to it and module build rules
+# interpolated below. This is done by scripts/lookups-Makefile.
+
+# MAGIC-TAG-MODS-OBJ-RULES-GO-HERE
+
+
+all: miscmods.a $(MODS)
+
+miscmods.a: $(OBJ)
+ @$(RM_COMMAND) -f miscmods.a
+ @echo "$(AR) miscmods.a"
+ @$(AR) miscmods.a $(OBJ)
+ $(RANLIB) $@
+
+.SUFFIXES: .o .c .so
+.c.o:; @echo "$(CC) $*.c"
+ $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) $*.c
+
+.c.so:; @echo "$(CC) -shared $*.c"
+ $(FE)$(CC) $(SUPPORT_$*_INCLUDE) $(SUPPORT_$*_LIBS) -DDYNLOOKUP $(CFLAGS_DYNAMIC) $(CFLAGS) $(INCLUDE) $(DLFLAGS) $*.c -o $@
+
+spf.o spf.so: $(HDRS) spf.h spf.c
+
+# End
--- /dev/null
+/*************************************************
+* Exim - an Internet mail transport agent *
+*************************************************/
+
+/* SPF support.
+ Copyright (c) The Exim Maintainers 2015 - 2024
+ Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004 - 2014
+ License: GPL
+ SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+/* Code for calling spf checks via libspf-alt. Called from acl.c. */
+
+#include "../exim.h"
+#ifdef SUPPORT_SPF
+
+/* must be kept in numeric order */
+static spf_result_id spf_result_id_list[] = {
+ /* name value */
+ { US"invalid", 0},
+ { US"neutral", 1 },
+ { US"pass", 2 },
+ { US"fail", 3 },
+ { US"softfail", 4 },
+ { US"none", 5 },
+ { US"temperror", 6 }, /* RFC 4408 defined */
+ { US"permerror", 7 } /* RFC 4408 defined */
+};
+
+SPF_server_t *spf_server = NULL;
+SPF_request_t *spf_request = NULL;
+SPF_response_t *spf_response = NULL;
+SPF_response_t *spf_response_2mx = NULL;
+
+SPF_dns_rr_t * spf_nxdomain = NULL;
+
+uschar * spf_guess = US"v=spf1 a/24 mx/24 ptr ?all";
+uschar * spf_header_comment = NULL;
+uschar * spf_received = NULL;
+uschar * spf_result = NULL;
+uschar * spf_smtp_comment = NULL;
+uschar * spf_smtp_comment_template
+ /* Used to be: "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}" */
+ = US"Please%_see%_http://www.open-spf.org/Why";
+BOOL spf_result_guessed = FALSE;
+
+
+
+
+static gstring *
+spf_lib_version_report(gstring * g)
+{
+int maj, min, patch;
+
+SPF_get_lib_version(&maj, &min, &patch);
+g = string_fmt_append(g, "Library version: spf2: Compile: %d.%d.%d\n",
+ SPF_LIB_VERSION_MAJOR, SPF_LIB_VERSION_MINOR, SPF_LIB_VERSION_PATCH);
+g = string_fmt_append(g, " Runtime: %d.%d.%d\n",
+ maj, min, patch);
+return g;
+}
+
+
+
+static SPF_dns_rr_t *
+SPF_dns_exim_lookup(SPF_dns_server_t *spf_dns_server,
+ const char *domain, ns_type rr_type, int should_cache)
+{
+dns_answer * dnsa = store_get_dns_answer();
+dns_scan dnss;
+SPF_dns_rr_t * spfrr;
+unsigned found = 0;
+
+SPF_dns_rr_t srr = {
+ .domain = CS domain, /* query information */
+ .domain_buf_len = 0,
+ .rr_type = rr_type,
+
+ .rr_buf_len = 0, /* answer information */
+ .rr_buf_num = 0, /* no free of s */
+ .utc_ttl = 0,
+
+ .hook = NULL, /* misc information */
+ .source = spf_dns_server
+};
+
+DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", domain);
+
+/* Shortcircuit SPF RR lookups by returning NO_DATA. They were obsoleted by
+RFC 6686/7208 years ago. see bug #1294 */
+
+if (rr_type == T_SPF)
+ {
+ HDEBUG(D_host_lookup) debug_printf("faking NO_DATA for SPF RR(99) lookup\n");
+ srr.herrno = NO_DATA;
+ SPF_dns_rr_dup(&spfrr, &srr);
+ store_free_dns_answer(dnsa);
+ return spfrr;
+ }
+
+switch (dns_lookup(dnsa, US domain, rr_type, NULL))
+ {
+ case DNS_AGAIN: srr.herrno = TRY_AGAIN; break;
+ case DNS_NOMATCH: srr.herrno = HOST_NOT_FOUND; break;
+ case DNS_NODATA: srr.herrno = NO_DATA; break;
+ case DNS_FAIL:
+ default: srr.herrno = NO_RECOVERY; break;
+ case DNS_SUCCEED:
+ srr.herrno = NETDB_SUCCESS;
+ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
+ rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
+ /* Need to alloc space for all records, so no early-out */
+ if (rr->type == rr_type) found++;
+ break;
+ }
+
+if (found == 0)
+ {
+ SPF_dns_rr_dup(&spfrr, &srr);
+ store_free_dns_answer(dnsa);
+ return spfrr;
+ }
+
+srr.rr = store_malloc(sizeof(SPF_dns_rr_data_t) * found);
+
+found = 0;
+for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
+ rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
+ if (rr->type == rr_type)
+ {
+ const uschar * s = rr->data;
+
+ srr.ttl = rr->ttl;
+ switch(rr_type)
+ {
+ case T_MX:
+ if (rr->size < 2) continue;
+ s += 2; /* skip the MX precedence field */
+ case T_PTR:
+ {
+ uschar * buf = store_malloc(256);
+ (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
+ (DN_EXPAND_ARG4_TYPE)buf, 256);
+ s = buf;
+ break;
+ }
+
+ case T_TXT:
+ {
+ gstring * g = NULL;
+ uschar chunk_len;
+
+ if (rr->size < 1+6) continue; /* min for version str */
+ if (strncmpic(rr->data+1, US SPF_VER_STR, 6) != 0)
+ {
+ HDEBUG(D_host_lookup) debug_printf("not an spf record: %.*s\n",
+ (int) s[0], s+1);
+ continue;
+ }
+
+ /* require 1 byte for the chunk_len */
+ for (int off = 0; off < rr->size - 1; off += chunk_len)
+ {
+ if ( !(chunk_len = s[off++])
+ || rr->size < off + chunk_len /* ignore bogus size chunks */
+ ) break;
+ g = string_catn(g, s+off, chunk_len);
+ }
+ if (!g)
+ continue;
+ gstring_release_unused(g);
+ s = string_copy_malloc(string_from_gstring(g));
+ DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", s);
+ break;
+ }
+
+ case T_A:
+ case T_AAAA:
+ default:
+ {
+ uschar * buf = store_malloc(dnsa->answerlen + 1);
+ s = memcpy(buf, s, dnsa->answerlen + 1);
+ break;
+ }
+ }
+ srr.rr[found++] = (void *) s;
+ }
+
+/* Did we filter out all TXT RRs? Return NO_DATA instead of SUCCESS with
+empty ANSWER section. */
+
+if (!(srr.num_rr = found))
+ srr.herrno = NO_DATA;
+
+/* spfrr->rr must have been malloc()d for this */
+SPF_dns_rr_dup(&spfrr, &srr);
+store_free_dns_answer(dnsa);
+return spfrr;
+}
+
+
+
+static SPF_dns_server_t *
+SPF_dns_exim_new(int debug)
+{
+SPF_dns_server_t * spf_dns_server = store_malloc(sizeof(SPF_dns_server_t));
+
+/* DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n"); */
+
+memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
+spf_dns_server->destroy = NULL;
+spf_dns_server->lookup = SPF_dns_exim_lookup;
+spf_dns_server->get_spf = NULL;
+spf_dns_server->get_exp = NULL;
+spf_dns_server->add_cache = NULL;
+spf_dns_server->layer_below = NULL;
+spf_dns_server->name = "exim";
+spf_dns_server->debug = debug;
+
+/* XXX This might have to return NO_DATA sometimes. */
+
+spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server,
+ "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
+if (!spf_nxdomain)
+ {
+ store_free(spf_dns_server);
+ return NULL;
+ }
+
+return spf_dns_server;
+}
+
+
+
+
+/* Construct the SPF library stack.
+ Return: Boolean success.
+*/
+
+static BOOL
+spf_init(void * dummy_ctx)
+{
+SPF_dns_server_t * dc;
+int debug = 0;
+const uschar *s;
+
+DEBUG(D_receive) debug = 1;
+
+/* We insert our own DNS access layer rather than letting the spf library
+do it, so that our dns access path is used for debug tracing and for the
+testsuite. */
+
+if (!(dc = SPF_dns_exim_new(debug)))
+ {
+ DEBUG(D_receive) debug_printf("spf: SPF_dns_exim_new() failed\n");
+ return FALSE;
+ }
+if (!(dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
+ {
+ DEBUG(D_receive) debug_printf("spf: SPF_dns_cache_new() failed\n");
+ return FALSE;
+ }
+if (!(spf_server = SPF_server_new_dns(dc, debug)))
+ {
+ DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
+ return FALSE;
+ }
+
+/* Override the outdated explanation URL.
+See https://www.mail-archive.com/mailop@mailop.org/msg08019.html
+Used to work as "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}",
+but is broken now (May 18th, 2020) */
+
+GET_OPTION("spf_smtp_comment_template");
+if (!(s = expand_string(spf_smtp_comment_template)))
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "expansion of spf_smtp_comment_template failed");
+
+SPF_server_set_explanation(spf_server, CCS s, &spf_response);
+if (SPF_response_errcode(spf_response) != SPF_E_SUCCESS)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", SPF_strerror(SPF_response_errcode(spf_response)));
+
+return TRUE;
+}
+
+
+/* Set up a context that can be re-used for several
+ messages on the same SMTP connection (that come from the
+ same host with the same HELO string).
+
+Return: Boolean success
+*/
+
+static BOOL
+spf_conn_init(uschar * spf_helo_domain, uschar * spf_remote_addr)
+{
+DEBUG(D_receive)
+ debug_printf("spf_conn_init: %s %s\n", spf_helo_domain, spf_remote_addr);
+
+if (!spf_server && !spf_init(NULL)) return FALSE;
+
+if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
+ {
+ DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
+ primary_hostname);
+ spf_server = NULL;
+ return FALSE;
+ }
+
+spf_request = SPF_request_new(spf_server);
+
+if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
+ && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
+ )
+ {
+ DEBUG(D_receive)
+ debug_printf("spf: SPF_request_set_ipv4_str() and "
+ "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
+ spf_server = NULL;
+ spf_request = NULL;
+ return FALSE;
+ }
+
+if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
+ {
+ DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
+ spf_helo_domain);
+ spf_server = NULL;
+ spf_request = NULL;
+ return FALSE;
+ }
+
+return TRUE;
+}
+
+static void
+spf_smtp_reset(void)
+{
+spf_header_comment = spf_received = spf_result = spf_smtp_comment = NULL;
+spf_result_guessed = FALSE;
+}
+
+
+static void
+spf_response_debug(SPF_response_t * spf_response)
+{
+if (SPF_response_messages(spf_response) == 0)
+ debug_printf(" (no errors)\n");
+else for (int i = 0; i < SPF_response_messages(spf_response); i++)
+ {
+ SPF_error_t * err = SPF_response_message(spf_response, i);
+ debug_printf( "%s_msg = (%d) %s\n",
+ (SPF_error_errorp(err) ? "warn" : "err"),
+ SPF_error_code(err),
+ SPF_error_message(err));
+ }
+}
+
+
+/* spf_process adds the envelope sender address to the existing
+ context (if any), retrieves the result, sets up expansion
+ strings and evaluates the condition outcome.
+
+Return: OK/FAIL */
+
+static int
+spf_process(const uschar ** listptr, const uschar * spf_envelope_sender,
+ int action)
+{
+int sep = 0;
+const uschar *list = *listptr;
+uschar *spf_result_id;
+int rc = SPF_RESULT_PERMERROR;
+
+DEBUG(D_receive) debug_printf("spf_process\n");
+
+if (!(spf_server && spf_request))
+ /* no global context, assume temp error and skip to evaluation */
+ rc = SPF_RESULT_PERMERROR;
+
+else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
+ /* Invalid sender address. This should be a real rare occurrence */
+ rc = SPF_RESULT_PERMERROR;
+
+else
+ {
+ /* get SPF result */
+ if (action == SPF_PROCESS_FALLBACK)
+ {
+ SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
+ spf_result_guessed = TRUE;
+ }
+ else
+ SPF_request_query_mailfrom(spf_request, &spf_response);
+
+ /* set up expansion items */
+ spf_header_comment = US SPF_response_get_header_comment(spf_response);
+ spf_received = US SPF_response_get_received_spf(spf_response);
+ spf_result = US SPF_strresult(SPF_response_result(spf_response));
+ spf_smtp_comment = US SPF_response_get_smtp_comment(spf_response);
+
+ rc = SPF_response_result(spf_response);
+
+ DEBUG(D_acl) spf_response_debug(spf_response);
+ }
+
+/* We got a result. Now see if we should return OK or FAIL for it */
+DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
+
+if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
+ return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
+
+while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
+ {
+ BOOL negate, result;
+
+ if ((negate = spf_result_id[0] == '!'))
+ spf_result_id++;
+
+ result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
+ if (negate != result) return OK;
+ }
+
+/* no match */
+return FAIL;
+}
+
+
+
+static gstring *
+authres_spf(gstring * g)
+{
+uschar * s;
+if (spf_result)
+ {
+ int start = 0; /* Compiler quietening */
+ DEBUG(D_acl) start = gstring_length(g);
+
+ g = string_append(g, 2, US";\n\tspf=", spf_result);
+ if (spf_result_guessed)
+ g = string_cat(g, US" (best guess record for domain)");
+
+ s = expand_string(US"$sender_address_domain");
+ if (s && *s)
+ g = string_append(g, 2, US" smtp.mailfrom=", s);
+ else
+ {
+ s = sender_helo_name;
+ g = s && *s
+ ? string_append(g, 2, US" smtp.helo=", s)
+ : string_cat(g, US" smtp.mailfrom=<>");
+ }
+ DEBUG(D_acl) debug_printf("SPF:\tauthres '%.*s'\n",
+ gstring_length(g) - start - 3, g->s + start + 3);
+ }
+else
+ DEBUG(D_acl) debug_printf("SPF:\tno authres\n");
+return g;
+}
+
+
+/* Ugly; used only by dmarc (peeking into our data!)
+Exposure of values as $variables might be better? */
+
+static SPF_response_t *
+spf_get_response(void)
+{
+return spf_response;
+}
+
+/******************************************************************************/
+/* Lookup support */
+
+static void *
+spf_lookup_open(const uschar * filename, uschar ** errmsg)
+{
+SPF_dns_server_t * dc;
+SPF_server_t * spf_server = NULL;
+int debug = 0;
+
+DEBUG(D_lookup) debug = 1;
+
+if ((dc = SPF_dns_exim_new(debug)))
+ if ((dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
+ spf_server = SPF_server_new_dns(dc, debug);
+
+if (!spf_server)
+ {
+ *errmsg = US"SPF_dns_exim_nnew() failed";
+ return NULL;
+ }
+return (void *) spf_server;
+}
+
+static void
+spf_lookup_close(void * handle)
+{
+SPF_server_t * spf_server = handle;
+if (spf_server) SPF_server_free(spf_server);
+}
+
+static int
+spf_lookup_find(void * handle, const uschar * filename,
+ const uschar * keystring, int key_len, uschar ** result, uschar ** errmsg,
+ uint * do_cache, const uschar * opts)
+{
+SPF_server_t *spf_server = handle;
+SPF_request_t *spf_request;
+SPF_response_t *spf_response = NULL;
+
+if (!(spf_request = SPF_request_new(spf_server)))
+ {
+ *errmsg = US"SPF_request_new() failed";
+ return FAIL;
+ }
+
+#if HAVE_IPV6
+switch (string_is_ip_address(filename, NULL))
+#else
+switch (4)
+#endif
+ {
+ case 4:
+ if (!SPF_request_set_ipv4_str(spf_request, CS filename))
+ break;
+ *errmsg = string_sprintf("invalid IPv4 address '%s'", filename);
+ return FAIL;
+#if HAVE_IPV6
+
+ case 6:
+ if (!SPF_request_set_ipv6_str(spf_request, CS filename))
+ break;
+ *errmsg = string_sprintf("invalid IPv6 address '%s'", filename);
+ return FAIL;
+
+ default:
+ *errmsg = string_sprintf("invalid IP address '%s'", filename);
+ return FAIL;
+#endif
+ }
+
+if (SPF_request_set_env_from(spf_request, CS keystring))
+ {
+ *errmsg = string_sprintf("invalid envelope from address '%s'", keystring);
+ return FAIL;
+}
+
+SPF_request_query_mailfrom(spf_request, &spf_response);
+*result = string_copy(US SPF_strresult(SPF_response_result(spf_response)));
+
+DEBUG(D_lookup) spf_response_debug(spf_response);
+
+SPF_response_free(spf_response);
+SPF_request_free(spf_request);
+return OK;
+}
+
+
+/******************************************************************************/
+/* Module API */
+
+static optionlist spf_options[] = {
+ { "spf_guess", opt_stringptr, {&spf_guess} },
+ { "spf_smtp_comment_template",opt_stringptr, {&spf_smtp_comment_template} },
+};
+
+static void * spf_functions[] = {
+ spf_conn_init,
+ spf_process,
+ authres_spf,
+ spf_get_response, /* ugly; for dmarc */
+ spf_smtp_reset,
+
+ spf_lookup_open,
+ spf_lookup_close,
+ spf_lookup_find,
+};
+
+static var_entry spf_variables[] = {
+ { "spf_guess", vtype_stringptr, &spf_guess },
+ { "spf_header_comment", vtype_stringptr, &spf_header_comment },
+ { "spf_received", vtype_stringptr, &spf_received },
+ { "spf_result", vtype_stringptr, &spf_result },
+ { "spf_result_guessed", vtype_bool, &spf_result_guessed },
+ { "spf_smtp_comment", vtype_stringptr, &spf_smtp_comment },
+};
+
+misc_module_info spf_module_info =
+{
+ .name = US"spf",
+# if SUPPORT_SPF==2
+ .dyn_magic = MISC_MODULE_MAGIC,
+# endif
+ .init = spf_init,
+ .lib_vers_report = spf_lib_version_report,
+
+ .options = spf_options,
+ .options_count = nelem(spf_options),
+
+ .functions = spf_functions,
+ .functions_count = nelem(spf_functions),
+
+ .variables = spf_variables,
+ .variables_count = nelem(spf_variables),
+};
+
+#endif /* almost all the file */
--- /dev/null
+/*************************************************
+* Exim - an Internet mail transport agent *
+*************************************************/
+
+/* SPF support.
+ Copyright (c) The Exim Maintainers 2016 - 2024
+ Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004
+ License: GPL
+ SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#ifdef SUPPORT_SPF
+
+/* Yes, we do have ns_type. spf.h redefines it if we don't set this. Doh */
+#if !defined(HAVE_NS_TYPE) && defined(NS_INADDRSZ)
+# define HAVE_NS_TYPE
+#endif
+#include <spf2/spf.h>
+
+#include <spf2/spf_dns_resolv.h>
+#include <spf2/spf_dns_cache.h>
+
+typedef struct spf_result_id {
+ uschar *name;
+ int value;
+} spf_result_id;
+
+#define SPF_PROCESS_NORMAL 0
+#define SPF_PROCESS_GUESS 1
+#define SPF_PROCESS_FALLBACK 2
+
+#endif
{ "spamd_address", opt_stringptr, {&spamd_address} },
#endif
#ifdef SUPPORT_SPF
- { "spf_guess", opt_stringptr, {&spf_guess} },
- { "spf_smtp_comment_template",opt_stringptr, {&spf_smtp_comment_template} },
+ { "spf_guess", opt_module, {US"spf"} },
+ { "spf_smtp_comment_template",opt_module, {US"spf"} },
#endif
{ "split_spool_directory", opt_bool, {&split_spool_directory} },
{ "spool_directory", opt_stringptr, {&spool_directory} },
offset = 4;
}
+sublist:
+
/* Search the list for the given name. A non-existent name, or an option that
is set twice, is a disaster. */
case opt_func:
ol->v.fn(name, s, 0);
break;
+
+ case opt_module:
+ {
+ uschar * errstr;
+ misc_module_info * mi = misc_mod_find(US ol->v.value, &errstr);
+ if (!mi)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "failed to find %s module for %s: %s", US ol->v.value, name, errstr);
+
+debug_printf("hunting for option %s in module %s\n", name, mi->name);
+ oltop = mi->options;
+ last = mi->options_count;
+ goto sublist;
+ }
}
return TRUE;
#endif
#ifdef SUPPORT_DMARC
-if (sender_host_address) dmarc_init(); /* initialize libopendmarc */
+if (sender_host_address) dmarc_conn_init(); /* initialize libopendmarc */
#endif
/* In SMTP sessions we may receive several messages in one connection. Before
#endif
dnslist_domain = dnslist_matched = NULL;
#ifdef SUPPORT_SPF
-spf_header_comment = spf_received = spf_result = spf_smtp_comment = NULL;
-spf_result_guessed = FALSE;
+ {
+ misc_module_info * mi = misc_mod_findonly(US"spf");
+ if (mi)
+ {
+ typedef void (*fn_t)(void);
+ (((fn_t *) mi->functions)[4])(); /* spf_smtp_reset*/
+ }
+ }
#endif
#ifndef DISABLE_DKIM
dkim_cur_signer = dkim_signers =
}
#ifdef SUPPORT_SPF
- /* set up SPF context */
- spf_conn_init(sender_helo_name, sender_host_address);
+ /* If we have an spf module, set up SPF context */
+ {
+ misc_module_info * mi = misc_mod_findonly(US"spf");
+ if (mi)
+ {
+ /* We have hardwired function-call numbers, and also prototypes for the
+ functions. We could do a function name table search for the number
+ but I can't see how to deal with prototypes. Is a K&R non-prototyped
+ function still usable with today's compilers? */
+
+ typedef BOOL (*fn_t)(uschar *, uschar *);
+ fn_t fn = ((fn_t *) mi->functions)[0]; /* spf_conn_init */
+
+ (void) fn(sender_helo_name, sender_host_address);
+ }
+ }
#endif
/* Apply an ACL check if one is defined; afterwards, recheck
+++ /dev/null
-/*************************************************
-* Exim - an Internet mail transport agent *
-*************************************************/
-
-/* SPF support.
- Copyright (c) The Exim Maintainers 2015 - 2024
- Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004 - 2014
- License: GPL
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-/* Code for calling spf checks via libspf-alt. Called from acl.c. */
-
-#include "exim.h"
-#ifdef SUPPORT_SPF
-
-/* must be kept in numeric order */
-static spf_result_id spf_result_id_list[] = {
- /* name value */
- { US"invalid", 0},
- { US"neutral", 1 },
- { US"pass", 2 },
- { US"fail", 3 },
- { US"softfail", 4 },
- { US"none", 5 },
- { US"temperror", 6 }, /* RFC 4408 defined */
- { US"permerror", 7 } /* RFC 4408 defined */
-};
-
-SPF_server_t *spf_server = NULL;
-SPF_request_t *spf_request = NULL;
-SPF_response_t *spf_response = NULL;
-SPF_response_t *spf_response_2mx = NULL;
-
-SPF_dns_rr_t * spf_nxdomain = NULL;
-
-
-gstring *
-spf_lib_version_report(gstring * g)
-{
-int maj, min, patch;
-
-SPF_get_lib_version(&maj, &min, &patch);
-g = string_fmt_append(g, "Library version: spf2: Compile: %d.%d.%d\n",
- SPF_LIB_VERSION_MAJOR, SPF_LIB_VERSION_MINOR, SPF_LIB_VERSION_PATCH);
-g = string_fmt_append(g, " Runtime: %d.%d.%d\n",
- maj, min, patch);
-return g;
-}
-
-
-
-static SPF_dns_rr_t *
-SPF_dns_exim_lookup(SPF_dns_server_t *spf_dns_server,
- const char *domain, ns_type rr_type, int should_cache)
-{
-dns_answer * dnsa = store_get_dns_answer();
-dns_scan dnss;
-SPF_dns_rr_t * spfrr;
-unsigned found = 0;
-
-SPF_dns_rr_t srr = {
- .domain = CS domain, /* query information */
- .domain_buf_len = 0,
- .rr_type = rr_type,
-
- .rr_buf_len = 0, /* answer information */
- .rr_buf_num = 0, /* no free of s */
- .utc_ttl = 0,
-
- .hook = NULL, /* misc information */
- .source = spf_dns_server
-};
-
-DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", domain);
-
-/* Shortcircuit SPF RR lookups by returning NO_DATA. They were obsoleted by
-RFC 6686/7208 years ago. see bug #1294 */
-
-if (rr_type == T_SPF)
- {
- HDEBUG(D_host_lookup) debug_printf("faking NO_DATA for SPF RR(99) lookup\n");
- srr.herrno = NO_DATA;
- SPF_dns_rr_dup(&spfrr, &srr);
- store_free_dns_answer(dnsa);
- return spfrr;
- }
-
-switch (dns_lookup(dnsa, US domain, rr_type, NULL))
- {
- case DNS_AGAIN: srr.herrno = TRY_AGAIN; break;
- case DNS_NOMATCH: srr.herrno = HOST_NOT_FOUND; break;
- case DNS_NODATA: srr.herrno = NO_DATA; break;
- case DNS_FAIL:
- default: srr.herrno = NO_RECOVERY; break;
- case DNS_SUCCEED:
- srr.herrno = NETDB_SUCCESS;
- for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
- rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
- /* Need to alloc space for all records, so no early-out */
- if (rr->type == rr_type) found++;
- break;
- }
-
-if (found == 0)
- {
- SPF_dns_rr_dup(&spfrr, &srr);
- store_free_dns_answer(dnsa);
- return spfrr;
- }
-
-srr.rr = store_malloc(sizeof(SPF_dns_rr_data_t) * found);
-
-found = 0;
-for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
- rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
- if (rr->type == rr_type)
- {
- const uschar * s = rr->data;
-
- srr.ttl = rr->ttl;
- switch(rr_type)
- {
- case T_MX:
- if (rr->size < 2) continue;
- s += 2; /* skip the MX precedence field */
- case T_PTR:
- {
- uschar * buf = store_malloc(256);
- (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
- (DN_EXPAND_ARG4_TYPE)buf, 256);
- s = buf;
- break;
- }
-
- case T_TXT:
- {
- gstring * g = NULL;
- uschar chunk_len;
-
- if (rr->size < 1+6) continue; /* min for version str */
- if (strncmpic(rr->data+1, US SPF_VER_STR, 6) != 0)
- {
- HDEBUG(D_host_lookup) debug_printf("not an spf record: %.*s\n",
- (int) s[0], s+1);
- continue;
- }
-
- /* require 1 byte for the chunk_len */
- for (int off = 0; off < rr->size - 1; off += chunk_len)
- {
- if ( !(chunk_len = s[off++])
- || rr->size < off + chunk_len /* ignore bogus size chunks */
- ) break;
- g = string_catn(g, s+off, chunk_len);
- }
- if (!g)
- continue;
- gstring_release_unused(g);
- s = string_copy_malloc(string_from_gstring(g));
- DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", s);
- break;
- }
-
- case T_A:
- case T_AAAA:
- default:
- {
- uschar * buf = store_malloc(dnsa->answerlen + 1);
- s = memcpy(buf, s, dnsa->answerlen + 1);
- break;
- }
- }
- srr.rr[found++] = (void *) s;
- }
-
-/* Did we filter out all TXT RRs? Return NO_DATA instead of SUCCESS with
-empty ANSWER section. */
-
-if (!(srr.num_rr = found))
- srr.herrno = NO_DATA;
-
-/* spfrr->rr must have been malloc()d for this */
-SPF_dns_rr_dup(&spfrr, &srr);
-store_free_dns_answer(dnsa);
-return spfrr;
-}
-
-
-
-SPF_dns_server_t *
-SPF_dns_exim_new(int debug)
-{
-SPF_dns_server_t * spf_dns_server = store_malloc(sizeof(SPF_dns_server_t));
-
-DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n");
-
-memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
-spf_dns_server->destroy = NULL;
-spf_dns_server->lookup = SPF_dns_exim_lookup;
-spf_dns_server->get_spf = NULL;
-spf_dns_server->get_exp = NULL;
-spf_dns_server->add_cache = NULL;
-spf_dns_server->layer_below = NULL;
-spf_dns_server->name = "exim";
-spf_dns_server->debug = debug;
-
-/* XXX This might have to return NO_DATA sometimes. */
-
-spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server,
- "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
-if (!spf_nxdomain)
- {
- store_free(spf_dns_server);
- return NULL;
- }
-
-return spf_dns_server;
-}
-
-
-
-
-/* Construct the SPF library stack.
- Return: Boolean success.
-*/
-
-BOOL
-spf_init(void)
-{
-SPF_dns_server_t * dc;
-int debug = 0;
-const uschar *s;
-
-DEBUG(D_receive) debug = 1;
-
-/* We insert our own DNS access layer rather than letting the spf library
-do it, so that our dns access path is used for debug tracing and for the
-testsuite. */
-
-if (!(dc = SPF_dns_exim_new(debug)))
- {
- DEBUG(D_receive) debug_printf("spf: SPF_dns_exim_new() failed\n");
- return FALSE;
- }
-if (!(dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
- {
- DEBUG(D_receive) debug_printf("spf: SPF_dns_cache_new() failed\n");
- return FALSE;
- }
-if (!(spf_server = SPF_server_new_dns(dc, debug)))
- {
- DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
- return FALSE;
- }
-
-/* Override the outdated explanation URL.
-See https://www.mail-archive.com/mailop@mailop.org/msg08019.html
-Used to work as "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}",
-but is broken now (May 18th, 2020) */
-
-GET_OPTION("spf_smtp_comment_template");
-if (!(s = expand_string(spf_smtp_comment_template)))
- log_write(0, LOG_MAIN|LOG_PANIC_DIE, "expansion of spf_smtp_comment_template failed");
-
-SPF_server_set_explanation(spf_server, CCS s, &spf_response);
-if (SPF_response_errcode(spf_response) != SPF_E_SUCCESS)
- log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", SPF_strerror(SPF_response_errcode(spf_response)));
-
-return TRUE;
-}
-
-
-/* Set up a context that can be re-used for several
- messages on the same SMTP connection (that come from the
- same host with the same HELO string).
-
-Return: Boolean success
-*/
-
-BOOL
-spf_conn_init(uschar * spf_helo_domain, uschar * spf_remote_addr)
-{
-DEBUG(D_receive)
- debug_printf("spf_conn_init: %s %s\n", spf_helo_domain, spf_remote_addr);
-
-if (!spf_server && !spf_init()) return FALSE;
-
-if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
- {
- DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
- primary_hostname);
- spf_server = NULL;
- return FALSE;
- }
-
-spf_request = SPF_request_new(spf_server);
-
-if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
- && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
- )
- {
- DEBUG(D_receive)
- debug_printf("spf: SPF_request_set_ipv4_str() and "
- "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
- spf_server = NULL;
- spf_request = NULL;
- return FALSE;
- }
-
-if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
- {
- DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
- spf_helo_domain);
- spf_server = NULL;
- spf_request = NULL;
- return FALSE;
- }
-
-return TRUE;
-}
-
-
-void
-spf_response_debug(SPF_response_t * spf_response)
-{
-if (SPF_response_messages(spf_response) == 0)
- debug_printf(" (no errors)\n");
-else for (int i = 0; i < SPF_response_messages(spf_response); i++)
- {
- SPF_error_t * err = SPF_response_message(spf_response, i);
- debug_printf( "%s_msg = (%d) %s\n",
- (SPF_error_errorp(err) ? "warn" : "err"),
- SPF_error_code(err),
- SPF_error_message(err));
- }
-}
-
-
-/* spf_process adds the envelope sender address to the existing
- context (if any), retrieves the result, sets up expansion
- strings and evaluates the condition outcome.
-
-Return: OK/FAIL */
-
-int
-spf_process(const uschar ** listptr, const uschar * spf_envelope_sender,
- int action)
-{
-int sep = 0;
-const uschar *list = *listptr;
-uschar *spf_result_id;
-int rc = SPF_RESULT_PERMERROR;
-
-DEBUG(D_receive) debug_printf("spf_process\n");
-
-if (!(spf_server && spf_request))
- /* no global context, assume temp error and skip to evaluation */
- rc = SPF_RESULT_PERMERROR;
-
-else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
- /* Invalid sender address. This should be a real rare occurrence */
- rc = SPF_RESULT_PERMERROR;
-
-else
- {
- /* get SPF result */
- if (action == SPF_PROCESS_FALLBACK)
- {
- SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
- spf_result_guessed = TRUE;
- }
- else
- SPF_request_query_mailfrom(spf_request, &spf_response);
-
- /* set up expansion items */
- spf_header_comment = US SPF_response_get_header_comment(spf_response);
- spf_received = US SPF_response_get_received_spf(spf_response);
- spf_result = US SPF_strresult(SPF_response_result(spf_response));
- spf_smtp_comment = US SPF_response_get_smtp_comment(spf_response);
-
- rc = SPF_response_result(spf_response);
-
- DEBUG(D_acl) spf_response_debug(spf_response);
- }
-
-/* We got a result. Now see if we should return OK or FAIL for it */
-DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
-
-if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
- return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
-
-while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
- {
- BOOL negate, result;
-
- if ((negate = spf_result_id[0] == '!'))
- spf_result_id++;
-
- result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
- if (negate != result) return OK;
- }
-
-/* no match */
-return FAIL;
-}
-
-
-
-gstring *
-authres_spf(gstring * g)
-{
-uschar * s;
-if (spf_result)
- {
- int start = 0; /* Compiler quietening */
- DEBUG(D_acl) start = gstring_length(g);
-
- g = string_append(g, 2, US";\n\tspf=", spf_result);
- if (spf_result_guessed)
- g = string_cat(g, US" (best guess record for domain)");
-
- s = expand_string(US"$sender_address_domain");
- if (s && *s)
- g = string_append(g, 2, US" smtp.mailfrom=", s);
- else
- {
- s = sender_helo_name;
- g = s && *s
- ? string_append(g, 2, US" smtp.helo=", s)
- : string_cat(g, US" smtp.mailfrom=<>");
- }
- DEBUG(D_acl) debug_printf("SPF:\tauthres '%.*s'\n",
- gstring_length(g) - start - 3, g->s + start + 3);
- }
-else
- DEBUG(D_acl) debug_printf("SPF:\tno authres\n");
-return g;
-}
-
-
-#endif
+++ /dev/null
-/*************************************************
-* Exim - an Internet mail transport agent *
-*************************************************/
-
-/* SPF support.
- Copyright (c) The Exim Maintainers 2016 - 2024
- Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004
- License: GPL
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#ifdef SUPPORT_SPF
-
-/* Yes, we do have ns_type. spf.h redefines it if we don't set this. Doh */
-#if !defined(HAVE_NS_TYPE) && defined(NS_INADDRSZ)
-# define HAVE_NS_TYPE
-#endif
-#include <spf2/spf.h>
-
-#include <spf2/spf_dns_resolv.h>
-#include <spf2/spf_dns_cache.h>
-
-typedef struct spf_result_id {
- uschar *name;
- int value;
-} spf_result_id;
-
-/* prototypes */
-gstring * spf_lib_version_report(gstring *);
-BOOL spf_init(void);
-BOOL spf_conn_init(uschar *, uschar *);
-int spf_process(const uschar **, const uschar *, int);
-void spf_response_debug(SPF_response_t *);
-
-#define SPF_PROCESS_NORMAL 0
-#define SPF_PROCESS_GUESS 1
-#define SPF_PROCESS_FALLBACK 2
-
-#endif
BOOL queue_2stage :1;
} qrunner;
+
+/* Types of variable table entry */
+
+enum vtypes {
+ vtype_int, /* value is address of int */
+ vtype_filter_int, /* ditto, but recognized only when filtering */
+ vtype_ino, /* value is address of ino_t (not always an int) */
+ vtype_uid, /* value is address of uid_t (not always an int) */
+ vtype_gid, /* value is address of gid_t (not always an int) */
+ vtype_bool, /* value is address of bool */
+ vtype_stringptr, /* value is address of pointer to string */
+ vtype_msgbody, /* as stringptr, but read when first required */
+ vtype_msgbody_end, /* ditto, the end of the message */
+ vtype_msgheaders, /* the message's headers, processed */
+ vtype_msgheaders_raw, /* the message's headers, unprocessed */
+ vtype_localpart, /* extract local part from string */
+ vtype_domain, /* extract domain from string */
+ vtype_string_func, /* value is string returned by given function */
+ vtype_todbsdin, /* value not used; generate BSD inbox tod */
+ vtype_tode, /* value not used; generate tod in epoch format */
+ vtype_todel, /* value not used; generate tod in epoch/usec format */
+ vtype_todf, /* value not used; generate full tod */
+ vtype_todl, /* value not used; generate log tod */
+ vtype_todlf, /* value not used; generate log file datestamp tod */
+ vtype_todzone, /* value not used; generate time zone only */
+ vtype_todzulu, /* value not used; generate zulu tod */
+ vtype_reply, /* value not used; get reply from headers */
+ vtype_pid, /* value not used; result is pid */
+ vtype_host_lookup, /* value not used; get host name */
+ vtype_load_avg, /* value not used; result is int from os_getloadavg */
+ vtype_pspace, /* partition space; value is T/F for spool/log */
+ vtype_pinodes, /* partition inodes; value is T/F for spool/log */
+ vtype_cert, /* SSL certificate */
+#ifndef DISABLE_DKIM
+ vtype_dkim, /* Lookup of value in DKIM signature */
+#endif
+ vtype_module, /* variable lives in a module; value is module name */
+};
+
+/* Type for main variable table */
+
+typedef struct {
+ const char *name;
+ enum vtypes type;
+ void *value;
+} var_entry;
+
+
+
+/* dynamic-load module info */
+
+typedef struct misc_module_info {
+ struct misc_module_info * next;
+
+ const uschar * name;
+ unsigned dyn_magic;
+ BOOL (*init)(void *); /* arg is the misc_module_info ptr */
+ gstring * (*lib_vers_report)(gstring *); /* underlying library */
+
+ void * options;
+ unsigned options_count;
+ void * functions;
+ unsigned functions_count;
+ void * variables;
+ unsigned variables_count;
+} misc_module_info;
+
+#define MISC_MODULE_MAGIC 0x4d4d4d31 /* MMM1 */
+
/* End of structs.h */
# Debug time & pid
-$time_pid = "(?:\\d{2}:\\d{2}:\\d{2}\\s+\\d+\\s)";
+$time_pid = "(?:(?:\\d{2}:\\d{2}:\\d{2}\\s+)?\\d+\\s)";
# Pattern for matching pids at start of stderr lines; initially something
# that won't match.
# different libraries will have different numbers (possibly 0) of follow-up
# lines, indenting with more data
if (/^$time_pid?Library version:/) {
- while (1) {
+ $_ = <IN>;
+ if (/^$time_pid?\s/) {
$_ = <IN>;
- next if /^$time_pid?\s/;
- goto RESET_AFTER_EXTRA_LINE_READ;
+ if (/^$time_pid?\s/) {
+ $_ = <IN>;
+ }
}
+ goto RESET_AFTER_EXTRA_LINE_READ;
}
# drop other build-time controls emitted for debugging
next if /^DKIM >> Body data for hash, canonicalized/;
# Not all platforms build with SPF enabled
- next if /(^spf_conn_init|^SPF_dns_exim_new|spf_compile\.c)/;
+ next if /(^$time_pid?spf_conn_init|spf_compile\.c)/;
next if /try option spf_smtp_comment_template$/;
+ next if /loading module 'spf'$/;
+ next if /^Loaded "spf"$/;
# Not all platforms have sendfile support
next if /^cannot use sendfile for body: no support$/;
01:01:01 p1235 sender_fullhost = (test) [10.0.0.0]
01:01:01 p1235 sender_rcvhost = [10.0.0.0] (helo=test)
01:01:01 p1235 set_process_info: pppp handling incoming connection from (test) [10.0.0.0]
-01:01:01 p1235 spf_conn_init: test 10.0.0.0
-01:01:01 p1235 SPF_dns_exim_new
01:01:01 p1235 try option acl_smtp_helo
01:01:01 p1235 SMTP>> 250 myhost.test.ex Hello test [10.0.0.0]
01:01:01 p1235 SMTP<< mail from:<a@b>