/* This file is part of gacopyz.
Copyright (C) 2006-2020 Sergey Poznyakoff
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#ifndef __gacopyz_h__
# define __gacopyz_h__
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef __cplusplus
extern "C" {
#endif
#define GACOPYZ_VERSION_MAJOR 2
#define GACOPYZ_VERSION_MINOR 0
/* Implementation version number */
#define SMFI_VERSION 0x01000000
#define SM_LM_VRS_MAJOR(v) (((v) & 0x7f000000) >> 24)
#define SM_LM_VRS_MINOR(v) (((v) & 0x007fff00) >> 8)
#define SM_LM_VRS_PLVL(v) ((v) & 0x0000007f)
#define GACOPYZ_SM_MKVER(maj,min,pat) \
((((maj) & 0x7f)<<24) | (((min) & 0x7ffff) << 8) | ((pat) & 0x7f))
/* Milter protocol version */
#define SMFI_PROT_VERSION 6
/* Minimal supported protocol version */
#define SMFI_PROT_VERSION_MIN 2
/* commands: don't use anything smaller than ' ' */
#define SMFIC_ABORT 'A' /* Abort */
#define SMFIC_BODY 'B' /* Body chunk */
#define SMFIC_CONNECT 'C' /* Connection information */
#define SMFIC_MACRO 'D' /* Define macro */
#define SMFIC_BODYEOB 'E' /* final body chunk (End) */
#define SMFIC_HELO 'H' /* HELO/EHLO */
#define SMFIC_HEADER 'L' /* Header */
#define SMFIC_MAIL 'M' /* MAIL from */
#define SMFIC_EOH 'N' /* EOH */
#define SMFIC_OPTNEG 'O' /* Option negotiation */
#define SMFIC_QUIT 'Q' /* QUIT */
#define SMFIC_RCPT 'R' /* RCPT to */
#define SMFIC_DATA 'T' /* DATA (SMFI_VERSION > 3) */
#define SMFIC_UNKNOWN 'U' /* Any unknown command */
/* actions (replies) */
#define SMFIR_ADDRCPT '+' /* add recipient */
#define SMFIR_DELRCPT '-' /* remove recipient */
#define SMFIR_ADDRCPT_PAR '2' /* add recipient */
#define SMFIR_SHUTDOWN '4' /* 421: shutdown (internal to MTA) */
#define SMFIR_ACCEPT 'a' /* accept */
#define SMFIR_REPLBODY 'b' /* replace body (chunk) */
#define SMFIR_CONTINUE 'c' /* continue */
#define SMFIR_DISCARD 'd' /* discard */
#define SMFIR_CHGFROM 'e' /* change envelope sender (from) */
#define SMFIR_CONN_FAIL 'f' /* cause a connection failure */
#define SMFIR_ADDHEADER 'h' /* add header */
#define SMFIR_INSHEADER 'i' /* insert header */
#if 0
#define SMFIR_SETSYMLIST 'l' /* set list of macros (unused) */
#endif
#define SMFIR_CHGHEADER 'm' /* change header */
#define SMFIR_PROGRESS 'p' /* progress */
#define SMFIR_QUARANTINE 'q' /* quarantine */
#define SMFIR_REJECT 'r' /* reject */
#define SMFIR_SKIP 's' /* skip */
#define SMFIR_TEMPFAIL 't' /* tempfail */
#define SMFIR_REPLYCODE 'y' /* reply code etc */
/* What the MTA can send/filter wants in protocol */
#define SMFIP_NOCONNECT 0x00000001L /* MTA should not send connect info */
#define SMFIP_NOHELO 0x00000002L /* MTA should not send HELO info */
#define SMFIP_NOMAIL 0x00000004L /* MTA should not send MAIL info */
#define SMFIP_NORCPT 0x00000008L /* MTA should not send RCPT info */
#define SMFIP_NOBODY 0x00000010L /* MTA should not send body */
#define SMFIP_NOHDRS 0x00000020L /* MTA should not send headers */
#define SMFIP_NOEOH 0x00000040L /* MTA should not send EOH */
#define SMFIP_NR_HDR 0x00000080L /* No reply for headers */
#define SMFIP_NOHREPL SMFIP_NR_HDR /* No reply for headers */
#define SMFIP_NOUNKNOWN 0x00000100L /* MTA should not send unknown
commands */
#define SMFIP_NODATA 0x00000200L /* MTA should not send DATA */
#define SMFIP_SKIP 0x00000400L /* MTA understands SMFIS_SKIP */
#define SMFIP_RCPT_REJ 0x00000800L /* MTA should also send rejected
RCPTs */
#define SMFIP_NR_CONN 0x00001000L /* No reply for connect */
#define SMFIP_NR_HELO 0x00002000L /* No reply for HELO */
#define SMFIP_NR_MAIL 0x00004000L /* No reply for MAIL */
#define SMFIP_NR_RCPT 0x00008000L /* No reply for RCPT */
#define SMFIP_NR_DATA 0x00010000L /* No reply for DATA */
#define SMFIP_NR_UNKN 0x00020000L /* No reply for UNKN */
#define SMFIP_NR_EOH 0x00040000L /* No reply for eoh */
#define SMFIP_NR_BODY 0x00080000L /* No reply for body chunk */
#define SMFIP_HDR_LEADSPC 0x00100000L /* header value leading space */
/* The protocol of V1 filter */
#define SMFI_V1_PROT (SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NOMAIL|\
SMFIP_NORCPT|SMFIP_NOBODY|SMFIP_NOHDRS)
/* The protocol of V2 filter */
#define SMFI_V2_PROT (SMFI_V1_PROT|SMFIP_NOEOH)
/* All defined prot bits */
#define SMFI_DEFAULT_PROT 0x001FFFFFL
/* Flags, defining milter capabilities */
#define SMFIF_NONE 0x00000000L /* no flags */
#define SMFIF_ADDHDRS 0x00000001L /* filter may add headers */
#define SMFIF_CHGBODY 0x00000002L /* filter may replace body */
#define SMFIF_MODBODY SMFIF_CHGBODY /* backwards compatible */
#define SMFIF_ADDRCPT 0x00000004L /* filter may add recipients */
#define SMFIF_DELRCPT 0x00000008L /* filter may delete recipients */
#define SMFIF_CHGHDRS 0x00000010L /* filter may change/delete headers */
#define SMFIF_QUARANTINE 0x00000020L /* filter may quarantine envelope */
#define SMFIF_CHGFROM 0x00000040L /* filter may change envelope "from" */
#define SMFIF_ADDRCPT_PAR 0x00000080L /* add recipients incl. args */
#define SMFI_V1_ACTS (SMFIF_ADDHDRS|SMFIF_CHGBODY|SMFIF_ADDRCPT|SMFIF_DELRCPT)
#define SMFI_V2_ACTS (SMFI_V1_ACTS|SMFIF_CHGHDRS|SMFIF_QUARANTINE)
#define SMFI_V6_ACTS (SMFI_V2_ACTS|SMFIF_CHGFROM|SMFIF_ADDRCPT_PAR)
#define SMFI_DEFAULT_ACTS SMFI_V6_ACTS
/* Log levels */
#define SMI_LOG_PROTO 0
#define SMI_LOG_DEBUG 1
#define SMI_LOG_INFO 2
#define SMI_LOG_WARN 3
#define SMI_LOG_ERR 4
#define SMI_LOG_FATAL 5
#define SMI_LOG_MASK(n) (1<<(n))
#define SMI_LOG_UPTO(n) ((1 << ((n)+1))-1) /* all levels through n */
#define SMI_LOG_FROM(n) (SMI_LOG_UPTO(SMI_LOG_FATAL) & \
~((n) == 0 ? 0 : SMI_LOG_UPTO((n)-1)))
#define SMI_DEFAULT_LOG_MASK SMI_LOG_MASK(SMI_LOG_INFO) \
| SMI_LOG_MASK(SMI_LOG_WARN) \
| SMI_LOG_MASK(SMI_LOG_ERR) \
| SMI_LOG_MASK(SMI_LOG_FATAL)
/* address families */
#define SMFIA_UNKNOWN 'U' /* unknown */
#define SMFIA_UNIX 'L' /* unix/local */
#define SMFIA_INET '4' /* inet */
#define SMFIA_INET6 '6' /* inet6 */
/* Function return codes */
#define MI_SUCCESS 0
#define MI_FAILURE (-1)
/* Data types */
typedef struct smfi_str SMFICTX;
typedef struct smfi_str *SMFICTX_PTR;
typedef struct gacopyz_conn *gacopyz_conn_t;
typedef struct gacopyz_iod *gacopyz_iod_t;
/* Return type for the callbacks */
typedef enum {
SMFIS_CONTINUE,
SMFIS_REJECT,
SMFIS_DISCARD,
SMFIS_ACCEPT,
SMFIS_TEMPFAIL,
SMFIS_NOREPLY = 7,
SMFIS_SKIP = 8,
SMFIS_ALL_OPTS = 10
} sfsistat;
typedef union {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_un sunix;
#ifdef GACOPYZ_IPV6
struct sockaddr_in6 sin6;
#endif
} milter_sockaddr_t;
enum gacopyz_stage {
gacopyz_stage_conn,
gacopyz_stage_helo,
gacopyz_stage_mail,
gacopyz_stage_rcpt,
gacopyz_stage_data,
gacopyz_stage_eom,
gacopyz_stage_eoh,
gacopyz_stage_max,
gacopyz_stage_none = gacopyz_stage_max
};
#define SMFIM_CONNECT gacopyz_stage_conn
#define SMFIM_HELO gacopyz_stage_helo
#define SMFIM_ENVFROM gacopyz_stage_mail
#define SMFIM_ENVRCPT gacopyz_stage_rcpt
#define SMFIM_DATA gacopyz_stage_data
#define SMFIM_EOM gacopyz_stage_eom
#define SMFIM_EOH gacopyz_stage_eoh
#define _SOCK_ADDR milter_sockaddr_t
#define smfiDesc gacopyz_milter_descr
typedef struct gacopyz_milter_descr gacopyz_milter_descr_t;
struct gacopyz_milter_descr
{
char *xxfi_name; /* filter name */
int xxfi_version; /* version code */
unsigned long xxfi_flags; /* flags */
/* Filters: */
/* connection info */
sfsistat (*xxfi_connect) (SMFICTX *, char *, milter_sockaddr_t *);
/* SMTP HELO command */
sfsistat (*xxfi_helo) (SMFICTX *, char *);
/* envelope sender */
sfsistat (*xxfi_envfrom) (SMFICTX *, char **);
/* envelope recipient */
sfsistat (*xxfi_envrcpt) (SMFICTX *, char **);
/* header */
sfsistat (*xxfi_header) (SMFICTX *, char *, char *);
/* end of header */
sfsistat (*xxfi_eoh) (SMFICTX *);
/* body block */
sfsistat (*xxfi_body) (SMFICTX *, unsigned char *, size_t);
/* end of message */
sfsistat (*xxfi_eom) (SMFICTX *);
/* message aborted */
sfsistat (*xxfi_abort) (SMFICTX *);
/* connection cleanup */
sfsistat (*xxfi_close) (SMFICTX *);
/* any unrecognized or unimplemented command (SMFI_VERSION > 2) */
sfsistat (*xxfi_unknown) (SMFICTX *, char *);
/* SMTP DATA command (SMFI_VERSION > 3) */
sfsistat (*xxfi_data) (SMFICTX *);
/* Milter negotiation (SMFI_VERSION > 3) */
sfsistat (*xxfi_negotiate) (SMFICTX *,
unsigned long, unsigned long,
unsigned long, unsigned long,
unsigned long *, unsigned long *,
unsigned long *, unsigned long *);
/* Extensions: */
int (*xxfi_start) (); /* Child start callback */
int (*xxfi_finish) (); /* Child finish callback */
int (*xxfi_idle) (gacopyz_conn_t); /* Idle handler */
int (*xxfi_accept) (gacopyz_conn_t, int, const milter_sockaddr_t *,
int salen);
int logmask;
struct timeval ctx_timeout;
};
/* Standard API calls */
extern int smfi_version (unsigned int *, unsigned int *, unsigned int *);
extern int smfi_opensocket (int);
extern int smfi_register (struct smfiDesc);
extern int smfi_main (void);
extern int smfi_setbacklog (int);
extern int smfi_setdbg (int);
extern int smfi_settimeout (time_t);
extern int smfi_setconn (char *);
extern int smfi_stop (void);
#define smfi_getsymval(ctx,s) ((char*) gacopyz_getsymval(ctx, (char*)s))
int smfi_addheader (SMFICTX *, char *, char *);
int smfi_chgheader (SMFICTX *, char *, int, char *);
int smfi_insheader (SMFICTX *, int, char *, char *);
int smfi_addrcpt (SMFICTX *, char *);
int smfi_delrcpt (SMFICTX *, char *);
int smfi_progress (SMFICTX *);
int smfi_replacebody (SMFICTX *, unsigned char *, int);
int smfi_quarantine (SMFICTX *ctx, char *reason);
int smfi_addrcpt_par(SMFICTX *ctx, char *rcpt, char *args);
int smfi_chgfrom(SMFICTX *ctx, char *from, char *args);
#define smfi_setpriv gacopyz_setpriv
#define smfi_getpriv gacopyz_getpriv
size_t smfi_setmaxdatasize (size_t);
int smfi_setsymlist(SMFICTX *ctx, int stage, char *macros);
/* Replies: */
extern int smfi_setreply (SMFICTX *, char *, char *, char *);
int smfi_setmlreply (SMFICTX *, const char *, const char *, ...);
/* Extensions */
extern int smfi_set_foreground(int val);
extern int smfi_setlogmask(int logmask);
/* New API calls: */
int gacopyz_init(gacopyz_conn_t *pconn, struct smfiDesc *desc);
void gacopyz_free(gacopyz_conn_t conn);
const char *gacopyz_safe_header_value(const char *input, char **poutput);
int gacopyz_set_logmask(gacopyz_conn_t conn, int mask);
int gacopyz_get_logmask(gacopyz_conn_t conn, int *mask);
int gacopyz_set_foreground(gacopyz_conn_t conn, int fg);
int gacopyz_set_master_timeout(gacopyz_conn_t conn, struct timeval *timeout);
int gacopyz_get_master_timeout(gacopyz_conn_t conn, struct timeval *timeout);
int gacopyz_set_ctx_timeout(gacopyz_conn_t conn, struct timeval *timeout);
int gacopyz_get_ctx_timeout(gacopyz_conn_t conn, struct timeval *timeout);
int gacopyz_stop(gacopyz_conn_t conn);
int gacopyz_open(gacopyz_conn_t conn, const char *cstr,
int backlog, int rmsocket);
int gacopyz_parse_connection(const char *cstr,
char **pproto, char **pport, char **ppath);
int gacopyz_connect(gacopyz_conn_t *, struct smfiDesc *, const char *,
int backlog, int rmsocket);
int gacopyz_get_fd(gacopyz_conn_t);
int gacopyz_run(gacopyz_conn_t);
int gacopyz_context_loop(int fd, struct smfiDesc const *desc,
milter_sockaddr_t *addr, socklen_t addrlen,
void *calldata);
int gacopyz_register_child(gacopyz_conn_t conn, pid_t pid);
void gacopyz_cleanup_children(gacopyz_conn_t conn);
void gacopyz_cleanup_conn(gacopyz_conn_t conn);
void gacopyz_setup_signals(void);
const char *gacopyz_getsymval (SMFICTX *, const char *);
int gacopyz_setreply(SMFICTX *ctx, const char *rcode, const char *xcode,
const char *message);
int gacopyz_setmlreply_v(SMFICTX *ctx, const char *rcode, const char *xcode,
...);
int gacopyz_setmlreply_va(SMFICTX *ctx, const char *rcode, const char *xcode,
va_list ap);
int _gacopyz_setmlreply_va(SMFICTX *ctx, size_t max, const char *rcode,
const char *xcode, va_list ap);
int gacopyz_add_header(SMFICTX *ctx, const char *headerf,
const char *headerv);
int gacopyz_insert_header(SMFICTX *ctx, int index, const char *headerf,
const char *headerv);
int gacopyz_change_header(SMFICTX *ctx, int index, const char *headerf,
const char *headerv);
int gacopyz_add_rcpt(SMFICTX *ctx, const char *rcpt);
int gacopyz_del_rcpt(SMFICTX *ctx, const char *rcpt);
int gacopyz_replace_body(SMFICTX *ctx, const unsigned char *bodyp,
size_t bodylen);
int gacopyz_replace_body_fn(SMFICTX *ctx,
void *bodyp,
ssize_t (*cpf)(char*,void*,size_t));
int gacopyz_replace_body_fd(SMFICTX *ctx, int fd);
int gacopyz_progress(SMFICTX *ctx);
int gacopyz_quarantine(SMFICTX *ctx, const char *reason);
int gacopyz_add_rcpt_par(SMFICTX *ctx, const char *rcpt, const char *args);
int gacopyz_chgfrom(SMFICTX *ctx, const char *from, const char *args);
int gacopyz_setsymlist(SMFICTX *ctx, enum gacopyz_stage ind,
const char *macros);
int gacopyz_setpriv (SMFICTX *ctx, void *data);
void *gacopyz_getpriv (SMFICTX *ctx);
void *gacopyz_getclosure(SMFICTX *ctx);
int gacopyz_client_sockname(SMFICTX *ctx,
milter_sockaddr_t *name, socklen_t *namelen);
int gacopyz_server_sockname(SMFICTX *ctx,
milter_sockaddr_t *name, socklen_t *namelen);
/* Logging (extensions) */
extern const char *gacopyz_stage_name[gacopyz_stage_max];
#define GACOPYZ_VBUFSIZE 69
size_t gacopyz_format_vbuf(char vbuf[GACOPYZ_VBUFSIZE], const char *buf,
size_t size);
void gacopyz_log(int level, char *fmt, ...);
void gacopyz_logdump(int level, const char *pfx,
const char *buf, size_t size);
void gacopyz_io_log(gacopyz_iod_t iod, int level, char *fmt, ...);
void gacopyz_io_logdump(gacopyz_iod_t iod, const char *pfx,
const char *buf, size_t size);
void gacopyz_set_logger(void (*)(int, char *, va_list));
void gacopyz_stderr_log_printer(int level, char *fmt, va_list ap);
void gacopyz_syslog_log_printer(int level, char *fmt, va_list ap);
int gacopyz_string_to_log_level(const char *str);
const char *gacopyz_log_level_to_string(int level);
/* Server */
typedef struct gacopyz_srv *gacopyz_srv_t;
enum gacopyz_flag_op {
gacopyz_flag_rpl,
gacopyz_flag_set,
gacopyz_flag_clr
};
#define GACOPYZ_SRV_DISABLED 0x0001
int gacopyz_srv_create(gacopyz_srv_t *p, const char *name,
const char *portspec, unsigned logmask);
int gacopyz_srv_create_X(gacopyz_srv_t *p, const char *spec, unsigned logmask);
void gacopyz_srv_destroy(gacopyz_srv_t *p);
int gacopyz_srv_find_macro (gacopyz_srv_t srv, const char *name,
const char **pval);
void gacopyz_srv_define_macro (gacopyz_srv_t srv,
const char *name, const char *value);
void gacopyz_srv_del_macro (gacopyz_srv_t srv, const char *name);
void gacopyz_srv_clear_macros (gacopyz_srv_t srv);
void gacopyz_srv_clear_macros_pred(gacopyz_srv_t srv,
int (*pred) (const char*, void*),
void *data);
void gacopyz_srv_iterate_macros (gacopyz_srv_t srv,
int (*func) (const char *name,
const char *value,
void *data),
void *data);
void gacopyz_srv_count_macros (gacopyz_srv_t srv, size_t *count);
const char **gacopyz_srv_get_required_macros(gacopyz_srv_t srv,
enum gacopyz_stage stage);
const char *gacopyz_srv_get_id(gacopyz_srv_t srv);
const char *gacopyz_srv_get_portspec(gacopyz_srv_t srv);
int gacopyz_srv_flags(gacopyz_srv_t srv, int flags, enum gacopyz_flag_op op);
int gacopyz_srv_get_flags(gacopyz_srv_t srv);
int gacopyz_srv_get_logmask(gacopyz_srv_t srv);
int gacopyz_srv_get_fd(gacopyz_srv_t srv);
int gacopyz_srv_get_onerr(gacopyz_srv_t srv);
int gacopyz_srv_open(gacopyz_srv_t srv);
int gacopyz_srv_init(gacopyz_srv_t srv);
int gacopyz_srv_negotiate(gacopyz_srv_t srv);
int gacopyz_srv_abort(gacopyz_srv_t srv);
int gacopyz_srv_quit(gacopyz_srv_t srv);
int gacopyz_srv_connect(gacopyz_srv_t srv, const char *hostname,
struct sockaddr *sa);
int gacopyz_srv_helo (gacopyz_srv_t p, const char *domain);
int gacopyz_srv_envfrom (gacopyz_srv_t p, char **argv);
int gacopyz_srv_envrcpt (gacopyz_srv_t p, char **argv);
int gacopyz_srv_header (gacopyz_srv_t p, char *name, char *value);
int gacopyz_srv_eoh (gacopyz_srv_t p);
int gacopyz_srv_body (gacopyz_srv_t p, unsigned char *str, size_t size);
int gacopyz_srv_eom (gacopyz_srv_t p, unsigned char *str, size_t size);
int gacopyz_srv_abort (gacopyz_srv_t p);
int gacopyz_srv_close (gacopyz_srv_t p);
int gacopyz_srv_data (gacopyz_srv_t p);
int gacopyz_srv_onerror(gacopyz_srv_t srv, int code);
void gacopyz_srv_set_logmask(gacopyz_srv_t srv, int logmask);
void gacopyz_srv_set_memerror(gacopyz_srv_t srv,
void (*memerror)(gacopyz_srv_t, const char *,
unsigned int));
void gacopyz_srv_set_callback(gacopyz_srv_t srv,
int (*cb) (gacopyz_srv_t, int, int, void *));
void gacopyz_srv_set_callback_data(gacopyz_srv_t, void *);
int gacopyz_srv_set_source(gacopyz_srv_t srv, struct sockaddr *sa,
socklen_t len);
int gacopyz_srv_set_version(gacopyz_srv_t srv, unsigned long version);
int gacopyz_srv_set_protocol(gacopyz_srv_t srv, unsigned long proto);
int gacopyz_srv_set_actions(gacopyz_srv_t srv, unsigned long acts);
#define GACOPYZ_WRITE_TIMEOUT 10
#define GACOPYZ_READ_TIMEOUT 10
#define GACOPYZ_EOM_TIMEOUT 300
#define GACOPYZ_CONNECT_TIMEOUT 300
#define GACOPYZ_TO_WRITE 0 /* Timeout for sending information */
#define GACOPYZ_TO_READ 1 /* Timeout waiting for a response */
#define GACOPYZ_TO_EOM 2 /* Timeout for ACK/NAK to EOM */
#define GACOPYZ_TO_CONNECT 3 /* Timeout for connect() */
#define GACOPYZ_TO_COUNT 4
void gacopyz_srv_set_all_timeouts(gacopyz_srv_t srv, struct timeval *tvp);
int gacopyz_srv_set_timeout(gacopyz_srv_t srv, unsigned n,
struct timeval *tvp);
int gacopyz_srv_set_timeout_sec(gacopyz_srv_t srv, unsigned n, time_t t);
int gacopyz_srv_reply(gacopyz_srv_t srv, char **msg, size_t *size);
void gacopyz_srv_reply_raw(gacopyz_srv_t srv, char **msg, size_t *size);
#ifdef __cplusplus
}
#endif
#endif /* __gacopyz_h__ */