static void qmqpd_copy_sender(QMQPD_STATE *state) { char *end_prefix; char *end_origin; int verp_requested; static char verp_delims[] = "-="; /* * If the sender address looks like prefix@origin-@[], then request * variable envelope return path delivery, with an envelope sender * address of prefi@origin, and with VERP delimiters of x and =. This * way, the recipients will see envelope sender addresses that look like: * prefixuser=domain@origin. */ state->where = "receiving sender address"; netstring_get(state->client, state->buf, var_line_limit); VSTRING_TERMINATE(state->buf); verp_requested = ((end_origin = vstring_end(state->buf) - 4) > STR(state->buf) && strcmp(end_origin, "-@[]") == 0 && (end_prefix = strchr(STR(state->buf), '@')) != 0 /* XXX */ && --end_prefix < end_origin - 2 /* non-null origin */ && end_prefix > STR(state->buf)); /* non-null prefix */ if (verp_requested) { verp_delims[0] = end_prefix[0]; if (verp_delims_verify(verp_delims) != 0) { state->err |= CLEANUP_STAT_CONT; /* XXX */ vstring_sprintf(state->why_rejected, "Invalid VERP delimiters: \"%s\". Need two characters from \"%s\"", verp_delims, var_verp_filter); } memmove(end_prefix, end_prefix + 1, end_origin - end_prefix - 1); vstring_truncate(state->buf, end_origin - STR(state->buf) - 1); } if (state->err == CLEANUP_STAT_OK && REC_PUT_BUF(state->cleanup, REC_TYPE_FROM, state->buf) < 0) state->err = CLEANUP_STAT_WRITE; if (verp_requested) if (state->err == CLEANUP_STAT_OK && rec_put(state->cleanup, REC_TYPE_VERP, verp_delims, 2) < 0) state->err = CLEANUP_STAT_WRITE; state->sender = mystrndup(STR(state->buf), LEN(state->buf)); }
static void receive_reply(int unused_event, char *context) { SESSION *session = (SESSION *) context; int except; /* * Prepare for disaster. */ if ((except = vstream_setjmp(session->stream)) != 0) msg_fatal("%s while receiving server reply", exception_text(except)); /* * Receive and process the server reply. */ netstring_get(session->stream, buffer, var_line_limit); if (msg_verbose) vstream_printf("<< %.*s\n", (int) LEN(buffer), STR(buffer)); if (STR(buffer)[0] != QMQP_STAT_OK) msg_fatal("%s error: %.*s", STR(buffer)[0] == QMQP_STAT_RETRY ? "recoverable" : STR(buffer)[0] == QMQP_STAT_HARD ? "unrecoverable" : "unknown", (int) LEN(buffer) - 1, STR(buffer) + 1); /* * Update the optional running counter. */ if (count) { counter++; vstream_printf("%d\r", counter); vstream_fflush(VSTREAM_OUT); } /* * Finish this session. QMQP sends only one message per session. */ event_disable_readwrite(vstream_fileno(session->stream)); vstream_fclose(session->stream); session->stream = 0; start_another(session); }
static void qmqpd_copy_recipients(QMQPD_STATE *state) { int ch; /* * Remember the first recipient. We are done when we read the over-all * netstring terminator. * * XXX This approach violates abstractions, but it is a heck of a lot more * convenient than counting the over-all byte count down to zero, like * qmail does. */ state->where = "receiving recipient address"; while ((ch = VSTREAM_GETC(state->client)) != ',') { vstream_ungetc(state->client, ch); netstring_get(state->client, state->buf, var_line_limit); if (state->err == CLEANUP_STAT_OK && REC_PUT_BUF(state->cleanup, REC_TYPE_RCPT, state->buf) < 0) state->err = CLEANUP_STAT_WRITE; state->rcpt_count++; if (state->recipient == 0) state->recipient = mystrndup(STR(state->buf), LEN(state->buf)); } }
static void qmqpd_read_content(QMQPD_STATE *state) { state->where = "receiving message content"; netstring_get(state->client, state->message, var_message_limit); }
static const char *dict_sockmap_lookup(DICT *dict, const char *key) { const char *myname = "dict_sockmap_lookup"; DICT_SOCKMAP *dp = (DICT_SOCKMAP *) dict; AUTO_CLNT *sockmap_clnt = DICT_SOCKMAP_RH_HANDLE(dp->client_info); VSTREAM *fp; int netstring_err; char *reply_payload; int except_count; const char *error_class; if (msg_verbose) msg_info("%s: key %s", myname, key); /* * Optionally fold the key. */ if (dict->flags & DICT_FLAG_FOLD_MUL) { if (dict->fold_buf == 0) dict->fold_buf = vstring_alloc(100); vstring_strcpy(dict->fold_buf, key); key = lowercase(STR(dict->fold_buf)); } /* * We retry connection-level errors once, to make server restarts * transparent. */ for (except_count = 0; /* see below */ ; except_count++) { /* * Look up the stream. */ if ((fp = auto_clnt_access(sockmap_clnt)) == 0) { msg_warn("table %s:%s lookup error: %m", dict->type, dict->name); dict->error = DICT_ERR_RETRY; return (0); } /* * Set up an exception handler. */ netstring_setup(fp, dict_sockmap_timeout); if ((netstring_err = vstream_setjmp(fp)) == 0) { /* * Send the query. This may raise an exception. */ vstring_sprintf(dp->rdwr_buf, "%s %s", dp->sockmap_name, key); NETSTRING_PUT_BUF(fp, dp->rdwr_buf); /* * Receive the response. This may raise an exception. */ netstring_get(fp, dp->rdwr_buf, dict_sockmap_max_reply); /* * If we got here, then no exception was raised. */ break; } /* * Handle exceptions. */ else { /* * We retry a broken connection only once. */ if (except_count == 0 && netstring_err == NETSTRING_ERR_EOF && errno != ETIMEDOUT) { auto_clnt_recover(sockmap_clnt); continue; } /* * We do not retry other errors. */ else { msg_warn("table %s:%s lookup error: %s", dict->type, dict->name, netstring_strerror(netstring_err)); dict->error = DICT_ERR_RETRY; return (0); } } } /* * Parse the reply. */ VSTRING_TERMINATE(dp->rdwr_buf); reply_payload = split_at(STR(dp->rdwr_buf), ' '); if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_OK) == 0) { dict->error = 0; return (reply_payload); } else if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_NOTFOUND) == 0) { dict->error = 0; return (0); } /* We got no definitive reply. */ if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_TEMP) == 0) { error_class = "temporary"; dict->error = DICT_ERR_RETRY; } else if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_TIMEOUT) == 0) { error_class = "timeout"; dict->error = DICT_ERR_RETRY; } else if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_PERM) == 0) { error_class = "permanent"; dict->error = DICT_ERR_CONFIG; } else { error_class = "unknown"; dict->error = DICT_ERR_RETRY; } while (reply_payload && ISSPACE(*reply_payload)) reply_payload++; msg_warn("%s:%s socketmap server %s error%s%.200s", dict->type, dict->name, error_class, reply_payload && *reply_payload ? ": " : "", reply_payload && *reply_payload ? printable(reply_payload, '?') : ""); return (0); }
int main (void) { stralloc indata = STRALLOC_ZERO ; unsigned int instate = 0 ; PROG = "s6-ftrigrd" ; if (ndelay_on(0) < 0) strerr_diefu2sys(111, "ndelay_on ", "0") ; if (ndelay_on(1) < 0) strerr_diefu2sys(111, "ndelay_on ", "1") ; if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ; { struct taia deadline, stamp ; taia_now(&stamp) ; taia_addsec(&deadline, &stamp, 2) ; if (!skaserver2_sync(&asyncout, FTRIGR_BANNER1, FTRIGR_BANNER1_LEN, FTRIGR_BANNER2, FTRIGR_BANNER2_LEN, &deadline, &stamp)) strerr_diefu1sys(111, "sync with client") ; } for (;;) { register unsigned int n = genalloc_len(ftrigio_t, &a) ; iopause_fd x[3 + n] ; unsigned int i = 0 ; int r ; x[0].fd = 0 ; x[0].events = IOPAUSE_EXCEPT | IOPAUSE_READ ; x[1].fd = 1 ; x[1].events = IOPAUSE_EXCEPT | (bufalloc_len(bufalloc_1) ? IOPAUSE_WRITE : 0) ; x[2].fd = bufalloc_fd(&asyncout) ; x[2].events = IOPAUSE_EXCEPT | (bufalloc_len(&asyncout) ? IOPAUSE_WRITE : 0) ; for (; i < n ; i++) { register ftrigio_t_ref p = genalloc_s(ftrigio_t, &a) + i ; p->xindex = 3 + i ; x[3+i].fd = p->trig.fd ; x[3+i].events = IOPAUSE_READ ; } r = iopause(x, 3 + n, 0, 0) ; if (r < 0) { cleanup() ; strerr_diefu1sys(111, "iopause") ; } /* client closed => exit */ if ((x[0].revents | x[1].revents) & IOPAUSE_EXCEPT) break ; /* client reading => flush pending data */ if (x[1].revents & IOPAUSE_WRITE) if ((bufalloc_flush(bufalloc_1) == -1) && !error_isagain(errno)) { cleanup() ; strerr_diefu1sys(111, "flush stdout") ; } if (x[2].revents & IOPAUSE_WRITE) if ((bufalloc_flush(&asyncout) == -1) && !error_isagain(errno)) { cleanup() ; strerr_diefu1sys(111, "flush asyncout") ; } /* scan listening ftrigs */ for (i = 0 ; i < genalloc_len(ftrigio_t, &a) ; i++) { register ftrigio_t_ref p = genalloc_s(ftrigio_t, &a) + i ; if (x[p->xindex].revents & IOPAUSE_READ) { char c ; register int r = sanitize_read(fd_read(p->trig.fd, &c, 1)) ; if (!r) continue ; if (r < 0) { trig(p->id, 'd', errno) ; remove(i--) ; } else if (!sredfa_feed(p->re, &p->dfastate, c)) { trig(p->id, 'd', ENOEXEC) ; remove(i--) ; } else if (p->dfastate & SREDFA_ACCEPT) { trig(p->id, '!', c) ; if (p->options & FTRIGR_REPEAT) p->dfastate = SREDFA_START ; else remove(i--) ; } } } /* client writing => get data and parse it */ if (buffer_len(buffer_0small) || x[0].revents & IOPAUSE_READ) { int r ; for (;;) { uint16 id ; r = sanitize_read(netstring_get(buffer_0small, &indata, &instate)) ; if (r <= 0) break ; if (indata.len < 3) { cleanup() ; strerr_dief1x(100, "invalid client request") ; } uint16_unpack_big(indata.s, &id) ; switch (indata.s[2]) /* protocol parsing */ { case 'U' : /* unsubscribe */ { register unsigned int i = genalloc_len(ftrigio_t, &a) ; for (; i ; i--) if (genalloc_s(ftrigio_t, &a)[i-1].id == id) break ; if (i) remove(i-1) ; answer(0) ; break ; } case 'L' : /* subscribe to path and match re */ { ftrigio_t f = FTRIGIO_ZERO ; uint32 pathlen, relen ; if (indata.len < 18) { answer(EPROTO) ; break ; } uint32_unpack_big(indata.s + 3, &f.options) ; uint32_unpack_big(indata.s + 7, &pathlen) ; uint32_unpack_big(indata.s + 11, &relen) ; if (((pathlen + relen + 16) != indata.len) || indata.s[15 + pathlen]) { answer(EPROTO) ; break ; } f.id = id ; if (!stralloc_0(&indata)) { answer(errno) ; break ; } f.re = sredfa_new() ; if (!f.re) { answer(errno) ; break ; } if (!sredfa_from_regexp(f.re, indata.s + 16 + pathlen) || !ftrig1_make(&f.trig, indata.s + 15)) { sredfa_delete(f.re) ; answer(errno) ; break ; } if (!genalloc_append(ftrigio_t, &a, &f)) { ftrigio_deepfree(&f) ; answer(errno) ; break ; } answer(0) ; break ; } default : { cleanup() ; strerr_dief1x(100, "invalid client request") ; } } indata.len = 0 ; } /* end loop: parse input from client */ if (r < 0) { if (errno == EPIPE) break ; /* client closed */ else { cleanup() ; strerr_diefu1sys(111, "read a netstring") ; } } } /* end if: stuff to read on stdin */ } /* end loop: main iopause */ cleanup() ; return 0 ; }