/* 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__ */