X-Git-Url: http://git.madism.org/?a=blobdiff_plain;f=postfix.c;h=e9cea6af3044a7046e0212e09b9beb3bfaf15cc8;hb=4fe282310226fdfc4a5c7b9c4131e6e5ba96315f;hp=5155471ec20ec6086ef022f49a7f93a6a31aebca;hpb=85f5a14903a2ffcbea92b3dffded303b69d1d712;p=apps%2Fpfixtools.git diff --git a/postfix.c b/postfix.c index 5155471..e9cea6a 100644 --- a/postfix.c +++ b/postfix.c @@ -34,64 +34,212 @@ */ #include +#include #include +#include #include #include "job.h" #include "postfix.h" +#include "buffer.h" +#include "tokens.h" + +#define ishspace(c) ((c) == ' ' || (c) == '\t') struct jpriv_t { buffer_t ibuf; buffer_t obuf; + query_t query; }; -struct tpriv_t { - job_t *jobs; -}; - -void postfix_start(job_t *job, query_t *query) +static jpriv_t *postfix_jpriv_init(jpriv_t *jp) +{ + buffer_init(&jp->ibuf); + buffer_init(&jp->obuf); + query_init(&jp->query); + return jp; +} +static void postfix_jpriv_wipe(jpriv_t *jp) { + query_wipe(&jp->query); + buffer_wipe(&jp->ibuf); + buffer_wipe(&jp->obuf); } +DO_NEW(jpriv_t, postfix_jpriv); +DO_DELETE(jpriv_t, postfix_jpriv); -void postfix_stop(job_t *job) +static void postfix_stop(job_t *job) { + postfix_jpriv_delete(&job->jdata); } -void postfix_process(job_t *job) +static int postfix_parsejob(query_t *query) { - if (job->state & JOB_LISTEN) { - /* TODO check return code */ - job_accept(job, JOB_READ); +#define PARSE_CHECK(expr, error, ...) \ + do { \ + if (!(expr)) { \ + syslog(LOG_ERR, error, ##__VA_ARGS__); \ + return -1; \ + } \ + } while (0) + + char *p = vskipspaces(query->data.data); + + while (*p) { + char *k, *v; + int klen, vlen, vtk; + + while (ishspace(*p)) + p++; + p = strchr(k = p, '='); + PARSE_CHECK(p, "could not find '=' in line"); + for (klen = p - k; klen && ishspace(k[klen]); klen--); + p += 1; /* skip = */ + + while (ishspace(*p)) + p++; + p = strstr(v = p, "\r\n"); + PARSE_CHECK(p, "could not find final \\r\\n in line"); + for (vlen = p - v; vlen && ishspace(v[vlen]); vlen--); + p += 2; /* skip \r\n */ + + vtk = tokenize(v, vlen); + switch (tokenize(k, klen)) { +#define CASE(up, low) case PTK_##up: query->low = v; v[vlen] = '\0'; break; + CASE(HELO_NAME, helo_name); + CASE(QUEUE_ID, queue_id); + CASE(SENDER, sender); + CASE(RECIPIENT, recipient); + CASE(RECIPIENT_COUNT, recipient_count); + CASE(CLIENT_ADDRESS, client_address); + CASE(CLIENT_NAME, client_name); + CASE(RCLIENT_NAME, rclient_name); + CASE(INSTANCE, instance); + CASE(SASL_METHOD, sasl_method); + CASE(SASL_USERNAME, sasl_username); + CASE(SASL_SENDER, sasl_sender); + CASE(SIZE, size); + CASE(CCERT_SUBJECT, ccert_subject); + CASE(CCERT_ISSUER, ccert_issuer); + CASE(CCSERT_FINGERPRINT, ccsert_fingerprint); + CASE(ENCRYPTION_PROTOCOL, encryption_protocol); + CASE(ENCRYPTION_CIPHER, encryption_cipher); + CASE(ENCRYPTION_KEYSIZE, encryption_keysize); + CASE(ETRN_DOMAIN, etrn_domain); +#undef CASE + + case PTK_REQUEST: + PARSE_CHECK(vtk == PTK_SMTPD_ACCESS_POLICY, + "unexpected `request' value: %.*s", vlen, v); + break; + + case PTK_PROTOCOL_NAME: + PARSE_CHECK(vtk == PTK_SMTP || vtk == PTK_ESMTP, + "unexpected `protocol_name' value: %.*s", vlen, v); + query->esmtp = vtk == PTK_ESMTP; + break; + + case PTK_PROTOCOL_STATE: + switch (vtk) { +#define CASE(name) case PTK_##name: query->state = SMTP_##name; break; + CASE(CONNECT); + CASE(EHLO); + CASE(HELO); + CASE(MAIL); + CASE(RCPT); + CASE(DATA); + CASE(END_OF_MESSAGE); + CASE(VRFY); + CASE(ETRN); + default: + PARSE_CHECK(false, "unexpected `protocol_state` value: %.*s", + vlen, v); +#undef CASE + } + break; + + default: + return -1; + } } - if (job->state & JOB_WRITE) { - int nbwritten; + return query->state == SMTP_UNKNOWN ? -1 : 0; + +#undef PARSE_CHECK +} + +static void postfix_process(job_t *job) +{ + int nb; + const char *p; + + switch (job->mode) { + case JOB_LISTEN: + if ((job = job_accept(job, JOB_READ))) { + job->jdata = postfix_jpriv_new(); + job->process = &postfix_process; + job->stop = &postfix_stop; + } + return; - nbwritten = write(job->fd, job->jdata->obuf.data, job->jdata->obuf.len); - if (nbwritten < 0) { - job->error = errno != EINTR && errno != EAGAIN; + case JOB_WRITE: + nb = write(job->fd, job->jdata->obuf.data, job->jdata->obuf.len); + if (nb < 0) { + if ((job->error = errno != EINTR && errno != EAGAIN)) { + syslog(LOG_ERR, "unexpected problem on the socket: %m"); + } return; } - buffer_consume(&job->jdata->obuf, nbwritten); - } + buffer_consume(&job->jdata->obuf, nb); + if (job->jdata->obuf.len) + return; + + job_update_mode(job, JOB_READ); - if (job->state & JOB_READ) { - int nbread; + /* fall through */ - nbread = buffer_read(&job->jdata->ibuf, job->fd, -1); - if (nbread < 0) { - job->error = errno != EINTR && errno != EAGAIN; + case JOB_READ: + nb = buffer_read(&job->jdata->ibuf, job->fd, -1); + if (nb < 0) { + if ((job->error = errno != EINTR && errno != EAGAIN)) { + syslog(LOG_ERR, "unexpected problem on the socket: %m"); + } return; } - if (nbread == 0) { + if (nb == 0) { + syslog(LOG_ERR, "unexpected eof"); job->error = true; return; } - if (!strstr(job->jdata->ibuf.data, "\r\n\r\n")) + p = strstr(skipspaces(job->jdata->ibuf.data), "\r\n\r\n"); + if (!p) { + if (job->jdata->ibuf.len > SHRT_MAX) { + syslog(LOG_ERR, "too much data without CRLFCRLF"); + job->error = true; + } return; + } + p += 4; + + query_reset(&job->jdata->query); + buffer_add(&job->jdata->query.data, job->jdata->ibuf.data, + p - 2 - job->jdata->ibuf.data); + buffer_consume(&job->jdata->query.data, p - job->jdata->ibuf.data); + + if (postfix_parsejob(&job->jdata->query) < 0) { + job->error = true; + return; + } + + job_update_mode(job, JOB_IDLE); + + /* TODO: run the scenario */ + return; - /* TODO: do the parse */ + default: + job->error = true; + return; } }