1 /******************************************************************************/
2 /* pfixtools: a collection of postfix related tools */
4 /* ________________________________________________________________________ */
6 /* Redistribution and use in source and binary forms, with or without */
7 /* modification, are permitted provided that the following conditions */
10 /* 1. Redistributions of source code must retain the above copyright */
11 /* notice, this list of conditions and the following disclaimer. */
12 /* 2. Redistributions in binary form must reproduce the above copyright */
13 /* notice, this list of conditions and the following disclaimer in the */
14 /* documentation and/or other materials provided with the distribution. */
15 /* 3. The names of its contributors may not be used to endorse or promote */
16 /* products derived from this software without specific prior written */
19 /* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS */
20 /* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */
21 /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
22 /* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY */
23 /* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL */
24 /* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS */
25 /* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) */
26 /* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, */
27 /* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN */
28 /* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE */
29 /* POSSIBILITY OF SUCH DAMAGE. */
31 /* Copyright (c) 2006-2008 the Authors */
32 /* see AUTHORS and source files for details */
33 /******************************************************************************/
36 * Copyright © 2007 Pierre Habouzit
37 * Copyright © 2008 Florent Bruneau
41 #include "policy_tokens.h"
44 const static_str_t smtp_state_names[SMTP_count] = {
50 { "END-OF-MESSAGE", 14 },
55 static const static_str_t static_ESMTP = { "ESMTP", 5 };
56 static const static_str_t static_SMTP = { "SMTP", 4 };
58 bool query_parse(query_t *query, char *p)
60 #define PARSE_CHECK(expr, error, ...) \
63 err(error, ##__VA_ARGS__); \
69 query->state = SMTP_UNKNOWN;
76 p = strchr(k = p, '=');
77 PARSE_CHECK(p, "could not find '=' in line");
78 for (klen = p - k; klen && isblank(k[klen]); klen--);
83 p = strchr(v = p, '\n');
84 PARSE_CHECK(p, "could not find final \\n in line");
85 for (vlen = p - v; vlen && isblank(v[vlen]); vlen--);
88 vtk = policy_tokenize(v, vlen);
89 switch (policy_tokenize(k, klen)) {
90 #define CASE(up, low) case PTK_##up: query->low.str = v; query->low.len = vlen; v[vlen] = '\0'; break;
91 CASE(HELO_NAME, helo_name);
92 CASE(QUEUE_ID, queue_id);
93 CASE(RECIPIENT_COUNT, recipient_count);
94 CASE(CLIENT_ADDRESS, client_address);
95 CASE(CLIENT_NAME, client_name);
96 CASE(REVERSE_CLIENT_NAME, reverse_client_name);
97 CASE(INSTANCE, instance);
98 CASE(SASL_METHOD, sasl_method);
99 CASE(SASL_USERNAME, sasl_username);
100 CASE(SASL_SENDER, sasl_sender);
102 CASE(CCERT_SUBJECT, ccert_subject);
103 CASE(CCERT_ISSUER, ccert_issuer);
104 CASE(CCERT_FINGERPRINT, ccert_fingerprint);
105 CASE(ENCRYPTION_PROTOCOL, encryption_protocol);
106 CASE(ENCRYPTION_CIPHER, encryption_cipher);
107 CASE(ENCRYPTION_KEYSIZE, encryption_keysize);
108 CASE(ETRN_DOMAIN, etrn_domain);
109 CASE(STRESS, stress);
113 query->sender.str = v;
114 query->sender.len = vlen;
116 query->sender_domain.str = memchr(query->sender.str, '@', vlen);
117 if (query->sender_domain.str != NULL) {
118 ++query->sender_domain.str;
119 query->sender_domain.len = query->sender.len
120 - (query->sender_domain.str - query->sender.str);
125 query->recipient.str = v;
126 query->recipient.len = vlen;
128 query->recipient_domain.str = memchr(query->recipient.str, '@', vlen);
129 if (query->recipient_domain.str != NULL) {
130 ++query->recipient_domain.str;
131 query->recipient_domain.len = query->recipient.len
132 - (query->recipient_domain.str - query->recipient.str);
138 PARSE_CHECK(vtk == PTK_SMTPD_ACCESS_POLICY,
139 "unexpected `request' value: %.*s", vlen, v);
142 case PTK_PROTOCOL_NAME:
143 PARSE_CHECK(vtk == PTK_SMTP || vtk == PTK_ESMTP,
144 "unexpected `protocol_name' value: %.*s", vlen, v);
145 query->esmtp = vtk == PTK_ESMTP;
148 case PTK_PROTOCOL_STATE:
150 #define CASE(name) case PTK_##name: query->state = SMTP_##name; break;
157 CASE(END_OF_MESSAGE);
161 PARSE_CHECK(false, "unexpected `protocol_state` value: %.*s",
168 warn("unexpected key, skipped: %.*s", klen, k);
173 return query->state != SMTP_UNKNOWN;
177 const static_str_t *query_field_for_id(const query_t *query, postlicyd_token id)
180 #define CASE(Up, Low) \
181 case PTK_ ## Up: return &query->Low;
182 CASE(HELO_NAME, helo_name)
183 CASE(QUEUE_ID, queue_id)
185 CASE(SENDER_DOMAIN, sender_domain)
186 CASE(RECIPIENT, recipient)
187 CASE(RECIPIENT_DOMAIN, recipient_domain)
188 CASE(RECIPIENT_COUNT, recipient_count)
189 CASE(CLIENT_ADDRESS, client_address)
190 CASE(CLIENT_NAME, client_name)
191 CASE(REVERSE_CLIENT_NAME, reverse_client_name)
192 CASE(INSTANCE, instance)
193 CASE(SASL_METHOD, sasl_method)
194 CASE(SASL_USERNAME, sasl_username)
195 CASE(SASL_SENDER, sasl_sender)
197 CASE(CCERT_SUBJECT, ccert_subject)
198 CASE(CCERT_ISSUER, ccert_issuer)
199 CASE(CCERT_FINGERPRINT, ccert_fingerprint)
200 CASE(ENCRYPTION_PROTOCOL, encryption_protocol)
201 CASE(ENCRYPTION_CIPHER, encryption_cipher)
202 CASE(ENCRYPTION_KEYSIZE, encryption_keysize)
203 CASE(ETRN_DOMAIN, etrn_domain)
206 case PTK_PROTOCOL_NAME:
207 return query->esmtp ? &static_ESMTP : &static_SMTP;
209 case PTK_PROTOCOL_STATE:
210 return &smtp_state_names[query->state];
212 default: return NULL;
216 const static_str_t *query_field_for_name(const query_t *query, const char *name)
218 postlicyd_token id = policy_tokenize(name, strlen(name));
219 if (id == PTK_UNKNOWN) {
220 warn("unknown query field %s", name);
223 return query_field_for_id(query, id);
226 ssize_t query_format(char *dest, size_t len, const char *fmt, const query_t *query)
231 #define WRITE(Src, Len) \
233 size_t __len = (Len); \
234 if (written < len) { \
235 size_t __to_write = MIN(len - written - 1, __len); \
236 memcpy(dest + written, (Src), __to_write); \
237 written += __to_write; \
241 while (*fmt != '\0') {
242 const char *next_format = strchr(fmt, '$');
243 while (next_format != NULL && next_format[1] != '{') {
244 next_format = strchr(next_format + 1, '$');
246 if (next_format == NULL) {
247 next_format = fmt + m_strlen(fmt);
249 WRITE(fmt, next_format - fmt);
253 next_format = strchr(fmt, '}');
254 if (next_format == NULL) {
258 postlicyd_token tok = policy_tokenize(fmt, next_format - fmt);
259 if (tok == PTK_UNKNOWN) {
260 warn("unknown field name \"%.*s\"", (int)(next_format - fmt), fmt);
262 const static_str_t *field = query == NULL ? NULL
263 : query_field_for_id(query, tok);
267 WRITE(field->str, field->len);
269 fmt = next_format + 1;
273 if (written > 0 && len > 0) {
274 dest[written] = '\0';
279 bool query_format_buffer(buffer_t *buf, const char *fmt, const query_t *query)
281 buffer_ensure(buf, m_strlen(fmt) + 64);
283 ssize_t size = array_free_space(*buf);
284 ssize_t format_size = query_format(array_end(*buf),
286 if (format_size == -1) {
288 } else if (format_size > size) {
289 buffer_ensure(buf, format_size + 1);
290 query_format(array_end(*buf),
291 array_free_space(*buf),
293 array_len(*buf) += format_size;
295 array_len(*buf) += format_size;