File: [local] / src / usr.sbin / smtpd / table_proc.c (download)
Revision 1.19, Tue May 14 13:28:08 2024 UTC (3 weeks, 4 days ago) by op
Branch: MAIN
Changes since 1.18: +11 -11 lines
use C99 syntax for filling the table_backend structs; ok gilles@
|
/* $OpenBSD: table_proc.c,v 1.19 2024/05/14 13:28:08 op Exp $ */
/*
* Copyright (c) 2024 Omar Polo <op@openbsd.org>
* Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "smtpd.h"
#include "log.h"
#define PROTOCOL_VERSION "0.1"
struct table_proc_priv {
FILE *in;
FILE *out;
char *line;
size_t linesize;
/*
* The last ID used in a request. At the moment the protocol
* is synchronous from our point of view, so it's used to
* assert that the table replied with the correct ID.
*/
char lastid[16];
};
static char *
table_proc_nextid(struct table *table)
{
struct table_proc_priv *priv = table->t_handle;
int r;
r = snprintf(priv->lastid, sizeof(priv->lastid), "%lld",
(unsigned long long)arc4random());
if (r < 0 || (size_t)r >= sizeof(priv->lastid))
fatal("table-proc: snprintf");
return (priv->lastid);
}
static void
table_proc_send(struct table *table, const char *type, int service,
const char *param)
{
struct table_proc_priv *priv = table->t_handle;
struct timeval tv;
gettimeofday(&tv, NULL);
fprintf(priv->out, "table|%s|%lld.%06ld|%s|%s",
PROTOCOL_VERSION, (long long)tv.tv_sec, (long)tv.tv_usec,
table->t_name, type);
if (service != -1) {
fprintf(priv->out, "|%s|%s", table_service_name(service),
table_proc_nextid(table));
if (param)
fprintf(priv->out, "|%s", param);
fputc('\n', priv->out);
} else
fprintf(priv->out, "|%s\n", table_proc_nextid(table));
if (fflush(priv->out) == EOF)
fatal("table-proc: fflush");
}
static const char *
table_proc_recv(struct table *table, const char *type)
{
struct table_proc_priv *priv = table->t_handle;
const char *l;
ssize_t linelen;
size_t len;
if ((linelen = getline(&priv->line, &priv->linesize, priv->in)) == -1)
fatal("table-proc: getline");
priv->line[strcspn(priv->line, "\n")] = '\0';
l = priv->line;
len = strlen(type);
if (strncmp(l, type, len) != 0)
goto err;
l += len;
if (*l != '|')
goto err;
l++;
len = strlen(priv->lastid);
if (strncmp(l, priv->lastid, len) != 0)
goto err;
l += len;
if (*l != '|')
goto err;
return (++l);
err:
log_warnx("warn: table-proc: failed to parse reply");
fatalx("table-proc: exiting");
}
/*
* API
*/
static int
table_proc_open(struct table *table)
{
struct table_proc_priv *priv;
const char *s;
ssize_t len;
int service, services = 0;
int fd, fdd;
priv = xcalloc(1, sizeof(*priv));
fd = fork_proc_backend("table", table->t_config, table->t_name, 1);
if (fd == -1)
fatalx("table-proc: exiting");
if ((fdd = dup(fd)) == -1) {
log_warnx("warn: table-proc: dup");
fatalx("table-proc: exiting");
}
if ((priv->in = fdopen(fd, "r")) == NULL)
fatalx("table-proc: fdopen");
if ((priv->out = fdopen(fdd, "w")) == NULL)
fatalx("table-proc: fdopen");
fprintf(priv->out, "config|smtpd-version|"SMTPD_VERSION"\n");
fprintf(priv->out, "config|protocol|"PROTOCOL_VERSION"\n");
fprintf(priv->out, "config|tablename|%s\n", table->t_name);
fprintf(priv->out, "config|ready\n");
if (fflush(priv->out) == EOF)
fatalx("table-proc: fflush");
while ((len = getline(&priv->line, &priv->linesize, priv->in)) != -1) {
priv->line[strcspn(priv->line, "\n")] = '\0';
if (strncmp(priv->line, "register|", 9) != 0)
fatalx("table-proc: invalid handshake reply");
s = priv->line + 9;
if (!strcmp(s, "ready"))
break;
service = table_service_from_name(s);
if (service == -1 || service == K_NONE)
fatalx("table-proc: unknown service %s", s);
services |= service;
}
if (ferror(priv->in))
fatalx("table-proc: getline");
if (services == 0)
fatalx("table-proc: no services registered");
table->t_handle = priv;
return (1);
}
static int
table_proc_update(struct table *table)
{
const char *r;
table_proc_send(table, "update", -1, NULL);
r = table_proc_recv(table, "update-result");
if (!strcmp(r, "ok"))
return (1);
if (!strcmp(r, "error"))
return (0);
log_warnx("warn: table-proc: failed parse reply");
fatalx("table-proc: exiting");
}
static void
table_proc_close(struct table *table)
{
struct table_proc_priv *priv = table->t_handle;
if (fclose(priv->in) == EOF)
fatal("table-proc: fclose");
if (fclose(priv->out) == EOF)
fatal("table-proc: fclose");
free(priv->line);
free(priv);
table->t_handle = NULL;
}
static int
table_proc_lookup(struct table *table, enum table_service s, const char *k, char **dst)
{
const char *req = "lookup", *res = "lookup-result";
const char *r;
if (dst == NULL) {
req = "check";
res = "check-result";
}
table_proc_send(table, req, s, k);
r = table_proc_recv(table, res);
/* common replies */
if (!strcmp(r, "not-found"))
return (0);
if (!strcmp(r, "error"))
return (-1);
if (dst == NULL) {
/* check op */
if (!strncmp(r, "found", 5))
return (1);
log_warnx("warn: table-proc: failed to parse reply");
fatalx("table-proc: exiting");
}
/* lookup op */
if (strncmp(r, "found|", 6) != 0) {
log_warnx("warn: table-proc: failed to parse reply");
fatalx("table-proc: exiting");
}
r += 6;
if (*r == '\0') {
log_warnx("warn: table-proc: empty response");
fatalx("table-proc: exiting");
}
if ((*dst = strdup(r)) == NULL)
return (-1);
return (1);
}
static int
table_proc_fetch(struct table *table, enum table_service s, char **dst)
{
const char *r;
table_proc_send(table, "fetch", s, NULL);
r = table_proc_recv(table, "fetch-result");
if (!strcmp(r, "not-found"))
return (0);
if (!strcmp(r, "error"))
return (-1);
if (strncmp(r, "found|", 6) != 0) {
log_warnx("warn: table-proc: failed to parse reply");
fatalx("table-proc: exiting");
}
r += 6;
if (*r == '\0') {
log_warnx("warn: table-proc: empty response");
fatalx("table-proc: exiting");
}
if ((*dst = strdup(r)) == NULL)
return (-1);
return (1);
}
struct table_backend table_backend_proc = {
.name = "proc",
.services = K_ANY,
.config = NULL,
.add = NULL,
.dump = NULL,
.open = table_proc_open,
.update = table_proc_update,
.close = table_proc_close,
.lookup = table_proc_lookup,
.fetch = table_proc_fetch,
};