Skip to main content

milter_sys/
lib.rs

1//! Low-level FFI bindings to libmilter.
2
3#![allow(non_camel_case_types)]
4
5use libc::{c_char, c_int, c_uchar, c_uint, c_ulong, c_void, size_t};
6
7pub const SMFI_VERSION: c_int = 0x1000001;
8pub const SMFI_PROT_VERSION: c_int = 6;
9
10pub const MILTER_LEN_BYTES: size_t = 4;
11pub const MILTER_OPTLEN: size_t = MILTER_LEN_BYTES * 3;
12pub const MILTER_CHUNK_SIZE: size_t = 65535;
13pub const MILTER_MAX_DATA_SIZE: size_t = 65535;
14
15pub const SMFI_V1_ACTS: c_ulong = 0xf;
16pub const SMFI_V2_ACTS: c_ulong = 0x3f;
17pub const SMFI_CURR_ACTS: c_ulong = 0x1ff;
18
19pub const SMFIA_UNKNOWN: c_uchar = b'U';
20pub const SMFIA_UNIX: c_uchar = b'L';
21pub const SMFIA_INET: c_uchar = b'4';
22pub const SMFIA_INET6: c_uchar = b'6';
23
24pub const SMFIC_ABORT: c_uchar = b'A';
25pub const SMFIC_BODY: c_uchar = b'B';
26pub const SMFIC_CONNECT: c_uchar = b'C';
27pub const SMFIC_MACRO: c_uchar = b'D';
28pub const SMFIC_BODYEOB: c_uchar = b'E';
29pub const SMFIC_HELO: c_uchar = b'H';
30pub const SMFIC_QUIT_NC: c_uchar = b'K';
31pub const SMFIC_HEADER: c_uchar = b'L';
32pub const SMFIC_MAIL: c_uchar = b'M';
33pub const SMFIC_EOH: c_uchar = b'N';
34pub const SMFIC_OPTNEG: c_uchar = b'O';
35pub const SMFIC_QUIT: c_uchar = b'Q';
36pub const SMFIC_RCPT: c_uchar = b'R';
37pub const SMFIC_DATA: c_uchar = b'T';
38pub const SMFIC_UNKNOWN: c_uchar = b'U';
39
40pub const SMFIR_ADDRCPT: c_uchar = b'+';
41pub const SMFIR_DELRCPT: c_uchar = b'-';
42pub const SMFIR_ADDRCPT_PAR: c_uchar = b'2';
43pub const SMFIR_SHUTDOWN: c_uchar = b'4';
44pub const SMFIR_ACCEPT: c_uchar = b'a';
45pub const SMFIR_REPLBODY: c_uchar = b'b';
46pub const SMFIR_CONTINUE: c_uchar = b'c';
47pub const SMFIR_DISCARD: c_uchar = b'd';
48pub const SMFIR_CHGFROM: c_uchar = b'e';
49pub const SMFIR_CONN_FAIL: c_uchar = b'f';
50pub const SMFIR_ADDHEADER: c_uchar = b'h';
51pub const SMFIR_INSHEADER: c_uchar = b'i';
52pub const SMFIR_SETSYMLIST: c_uchar = b'l';
53pub const SMFIR_CHGHEADER: c_uchar = b'm';
54pub const SMFIR_PROGRESS: c_uchar = b'p';
55pub const SMFIR_QUARANTINE: c_uchar = b'q';
56pub const SMFIR_REJECT: c_uchar = b'r';
57pub const SMFIR_SKIP: c_uchar = b's';
58pub const SMFIR_TEMPFAIL: c_uchar = b't';
59pub const SMFIR_REPLYCODE: c_uchar = b'y';
60
61pub const SMFIP_NOCONNECT: c_ulong = 0x1;
62pub const SMFIP_NOHELO: c_ulong = 0x2;
63pub const SMFIP_NOMAIL: c_ulong = 0x4;
64pub const SMFIP_NORCPT: c_ulong = 0x8;
65pub const SMFIP_NOBODY: c_ulong = 0x10;
66pub const SMFIP_NOHDRS: c_ulong = 0x20;
67pub const SMFIP_NOEOH: c_ulong = 0x40;
68pub const SMFIP_NR_HDR: c_ulong = 0x80;
69pub const SMFIP_NOHREPL: c_ulong = SMFIP_NR_HDR;
70pub const SMFIP_NOUNKNOWN: c_ulong = 0x100;
71pub const SMFIP_NODATA: c_ulong = 0x200;
72pub const SMFIP_SKIP: c_ulong = 0x400;
73pub const SMFIP_RCPT_REJ: c_ulong = 0x800;
74pub const SMFIP_NR_CONN: c_ulong = 0x1000;
75pub const SMFIP_NR_HELO: c_ulong = 0x2000;
76pub const SMFIP_NR_MAIL: c_ulong = 0x4000;
77pub const SMFIP_NR_RCPT: c_ulong = 0x8000;
78pub const SMFIP_NR_DATA: c_ulong = 0x10000;
79pub const SMFIP_NR_UNKN: c_ulong = 0x20000;
80pub const SMFIP_NR_EOH: c_ulong = 0x40000;
81pub const SMFIP_NR_BODY: c_ulong = 0x80000;
82pub const SMFIP_HDR_LEADSPC: c_ulong = 0x100000;
83pub const SMFIP_MDS_256K: c_ulong = 0x10000000;
84pub const SMFIP_MDS_1M: c_ulong = 0x20000000;
85
86pub const SMFI_V1_PROT: c_ulong = 0x3f;
87pub const SMFI_V2_PROT: c_ulong = 0x7f;
88pub const SMFI_CURR_PROT: c_ulong = 0x1fffff;
89pub const SMFI_INTERNAL: c_ulong = 0x70000000;
90
91pub const MI_SUCCESS: c_int = 0;
92pub const MI_FAILURE: c_int = -1;
93pub const MI_CONTINUE: c_int = 1;
94
95pub const SMFIF_NONE: c_ulong = 0x0;
96pub const SMFIF_ADDHDRS: c_ulong = 0x1;
97pub const SMFIF_CHGBODY: c_ulong = 0x2;
98pub const SMFIF_MODBODY: c_ulong = SMFIF_CHGBODY;
99pub const SMFIF_ADDRCPT: c_ulong = 0x4;
100pub const SMFIF_DELRCPT: c_ulong = 0x8;
101pub const SMFIF_CHGHDRS: c_ulong = 0x10;
102pub const SMFIF_QUARANTINE: c_ulong = 0x20;
103pub const SMFIF_CHGFROM: c_ulong = 0x40;
104pub const SMFIF_ADDRCPT_PAR: c_ulong = 0x80;
105pub const SMFIF_SETSYMLIST: c_ulong = 0x100;
106
107pub const SMFIM_NOMACROS: c_int = -1;
108pub const SMFIM_FIRST: c_int = 0;
109pub const SMFIM_CONNECT: c_int = 0;
110pub const SMFIM_HELO: c_int = 1;
111pub const SMFIM_ENVFROM: c_int = 2;
112pub const SMFIM_ENVRCPT: c_int = 3;
113pub const SMFIM_DATA: c_int = 4;
114pub const SMFIM_EOM: c_int = 5;
115pub const SMFIM_EOH: c_int = 6;
116pub const SMFIM_LAST: c_int = 6;
117
118pub const SMFIS_CONTINUE: c_int = 0;
119pub const SMFIS_REJECT: c_int = 1;
120pub const SMFIS_DISCARD: c_int = 2;
121pub const SMFIS_ACCEPT: c_int = 3;
122pub const SMFIS_TEMPFAIL: c_int = 4;
123pub const SMFIS_NOREPLY: c_int = 7;
124pub const SMFIS_SKIP: c_int = 8;
125pub const SMFIS_ALL_OPTS: c_int = 10;
126
127#[repr(C)]
128#[derive(Debug, Copy, Clone)]
129pub struct smfi_str {
130    _private: [u8; 0],
131}
132
133pub type SMFICTX = smfi_str;
134pub type SMFICTX_PTR = *mut smfi_str;
135pub type smfiDesc_str = smfiDesc;
136pub type smfiDesc_ptr = *mut smfiDesc;
137pub type sfsistat = c_int;
138
139#[repr(C)]
140#[derive(Debug, Copy, Clone)]
141pub struct smfiDesc {
142    pub xxfi_name: *mut c_char,
143    pub xxfi_version: c_int,
144    pub xxfi_flags: c_ulong,
145    pub xxfi_connect: Option<unsafe extern "C" fn(ctx: *mut SMFICTX, hostname: *mut c_char, hostaddr: *mut libc::sockaddr) -> sfsistat>,
146    pub xxfi_helo: Option<unsafe extern "C" fn(ctx: *mut SMFICTX, helohost: *mut c_char) -> sfsistat>,
147    pub xxfi_envfrom: Option<unsafe extern "C" fn(ctx: *mut SMFICTX, argv: *mut *mut c_char) -> sfsistat>,
148    pub xxfi_envrcpt: Option<unsafe extern "C" fn(ctx: *mut SMFICTX, argv: *mut *mut c_char) -> sfsistat>,
149    pub xxfi_header: Option<unsafe extern "C" fn(ctx: *mut SMFICTX, headerf: *mut c_char, headerv: *mut c_char) -> sfsistat>,
150    pub xxfi_eoh: Option<unsafe extern "C" fn(ctx: *mut SMFICTX) -> sfsistat>,
151    pub xxfi_body: Option<unsafe extern "C" fn(ctx: *mut SMFICTX, bodyp: *mut c_uchar, bodylen: size_t) -> sfsistat>,
152    pub xxfi_eom: Option<unsafe extern "C" fn(ctx: *mut SMFICTX) -> sfsistat>,
153    pub xxfi_abort: Option<unsafe extern "C" fn(ctx: *mut SMFICTX) -> sfsistat>,
154    pub xxfi_close: Option<unsafe extern "C" fn(ctx: *mut SMFICTX) -> sfsistat>,
155    pub xxfi_unknown: Option<unsafe extern "C" fn(ctx: *mut SMFICTX, arg: *const c_char) -> sfsistat>,
156    pub xxfi_data: Option<unsafe extern "C" fn(ctx: *mut SMFICTX) -> sfsistat>,
157    pub xxfi_negotiate: Option<
158        unsafe extern "C" fn(
159            ctx: *mut SMFICTX,
160            f0: c_ulong,
161            f1: c_ulong,
162            f2: c_ulong,
163            f3: c_ulong,
164            pf0: *mut c_ulong,
165            pf1: *mut c_ulong,
166            pf2: *mut c_ulong,
167            pf3: *mut c_ulong,
168        ) -> sfsistat,
169    >,
170}
171
172extern "C" {
173    pub fn smfi_opensocket(rmsocket: c_int) -> c_int;
174    pub fn smfi_register(descr: smfiDesc) -> c_int;
175    pub fn smfi_main() -> c_int;
176    pub fn smfi_setbacklog(obacklog: c_int) -> c_int;
177    pub fn smfi_setdbg(level: c_int) -> c_int;
178    pub fn smfi_settimeout(otimeout: c_int) -> c_int;
179    pub fn smfi_setconn(oconn: *mut c_char) -> c_int;
180    pub fn smfi_stop() -> c_int;
181    pub fn smfi_setmaxdatasize(sz: size_t) -> size_t;
182    pub fn smfi_version(pmajor: *mut c_uint, pminor: *mut c_uint, ppl: *mut c_uint) -> c_int;
183    pub fn smfi_getsymval(ctx: *mut SMFICTX, symname: *mut c_char) -> *mut c_char;
184    pub fn smfi_setreply(ctx: *mut SMFICTX, rcode: *mut c_char, xcode: *mut c_char, message: *mut c_char) -> c_int;
185    pub fn smfi_setmlreply(ctx: *mut SMFICTX, rcode: *const c_char, xcode: *const c_char, ...) -> c_int;
186    pub fn smfi_addheader(ctx: *mut SMFICTX, headerf: *mut c_char, headerv: *mut c_char) -> c_int;
187    pub fn smfi_chgheader(ctx: *mut SMFICTX, headerf: *mut c_char, index: c_int, headerv: *mut c_char) -> c_int;
188    pub fn smfi_insheader(ctx: *mut SMFICTX, index: c_int, headerf: *mut c_char, headerv: *mut c_char) -> c_int;
189    pub fn smfi_chgfrom(ctx: *mut SMFICTX, mail: *mut c_char, args: *mut c_char) -> c_int;
190    pub fn smfi_addrcpt(ctx: *mut SMFICTX, rcpt: *mut c_char) -> c_int;
191    pub fn smfi_addrcpt_par(ctx: *mut SMFICTX, rcpt: *mut c_char, args: *mut c_char) -> c_int;
192    pub fn smfi_delrcpt(ctx: *mut SMFICTX, rcpt: *mut c_char) -> c_int;
193    pub fn smfi_progress(ctx: *mut SMFICTX) -> c_int;
194    pub fn smfi_replacebody(ctx: *mut SMFICTX, bodyp: *mut c_uchar, bodylen: c_int) -> c_int;
195    pub fn smfi_quarantine(ctx: *mut SMFICTX, reason: *mut c_char) -> c_int;
196    pub fn smfi_setpriv(ctx: *mut SMFICTX, privatedata: *mut c_void) -> c_int;
197    pub fn smfi_getpriv(ctx: *mut SMFICTX) -> *mut c_void;
198    pub fn smfi_setsymlist(ctx: *mut SMFICTX, stage: c_int, macros: *mut c_char) -> c_int;
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    #[test]
206    fn all_current_protocol_opts() {
207        let protocol_opts = SMFIP_NOCONNECT
208            | SMFIP_NOHELO
209            | SMFIP_NOMAIL
210            | SMFIP_NORCPT
211            | SMFIP_NOBODY
212            | SMFIP_NOHDRS
213            | SMFIP_NOEOH
214            | SMFIP_NR_HDR
215            | SMFIP_NOUNKNOWN
216            | SMFIP_NODATA
217            | SMFIP_SKIP
218            | SMFIP_RCPT_REJ
219            | SMFIP_NR_CONN
220            | SMFIP_NR_HELO
221            | SMFIP_NR_MAIL
222            | SMFIP_NR_RCPT
223            | SMFIP_NR_DATA
224            | SMFIP_NR_UNKN
225            | SMFIP_NR_EOH
226            | SMFIP_NR_BODY
227            | SMFIP_HDR_LEADSPC;
228
229        assert_eq!(protocol_opts, SMFI_CURR_PROT);
230    }
231
232    #[test]
233    fn smfi_version_returns_version() {
234        let (mut a, mut b, mut c) = (0, 0, 0);
235
236        let result = unsafe { smfi_version(&mut a, &mut b, &mut c) };
237
238        assert_eq!(result, MI_SUCCESS);
239        assert_eq!((a, b, c), (1, 0, 1));
240    }
241
242    #[test]
243    fn register_milter() {
244        let desc = smfiDesc {
245            xxfi_name: "test\0".as_ptr() as _,
246            xxfi_version: SMFI_VERSION,
247            xxfi_flags: 0,
248            xxfi_connect: None,
249            xxfi_helo: None,
250            xxfi_envfrom: None,
251            xxfi_envrcpt: None,
252            xxfi_header: None,
253            xxfi_eoh: None,
254            xxfi_body: None,
255            xxfi_eom: None,
256            xxfi_abort: None,
257            xxfi_close: None,
258            xxfi_unknown: None,
259            xxfi_data: None,
260            xxfi_negotiate: None,
261        };
262
263        let result = unsafe { smfi_register(desc) };
264
265        assert_eq!(result, MI_SUCCESS);
266    }
267}