Hi
I have worked on this forther. The async ldap table is ready to test.
It's still partialy blocking (write and reconnect blocks), but search
is nonblocking.
I have also started to use the new table_api for the postgres table.
This is not yet finished.
[2024-07-15 12:47] Philipp <[email protected]>
> [2024-07-11 12:00] Omar Polo <[email protected]>
> > Philipp <[email protected]> wrote:
> [...]
> > > > My next step is to change the getline loop to a pool based loop. Then
> > > > add some registration for other fd. This way the backend can be full
> > > > asyncron.
> > [...]
> > I'd leave the choice of the event loop to use to the actual table
> > implementations. So that table-passwd can not care, and table-ldap can
> > use a poll-based loop or libevent or whatever it wants. Same for the
> > databases.
>
> > Running a fully asynchronous table on top of this with, say, libevent is
> > just a matter of a few lines of glue code, which is acceptable IMHO.
>
> > Suggestion: I make the handle_request() public and add a result struct.
> > So a table can choose to use the loop or implement it's own.
I have implemented this as I suggested.
> [...]
>
> Seperation between the single request types is because they are used all
> in different ways. Ihmo overloading the result functions to mutch makes
> only the code more complicated. Simple responses function for each response
> type don't have this problem.
Ok I was wrong, I have moved all request callbacks in one.
The current diff is attached. So others can review and test it. I also need
to do some more tests. Also I need to clean up the commit history.
Philipp
diff --git a/Makefile.am b/Makefile.am
index 5572b66..9132a4f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,11 +1,11 @@
noinst_PROGRAMS = table-ldap
-table_ldap_SOURCES = table_ldap.c aldap.c ber.c dict.c log.c table_stdio.c util.c
+table_ldap_SOURCES = table_ldap.c aldap.c ber.c dict.c log.c table_api.c util.c
LDADD = $(LIBOBJS)
EXTRA_DIST = aldap.h ber.h compat.h config.h.in \
- dict.h log.h table_stdio.h util.h
+ dict.h log.h table_api.h util.h
smtpdir = ${prefix}/libexec/smtpd
diff --git a/aldap.c b/aldap.c
index 1e86c7a..eec5c23 100644
--- a/aldap.c
+++ b/aldap.c
@@ -20,11 +20,15 @@
#include "compat.h"
#include <arpa/inet.h>
+#include <ber.h>
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
+#include <poll.h>
#include <string.h>
+#include <stdbool.h>
#include <stdlib.h>
+#include <tls.h>
#include <unistd.h>
#include <event.h>
@@ -136,6 +140,7 @@ aldap_send(struct aldap *ldap, struct ber_element *root)
char *data;
size_t len, done;
ssize_t error, wrote;
+ struct pollfd pfd = {ldap->fd, 0, 0};
len = ober_calc_len(root);
error = ober_write_elements(&ldap->ber, root);
@@ -147,16 +152,34 @@ aldap_send(struct aldap *ldap, struct ber_element *root)
done = 0;
data = ptr;
while (len > 0) {
+ if (pfd.events) {
+ /* TODO handle errors and timeout */
+ poll(&pfd, 1, -1);
+ }
+ pfd.events = 0;
+
if (ldap->tls != NULL) {
wrote = tls_write(ldap->tls, data + done, len);
- if (wrote == TLS_WANT_POLLIN ||
- wrote == TLS_WANT_POLLOUT)
+ switch (wrote) {
+ case TLS_WANT_POLLIN:
+ pfd.events = POLLIN;
+ continue;
+ case TLS_WANT_POLLOUT:
+ pfd.events = POLLOUT;
continue;
+ default:
+ break;
+ }
} else
wrote = write(ldap->fd, data + done, len);
- if (wrote == -1)
+ if (wrote == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ pfd.events = POLLOUT;
+ continue;
+ }
return -1;
+ }
len -= wrote;
done += wrote;
@@ -256,14 +279,16 @@ fail:
int
aldap_search(struct aldap *ldap, char *basedn, enum scope scope, const char *filter,
- const char *key, char **attrs, int typesonly, int sizelimit, int timelimit,
+ const char *key, char * const *attrs, int typesonly, int sizelimit, int timelimit,
struct aldap_page_control *page)
{
struct ber_element *root = NULL, *ber, *c;
int i;
- if ((root = ober_add_sequence(NULL)) == NULL)
+ if ((root = ober_add_sequence(NULL)) == NULL) {
+ ldap->err = ALDAP_ERR_OPERATION_FAILED;
goto fail;
+ }
ber = ober_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP,
LDAP_REQ_SEARCH);
@@ -286,8 +311,10 @@ aldap_search(struct aldap *ldap, char *basedn, enum scope scope, const char *fil
goto fail;
}
- if ((ber = ober_add_sequence(ber)) == NULL)
+ if ((ber = ober_add_sequence(ber)) == NULL) {
+ ldap->err = ALDAP_ERR_OPERATION_FAILED;
goto fail;
+ }
if (attrs != NULL)
for (i = 0; attrs[i] != NULL; i++) {
if ((ber = ober_add_string(ber, attrs[i])) == NULL)
@@ -352,7 +379,7 @@ fail:
}
struct aldap_message *
-aldap_parse(struct aldap *ldap)
+aldap_parse(struct aldap *ldap, bool blocking)
{
int class;
unsigned int type;
@@ -361,26 +388,54 @@ aldap_parse(struct aldap *ldap)
struct ber_element *a = NULL, *ep;
char rbuf[512];
int ret, retry;
+ struct pollfd pfd = {ldap->fd, 0, 0};
if ((m = calloc(1, sizeof(struct aldap_message))) == NULL)
- return NULL;
+ goto opfail;
retry = 0;
while (m->msg == NULL) {
if (retry || EVBUFFER_LENGTH(ldap->buf) == 0) {
+ if (pfd.events) {
+ /* TODO handle errors and timeout */
+ poll(&pfd, 1, -1);
+ }
+ pfd.events = 0;
+
if (ldap->tls) {
ret = tls_read(ldap->tls, rbuf, sizeof(rbuf));
- if (ret == TLS_WANT_POLLIN ||
- ret == TLS_WANT_POLLOUT)
- continue;
+ switch (ret) {
+ case TLS_WANT_POLLOUT:
+ if (blocking) {
+ pfd.events = POLLOUT;
+ continue;
+ }
+ goto epollout;
+ case TLS_WANT_POLLIN:
+ if (blocking) {
+ pfd.events = POLLIN;
+ continue;
+ }
+ goto epollin;
+ default:
+ break;
+ }
} else
ret = read(ldap->fd, rbuf, sizeof(rbuf));
+ if (ret == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+ if (blocking) {
+ pfd.events = POLLIN;
+ continue;
+ }
+ goto epollin;
+ }
if (ret <= 0) {
- goto parsefail;
+ goto opfail;
}
- evbuffer_add(ldap->buf, rbuf, ret);
+ if (evbuffer_add(ldap->buf, rbuf, ret) == -1)
+ goto enomem;
}
if (EVBUFFER_LENGTH(ldap->buf) > 0) {
@@ -450,11 +505,29 @@ aldap_parse(struct aldap *ldap)
}
return m;
+
parsefail:
evbuffer_drain(ldap->buf, EVBUFFER_LENGTH(ldap->buf));
ldap->err = ALDAP_ERR_PARSER_ERROR;
aldap_freemsg(m);
return NULL;
+opfail:
+ evbuffer_drain(ldap->buf, EVBUFFER_LENGTH(ldap->buf));
+ ldap->err = ALDAP_ERR_OPERATION_FAILED;
+ aldap_freemsg(m);
+ return NULL;
+enomem:
+ ldap->err = ALDAP_ERR_NOMEM;
+ aldap_freemsg(m);
+ return NULL;
+epollin:
+ ldap->err = ALDAP_ERR_NEED_POLLIN;
+ aldap_freemsg(m);
+ return NULL;
+epollout:
+ ldap->err = ALDAP_ERR_NEED_POLLOUT;
+ aldap_freemsg(m);
+ return NULL;
}
struct aldap_page_control *
@@ -498,6 +571,8 @@ aldap_parse_page_control(struct ber_element *control, size_t len)
void
aldap_freepage(struct aldap_page_control *page)
{
+ if (!page)
+ return;
free(page->cookie);
free(page);
}
@@ -505,7 +580,7 @@ aldap_freepage(struct aldap_page_control *page)
void
aldap_freemsg(struct aldap_message *msg)
{
- if (msg->msg)
+ if (msg && msg->msg)
ober_free_elements(msg->msg);
free(msg);
}
@@ -649,7 +724,7 @@ notfound:
}
int
-aldap_match_attr(struct aldap_message *msg, char *inkey,
+aldap_match_attr(const struct aldap_message *msg, char *inkey,
struct aldap_stringset **outvalues)
{
struct ber_element *a, *b;
@@ -686,21 +761,23 @@ notfound:
return (-1);
}
-int
+void
aldap_free_attr(struct aldap_stringset *values)
{
if (values == NULL)
- return -1;
+ return;
free(values->str);
free(values);
- return (1);
+ return;
}
void
aldap_free_url(struct aldap_url *lu)
{
+ if (!lu)
+ return;
free(lu->buffer);
}
diff --git a/aldap.h b/aldap.h
index 9bbc35a..9a23f29 100644
--- a/aldap.h
+++ b/aldap.h
@@ -17,10 +17,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <ber.h>
-#include <stdio.h>
-#include <tls.h>
-
#define LDAP_URL "ldap://"
#define LDAPS_URL "ldaps://"
#define LDAPTLS_URL "ldap+tls://"
@@ -37,6 +33,9 @@ struct aldap {
#define ALDAP_ERR_INVALID_FILTER 2
#define ALDAP_ERR_OPERATION_FAILED 3
#define ALDAP_ERR_TLS_ERROR 4
+#define ALDAP_ERR_NEED_POLLOUT 5
+#define ALDAP_ERR_NEED_POLLIN 6
+#define ALDAP_ERR_NOMEM 7
u_int8_t err;
int msgid;
struct ber ber;
@@ -215,14 +214,14 @@ struct aldap *aldap_init(int);
int aldap_tls(struct aldap *, struct tls_config *,
const char *);
int aldap_close(struct aldap *);
-struct aldap_message *aldap_parse(struct aldap *);
+struct aldap_message *aldap_parse(struct aldap *, bool);
void aldap_freemsg(struct aldap_message *);
int aldap_req_starttls(struct aldap *);
int aldap_bind(struct aldap *, char *, char *);
int aldap_unbind(struct aldap *);
-int aldap_search(struct aldap *, char *, enum scope, const char *, const char *, char **, int, int, int, struct aldap_page_control *);
+int aldap_search(struct aldap *, char *, enum scope, const char *, const char *, char * const *, int, int, int, struct aldap_page_control *);
int aldap_get_errno(struct aldap *, const char **);
int aldap_get_resultcode(struct aldap_message *);
@@ -236,13 +235,13 @@ int aldap_search_url(struct aldap *, char *, int, int, int,
struct aldap_page_control *);
int aldap_count_attrs(struct aldap_message *);
-int aldap_match_attr(struct aldap_message *, char *,
+int aldap_match_attr(const struct aldap_message *, char *,
struct aldap_stringset **);
int aldap_first_attr(struct aldap_message *, char **, struct
aldap_stringset **);
int aldap_next_attr(struct aldap_message *, char **,
struct aldap_stringset **);
-int aldap_free_attr(struct aldap_stringset *);
+void aldap_free_attr(struct aldap_stringset *);
struct aldap_page_control *aldap_parse_page_control(struct ber_element *, size_t len);
void aldap_freepage(struct aldap_page_control *);
diff --git a/table_api.c b/table_api.c
new file mode 100644
index 0000000..da40c5d
--- /dev/null
+++ b/table_api.c
@@ -0,0 +1,715 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) Philipp Takacs <[email protected]>
+ * Copyright (c) 2024 Omar Polo <[email protected]>
+ *
+ * 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 "config.h"
+
+#include <sys/tree.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <event.h>
+
+#include "dict.h"
+#include "log.h"
+#include "table_api.h"
+
+#ifndef MAXFDS
+#define MAXFDS 16
+#endif
+
+static void (*handler_async)(struct request *);
+static int (*handler_update)(void);
+static int (*handler_check)(int, struct dict *, const char *);
+static int (*handler_lookup)(int, struct dict *, const char *, char *, size_t);
+static int (*handler_fetch)(int, struct dict *, char *, size_t);
+
+static nfds_t nfds;
+static struct pollfd fds[MAXFDS];
+static fd_callback cbs[MAXFDS];
+
+static struct evbuffer *inbuffer;
+
+static bool configured;
+
+static char tablename[128];
+
+/*
+ * backword compatibility:
+ * register all the services since we don't have a clue yet what the
+ * table will do
+ */
+static int registered_services = K_ANY;
+
+/* Dummy; just kept for backward compatibility */
+static struct dict params;
+static struct dict lookup_entries;
+
+static int
+service_id(const char *service)
+{
+ if (!strcmp(service, "alias"))
+ return (K_ALIAS);
+ if (!strcmp(service, "domain"))
+ return (K_DOMAIN);
+ if (!strcmp(service, "credentials"))
+ return (K_CREDENTIALS);
+ if (!strcmp(service, "netaddr"))
+ return (K_NETADDR);
+ if (!strcmp(service, "userinfo"))
+ return (K_USERINFO);
+ if (!strcmp(service, "source"))
+ return (K_SOURCE);
+ if (!strcmp(service, "mailaddr"))
+ return (K_MAILADDR);
+ if (!strcmp(service, "addrname"))
+ return (K_ADDRNAME);
+ if (!strcmp(service, "mailaddrmap"))
+ return (K_MAILADDRMAP);
+
+ errx(1, "unknown service %s", service);
+}
+
+static char *
+table_api_service_name(enum table_service s)
+{
+ switch (s) {
+ case K_ALIAS: return "alias";
+ case K_DOMAIN: return "domain";
+ case K_CREDENTIALS: return "credentials";
+ case K_NETADDR: return "netaddr";
+ case K_USERINFO: return "userinfo";
+ case K_SOURCE: return "source";
+ case K_MAILADDR: return "mailaddr";
+ case K_ADDRNAME: return "addrname";
+ case K_MAILADDRMAP: return "mailaddrmap";
+ default: return "???";
+ }
+}
+
+static void
+fallback_update_handler(const char *id, const char *tname)
+{
+ int r;
+ strlcpy(tablename, tname, sizeof(tablename));
+
+ if (handler_update == NULL)
+ errx(1, "no update handler registered");
+
+ r = handler_update();
+ if (r == 1)
+ table_api_update_finish(id);
+ else
+ table_api_error(id, O_UPDATE, NULL);
+}
+
+static void
+fallback_check_handler(const char *id, const char *tname, int service, const char *key)
+{
+ int r;
+ strlcpy(tablename, tname, sizeof(tablename));
+
+ if (handler_check == NULL)
+ errx(1, "no check handler registered");
+
+ r = handler_check(service, ¶ms, key);
+ if (r == 0 || r == 1)
+ table_api_check_result(id, r == 1);
+ else
+ table_api_error(id, O_CHECK, NULL);
+}
+
+static void
+fallback_lookup_handler(const char *id, const char *tname, int service, const char *key)
+{
+ char buf[LINE_MAX];
+ int r;
+ strlcpy(tablename, tname, sizeof(tablename));
+
+ if (handler_lookup == NULL)
+ errx(1, "no lookup handler registered");
+
+ r = handler_lookup(service, ¶ms, key, buf, sizeof(buf));
+ if (r == 1) {
+ table_api_lookup_result(id, service, buf);
+ }
+ if (r == 1 || r == 0)
+ table_api_lookup_finish(id);
+ else
+ table_api_error(id, O_LOOKUP, NULL);
+}
+
+static void
+fallback_fetch_handler(const char *id, const char *tname, int service)
+{
+ char buf[LINE_MAX];
+ int r;
+ strlcpy(tablename, tname, sizeof(tablename));
+
+ if (handler_fetch == NULL)
+ errx(1, "no fetch handler registered");
+
+ r = handler_fetch(service, ¶ms, buf, sizeof(buf));
+ switch(r) {
+ case 1:
+ table_api_fetch_result(id, buf);
+ break;
+ case 0:
+ table_api_fetch_result(id, NULL);
+ break;
+ default:
+ table_api_error(id, O_FETCH, NULL);
+ }
+}
+
+void
+table_api_register_services(int s)
+{
+ registered_services = K_ANY & s;
+}
+
+void
+table_api_on_update(int(*cb)(void))
+{
+ handler_update = cb;
+}
+
+void
+table_api_on_check(int(*cb)(int, struct dict *, const char *))
+{
+ handler_check = cb;
+}
+
+void
+table_api_on_lookup(int(*cb)(int, struct dict *, const char *, char *, size_t))
+{
+ handler_lookup = cb;
+}
+
+void
+table_api_on_fetch(int(*cb)(int, struct dict *, char *, size_t))
+{
+ handler_fetch = cb;
+}
+
+void
+table_api_on_request(void(*cb)(struct request *))
+{
+ handler_async = cb;
+}
+
+const char *
+table_api_get_name(void)
+{
+ return tablename;
+}
+
+void
+table_api_error(const char *id, enum table_operation o, const char *error)
+{
+ struct evbuffer *res;
+
+ switch(o) {
+ case O_UPDATE:
+ printf("update-result|%s|error", id);
+ break;
+ case O_CHECK:
+ printf("check-result|%s|error", id);
+ break;
+ case O_LOOKUP:
+ printf("lookup-result|%s|error", id);
+ break;
+ case O_FETCH:
+ printf("fetch-result|%s|error", id);
+ break;
+ }
+
+#ifdef errormassage
+ if (error && *error) {
+ printf("|%s\n", error);
+ } else {
+ puts("|unknown");
+ }
+#else
+ (void)error;
+ puts("");
+#endif
+ if (fflush(stdout) == EOF)
+ err(1, "fflush");
+ res = dict_pop(&lookup_entries, id);
+ if (res)
+ evbuffer_free(res);
+}
+
+void
+table_api_update_finish(const char *id)
+{
+ if (!id) {
+ log_warnx("%s: unknow id %s", __func__, id);
+ return;
+ }
+
+ printf("update-result|%s|ok\n", id);
+ if (fflush(stdout) == EOF)
+ err(1, "fflush");
+}
+
+void
+table_api_check_result(const char *id, bool found)
+{
+ if (found)
+ printf("check-result|%s|found\n", id);
+ else
+ printf("check-result|%s|not-found\n", id);
+
+ if (fflush(stdout) == EOF)
+ err(1, "fflush");
+}
+
+void
+table_api_lookup_result(const char *id, enum table_service s, const char *buf)
+{
+ const char alias_sep[] = ", ";
+ struct evbuffer *res;
+
+ res = dict_get(&lookup_entries, id);
+
+ if (!res) {
+ res = evbuffer_new();
+ if (!res) {
+ table_api_error(id, O_LOOKUP, "can not alloc result");
+ return;
+ }
+ if (evbuffer_add(res, buf, strlen(buf)) == -1) {
+ table_api_error(id, O_LOOKUP, "can not alloc result");
+ evbuffer_free(res);
+ return;
+ }
+ dict_set(&lookup_entries, id, res);
+ return;
+ }
+ switch(s) {
+ case K_ALIAS:
+ if (evbuffer_add(res, alias_sep, sizeof(alias_sep)-1) == -1) {
+ table_api_error(id, O_LOOKUP, "can not extend result");
+ dict_pop(&lookup_entries, id);
+ evbuffer_free(res);
+ return;
+ }
+ if (evbuffer_add(res, buf, 0) == -1) {
+ table_api_error(id, O_LOOKUP, "can not extend result");
+ dict_pop(&lookup_entries, id);
+ evbuffer_free(res);
+ return;
+ }
+ break;
+ default:
+ log_warnx("id: %s lookup result override", id);
+ evbuffer_drain(res, evbuffer_get_length(res));
+ if (evbuffer_add(res, buf, sizeof(buf)) == -1) {
+ table_api_error(id, O_LOOKUP, "can not alloc result");
+ dict_pop(&lookup_entries, id);
+ evbuffer_free(res);
+ return;
+ }
+ }
+}
+
+void
+table_api_lookup_finish(const char *id)
+{
+ struct evbuffer *res;
+
+ res = dict_pop(&lookup_entries, id);
+ if (res && evbuffer_get_length(res)) {
+ if (evbuffer_add(res, "\0", 1) == -1) {
+ table_api_error(id, O_LOOKUP, "can not extend result");
+ evbuffer_free(res);
+ return;
+ }
+ printf("lookup-result|%s|found|%s\n", id, evbuffer_pullup(res, -1));
+ } else {
+ printf("lookup-result|%s|not-found\n", id);
+ }
+
+ if (fflush(stdout) == EOF)
+ err(1, "fflush");
+ if (res)
+ evbuffer_free(res);
+}
+
+void
+table_api_fetch_result(const char *id, const char *buf)
+{
+ if (buf && *buf)
+ printf("fetch-result|%s|found|%s\n", id, buf);
+ else
+ printf("fetch-result|%s|not-found\n", id);
+
+ if (fflush(stdout) == EOF)
+ err(1, "fflush");
+}
+
+void
+table_api_register_fd(int fd, short events, fd_callback cb)
+{
+ /* first fd is reservated for stdin */
+ if (!nfds)
+ nfds++;
+
+ if (nfds >= MAXFDS)
+ exit(1);
+
+ fds[nfds].fd = fd;
+ fds[nfds].events = events;
+ cbs[nfds] = cb;
+ nfds++;
+}
+
+void
+table_api_replace_fd(int old, int new)
+{
+ for (nfds_t i = 1; i < nfds; i++) {
+ if (fds[i].fd != old) {
+ continue;
+ }
+ fds[i].fd = new;
+ }
+}
+
+void
+table_api_fd_set_events(int fd, short events)
+{
+ for (size_t i = 0; i < nfds; i++) {
+ if (fds[i].fd != fd)
+ continue;
+ fds[i].events = events;
+ return;
+ }
+}
+
+void
+table_api_remove_fd(int fd)
+{
+ for (nfds_t i = 1; i < nfds; i++) {
+ if (fds[i].fd != fd) {
+ continue;
+ }
+ nfds--;
+ if (i+1 > nfds) {
+ break;
+ }
+ memmove(fds+i, fds+i+1, (nfds-i)*sizeof(*fds));
+ memmove(cbs+i, cbs+i+1, (nfds-i)*sizeof(*cbs));
+ }
+}
+
+bool
+table_api_parse_line(char *line, size_t linelen, struct request *req)
+{
+ char *t, *vers, *tname, *type, *service, *id, *key;
+ int sid;
+
+ t = line;
+ (void) linelen;
+
+ if (strncmp(t, "table|", 6)) {
+ log_warnx("malformed line");
+ return false;
+ }
+ t += 6;
+
+ vers = t;
+ if ((t = strchr(t, '|')) == NULL) {
+ log_warnx("malformed line: missing version");
+ return false;
+ }
+ *t++ = '\0';
+
+ if (strcmp(vers, "0.1") != 0) {
+ log_warnx("unsupported protocol version: %s", vers);
+ return false;
+ }
+
+ /* skip timestamp */
+ if ((t = strchr(t, '|')) == NULL) {
+ log_warnx("malformed line: missing timestamp");
+ return false;
+ }
+ *t++ = '\0';
+
+ tname = t;
+ if ((t = strchr(t, '|')) == NULL) {
+ log_warnx("malformed line: missing table name");
+ return false;
+ }
+ *t++ = '\0';
+ if (t - tname - 1 > (ptrdiff_t)req->tablesize) {
+ req->table = realloc(req->table, t - tname - 1);
+ req->tablesize = t - tname - 1;
+ if (!req->table)
+ fatal("realloc");
+ }
+ memcpy(req->table, tname, req->tablesize);
+
+ type = t;
+ if ((t = strchr(t, '|')) == NULL) {
+ log_warnx("malformed line: missing type");
+ return false;
+ }
+ *t++ = '\0';
+
+ if (!strcmp(type, "update")) {
+ if (line + linelen - t > (ptrdiff_t)req->idsize) {
+ req->id = realloc(req->id, line + linelen - t);
+ req->idsize = line + linelen - t;
+ if (!req->id)
+ fatal("realloc");
+ }
+ memcpy(req->id, t, line + linelen - t);
+ req->o = O_UPDATE;
+ return true;
+ }
+
+ service = t;
+ if ((t = strchr(t, '|')) == NULL) {
+ log_warnx("malformed line: missing service");
+ return false;
+ }
+ *t++ = '\0';
+ sid = service_id(service);
+
+ id = t;
+
+ if (!strcmp(type, "fetch")) {
+ if (line + linelen - id > (ptrdiff_t)req->idsize) {
+ req->id = realloc(req->id, line + linelen - id);
+ req->idsize = line + linelen - id;
+ if (!req->id)
+ fatal("realloc");
+ }
+ memcpy(req->id, id, line + linelen - id);
+ req->o = O_FETCH;
+ req->s = sid;
+ req->key = NULL;
+ return true;
+ }
+
+ if ((t = strchr(t, '|')) == NULL) {
+ log_warnx("malformed line: missing key");
+ return false;
+ }
+ *t++ = '\0';
+ if (t - id - 1 > (ptrdiff_t)req->idsize) {
+ req->id = realloc(req->id, t - id - 1);
+ req->idsize = t - id - 1;
+ if (!req->id)
+ fatal("realloc");
+ }
+ memcpy(req->id, id, t - id - 1);
+
+ key = t;
+ if (line + linelen - key > (ptrdiff_t)req->keysize) {
+ req->key = realloc(req->key, line + linelen - key);
+ req->keysize = line + linelen - key;
+ if (!req->key)
+ fatal("realloc");
+ }
+ memcpy(req->key, key, line + linelen - key);
+
+ if (!strcmp(type, "check")) {
+ req->o = O_CHECK;
+ req->s = sid;
+ return true;
+ } else if (!strcmp(type, "lookup")) {
+ req->o = O_LOOKUP;
+ req->s = sid;
+ return true;
+ } else {
+ log_warnx("unknown action %s", type);
+ return false;
+ }
+}
+
+void
+table_api_free_request(struct request *r)
+{
+ if (!r)
+ return;
+
+ free(r->id);
+ free(r->table);
+ free(r->key);
+ free(r);
+}
+
+static void
+do_callback(struct request *req)
+{
+ struct request *r;
+ switch (req->o) {
+ case O_UPDATE:
+ if (handler_update) {
+ fallback_update_handler(req->id, req->table);
+ return;
+ }
+ break;
+ case O_CHECK:
+ if (handler_check) {
+ fallback_check_handler(req->id, req->table, req->s, req->key);
+ return;
+ }
+ break;
+ case O_LOOKUP:
+ if (handler_lookup) {
+ fallback_lookup_handler(req->id, req->table, req->s, req->key);
+ return;
+ }
+ break;
+ case O_FETCH:
+ if (handler_fetch) {
+ fallback_fetch_handler(req->id, req->table, req->s);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ r = malloc(sizeof(*r));
+ *r = *req;
+ handler_async(r);
+}
+
+static void
+api_callback(int fd, short revents)
+{
+ bool retry = true;
+ char buf[BUFSIZ];
+ size_t linelen;
+ int serrno, ret;
+ char *line, *t;
+ struct request req = {0};
+
+ if (revents & (POLLERR|POLLNVAL)) {
+ exit(1);
+ }
+ if (revents & POLLHUP) {
+ exit(0);
+ }
+
+ do {
+ ret = read(fd, buf, sizeof(buf));
+ if (ret == 0) {
+ /* EOF */
+ exit(0);
+ }
+ if (ret < 0) {
+ serrno = errno;
+ if (serrno == EAGAIN || serrno == EWOULDBLOCK) {
+ retry = false;
+ continue;
+ }
+ err(1, "read");
+ }
+ evbuffer_add(inbuffer, buf, ret);
+ } while (retry);
+
+ while ((line = evbuffer_readline(inbuffer))) {
+ linelen = strlen(line);
+ t = line;
+ if (configured) {
+ if (!table_api_parse_line(line, linelen, &req)) {
+ errx(1, "can not parse input line: %s", line);
+ }
+ do_callback(&req);
+ free(line);
+ continue;
+ }
+
+ if (strncmp(t, "config|", 7) != 0)
+ errx(1, "unexpected config line: %s", line);
+ t += 7;
+
+ if (!strcmp(t, "ready")) {
+ configured = 1;
+
+ for (int s = K_ALIAS; s <= K_MAILADDRMAP; s <<= 1) {
+ printf("register|%s\n", table_api_service_name(s));
+ }
+
+ puts("register|ready");
+ if (fflush(stdout) == EOF)
+ err(1, "fflush");
+ }
+
+ free(line);
+ }
+}
+
+int
+table_api_dispatch(void)
+{
+ int serrno, r;
+ int flags;
+
+ dict_init(¶ms);
+ dict_init(&lookup_entries);
+
+ inbuffer = evbuffer_new();
+
+ flags = fcntl(STDIN_FILENO, F_GETFL, 0);
+ fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
+
+ fds[0].fd = STDIN_FILENO;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+ cbs[0] = api_callback;
+ if (!nfds)
+ nfds++;
+
+ while (nfds) {
+ r = poll(fds, nfds, 1024);
+ if (r == 0) {
+ /* TODO implement some timeout handling */
+ continue;
+ }
+ if (r < 0) {
+ serrno = errno;
+ if (serrno == ENOMEM || serrno == EAGAIN) {
+ continue;
+ }
+ }
+
+ for (nfds_t i = 0; i < nfds; i++) {
+ if (fds[i].revents) {
+ cbs[i](fds[i].fd, fds[i].revents);
+ }
+ }
+ }
+
+ return (0);
+}
diff --git a/table_stdio.h b/table_api.h
similarity index 55%
rename from table_stdio.h
rename to table_api.h
index 3afbbf2..eea37aa 100644
--- a/table_stdio.h
+++ b/table_api.h
@@ -15,6 +15,21 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#if defined(__clang__) || defined(__GNUC__)
+#define DEPRECATED __attribute__((deprecated))
+#else
+#define DEPRECATED
+#endif
+
+typedef void (*fd_callback)(int, short);
+
+enum table_operation {
+ O_UPDATE,
+ O_CHECK,
+ O_LOOKUP,
+ O_FETCH,
+};
+
enum table_service {
K_ALIAS = 0x001, /* returns struct expand */
K_DOMAIN = 0x002, /* returns struct destination */
@@ -28,10 +43,34 @@ enum table_service {
K_ANY = 0xfff,
};
+struct request {
+ char *id;
+ size_t idsize;
+ enum table_operation o;
+ char *table;
+ size_t tablesize;
+ enum table_service s;
+ char *key;
+ size_t keysize;
+};
+
+bool table_api_parse_line(char *line, size_t linelen, struct request *req);
+void table_api_free_request(struct request *req);
void table_api_register_services(int);
-void table_api_on_update(int(*)(void));
-void table_api_on_check(int(*)(int, struct dict *, const char *));
-void table_api_on_lookup(int(*)(int, struct dict *, const char *, char *, size_t));
+void table_api_on_update(int(*)(void)) DEPRECATED;
+void table_api_on_check(int(*)(int, struct dict *, const char *)) DEPRECATED;
+void table_api_on_lookup(int(*)(int, struct dict *, const char *, char *, size_t)) DEPRECATED;
void table_api_on_fetch(int(*)(int, struct dict *, char *, size_t));
+void table_api_on_request(void(*)(struct request *));
int table_api_dispatch(void);
-const char *table_api_get_name(void);
+void table_api_error(const char *, enum table_operation, const char *);
+void table_api_update_finish(const char *);
+void table_api_check_result(const char *, bool);
+void table_api_lookup_result(const char *, enum table_service, const char *);
+void table_api_lookup_finish(const char *);
+void table_api_fetch_result(const char *, const char *);
+const char *table_api_get_name(void) DEPRECATED;
+void table_api_register_fd(int fd, short events, fd_callback cb);
+void table_api_replace_fd(int old, int new);
+void table_api_remove_fd(int fd);
+void table_api_fd_set_events(int fd, short events);
diff --git a/table_ldap.c b/table_ldap.c
index d3bf260..7d6b267 100644
--- a/table_ldap.c
+++ b/table_ldap.c
@@ -25,15 +25,20 @@
#include <netdb.h>
#include <ctype.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <tls.h>
#include <unistd.h>
+#include "ber.h"
#include "aldap.h"
#include "dict.h"
#include "log.h"
-#include "table_stdio.h"
+#include "table_api.h"
#include "util.h"
#ifndef MAXIMUM
@@ -66,24 +71,22 @@ struct query_result {
char **v[MAX_ATTRS];
};
-static int ldap_run_query(int type, const char *, char *, size_t);
+static int ldap_open(void);
+static struct query * lookup_query(int type);
+static void ldap_lookup_entry(const struct request *req, const struct aldap_message *m);
+static struct aldap *ldap_connect(const char *addr);
+static void ldap_handle_response(const char *ldapid, const struct aldap_message *m, struct request *req);
+static void ldap_fd_callback(int fd, short revents);
+static int read_value(char **store, const char *key, const char *value);
static char *config, *url, *username, *password, *basedn, *ca_file;
+static struct dict requests;
static struct aldap *aldap;
static struct query queries[LDAP_MAX];
-static int
-table_ldap_update(void)
-{
- return 1;
-}
+static char *ldap_dn_attr[2] = { "dn", NULL };
-static int
-table_ldap_fetch(int service, struct dict *params, char *dst, size_t sz)
-{
- return -1;
-}
static struct aldap *
ldap_connect(const char *addr)
@@ -93,6 +96,7 @@ ldap_connect(const char *addr)
struct tls_config *tls_config = NULL;
struct addrinfo hints, *res0, *res;
int error, fd = -1;
+ int flags;
if (aldap_parse_url(addr, &lu) != 1) {
log_warnx("warn: ldap_parse_url fail");
@@ -140,6 +144,9 @@ ldap_connect(const char *addr)
goto out;
}
+ flags = fcntl(fd, F_GETFL, 0);
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
if (lu.protocol == LDAPS || lu.protocol == LDAPTLS) {
tls_config = tls_config_new();
if (!tls_config) {
@@ -168,6 +175,169 @@ out:
return ldap;
}
+static void
+ldap_lookup_entry(const struct request *req, const struct aldap_message *m)
+{
+ struct aldap_stringset *attr = NULL;
+ struct query *q = lookup_query(req->s);
+ char tmp[BUFSIZ];
+
+ switch (req->s) {
+ case K_ALIAS:
+ case K_MAILADDRMAP:
+ if (aldap_match_attr(m, q->attrs[0], &attr) == -1) {
+ return;
+ }
+ for (size_t i = 0; i < attr->len; i++) {
+ table_api_lookup_result(req->id, req->s, attr->str[i].ostr_val);
+ }
+ aldap_free_attr(attr);
+ break;
+ case K_DOMAIN:
+ case K_MAILADDR:
+ if (aldap_match_attr(m, q->attrs[0], &attr) == -1) {
+ break;
+ }
+ if (attr->len > 1)
+ log_warnx("req \"%s\" returned more then one attr \"%s\"", req->key, q->attrs[0]);
+ table_api_lookup_result(req->id, req->s, attr->str[0].ostr_val);
+ aldap_free_attr(attr);
+ break;
+ case K_CREDENTIALS:
+ if (aldap_match_attr(m, q->attrs[0], &attr) == -1)
+ break;
+ if (attr->len > 1)
+ log_warnx("req \"%s\" returned more then one attr \"%s\"", req->key, q->attrs[1]);
+ if (strlcat(tmp, attr->str[0].ostr_val, sizeof(tmp)) > sizeof(tmp))
+ break;
+ if (strlcat(tmp, ":", sizeof(tmp)) > sizeof(tmp))
+ break;
+ aldap_free_attr(attr);
+ if (aldap_match_attr(m, q->attrs[1], &attr) == -1)
+ break;
+ if (attr->len > 1)
+ log_warnx("req \"%s\" returned more then one attr \"%s\"", req->key, q->attrs[1]);
+ if (strlcat(tmp, attr->str[1].ostr_val, sizeof(tmp)) > sizeof(tmp))
+ break;
+ table_api_lookup_result(req->id, req->s, tmp);
+ break;
+ case K_USERINFO:
+ if (aldap_match_attr(m, q->attrs[0], &attr) == -1)
+ break;
+ if (attr->len > 1)
+ log_warnx("req \"%s\" returned more then one attr \"%s\"", req->key, q->attrs[0]);
+ if (strlcat(tmp, attr->str[0].ostr_val, sizeof(tmp)) > sizeof(tmp))
+ break;
+ if (strlcat(tmp, ":", sizeof(tmp)) > sizeof(tmp))
+ break;
+ aldap_free_attr(attr);
+ if (aldap_match_attr(m, q->attrs[1], &attr) == -1)
+ break;
+ if (attr->len > 1)
+ log_warnx("req \"%s\" returned more then one attr \"%s\"", req->key, q->attrs[1]);
+ if (strlcat(tmp, attr->str[1].ostr_val, sizeof(tmp)) > sizeof(tmp))
+ break;
+ if (strlcat(tmp, ":", sizeof(tmp)) > sizeof(tmp))
+ break;
+ aldap_free_attr(attr);
+ if (aldap_match_attr(m, q->attrs[2], &attr) == -1)
+ break;
+ if (attr->len > 1)
+ log_warnx("req \"%s\" returned more then one attr \"%s\"", req->key, q->attrs[2]);
+ if (strlcat(tmp, attr->str[1].ostr_val, sizeof(tmp)) > sizeof(tmp))
+ break;
+ table_api_lookup_result(req->id, req->s, tmp);
+ break;
+ default:
+ log_warnx("unhandled service");
+ break;
+ }
+
+ aldap_free_attr(attr);
+}
+
+static void
+ldap_handle_response(const char *ldapid, const struct aldap_message *m, struct request *req)
+{
+ switch (req->o) {
+ case O_CHECK:
+ switch (m->message_type) {
+ case LDAP_RES_SEARCH_ENTRY:
+ table_api_check_result(req->id, true);
+ break;
+ case LDAP_RES_SEARCH_RESULT:
+ table_api_check_result(req->id, false);
+ break;
+ default:
+ table_api_error(req->id, req->o, "unknown ldap response");
+ break;
+ }
+ dict_pop(&requests, ldapid);
+ table_api_free_request(req);
+ break;
+ case O_LOOKUP:
+ switch (m->message_type) {
+ case LDAP_RES_SEARCH_ENTRY:
+ ldap_lookup_entry(req, m);
+ break;
+ case LDAP_RES_SEARCH_RESULT:
+ if (m->page && m->page->cookie_len) {
+ table_api_error(req->id, req->o, "paginagion not yet implemented");
+ } else {
+ table_api_lookup_finish(req->id);
+ }
+ dict_pop(&requests, ldapid);
+ table_api_free_request(req);
+ break;
+ default:
+ table_api_error(req->id, req->o, "unknown ldap response");
+ dict_pop(&requests, ldapid);
+ table_api_free_request(req);
+ break;
+ }
+ default:
+ table_api_error(req->id, req->o, NULL);
+ dict_pop(&requests, ldapid);
+ table_api_free_request(req);
+ }
+}
+
+static void
+ldap_fd_callback(int fd, short revents)
+{
+ struct aldap_message *m = NULL;
+ struct request *req;
+ char ldapid[sizeof(int)*2+1];
+
+ if (revents & POLLHUP || revents & POLLERR) {
+ ldap_open();
+ return;
+ }
+
+ do {
+ aldap_freemsg(m);
+ m = aldap_parse(aldap, false);
+ if (!m) {
+ switch (aldap->err) {
+ case ALDAP_ERR_NEED_POLLOUT:
+ table_api_fd_set_events(aldap->fd, POLLOUT);
+ break;
+ case ALDAP_ERR_NEED_POLLIN:
+ table_api_fd_set_events(aldap->fd, POLLIN);
+ break;
+ default:
+ ldap_open();
+ }
+ continue;
+ }
+ snprintf(ldapid, sizeof(ldapid), "%x", m->msgid);
+ req = dict_get(&requests, ldapid);
+ if (req)
+ ldap_handle_response(ldapid, m, req);
+ } while (m);
+ aldap_freemsg(m);
+}
+
static int
read_value(char **store, const char *key, const char *value)
{
@@ -331,8 +501,10 @@ static int
ldap_open(void)
{
struct aldap_message *amsg = NULL;
+ int oldfd = 0;
if (aldap) {
+ oldfd = aldap->fd;
aldap_close(aldap);
log_info("info: table-ldap: closed previous connection");
}
@@ -348,7 +520,7 @@ ldap_open(void)
goto err;
}
- if ((amsg = aldap_parse(aldap)) == NULL) {
+ if ((amsg = aldap_parse(aldap, true)) == NULL) {
log_warnx("warn: aldap_parse");
goto err;
}
@@ -366,6 +538,13 @@ ldap_open(void)
goto err;
}
+ if (!oldfd) {
+ table_api_register_fd(aldap->fd, POLLIN, ldap_fd_callback);
+ } else {
+ table_api_replace_fd(oldfd, aldap->fd);
+ table_api_fd_set_events(aldap->fd, POLLIN);
+ }
+
if (amsg)
aldap_freemsg(amsg);
return 1;
@@ -378,134 +557,10 @@ err:
return 0;
}
-static int
-table_ldap_lookup(int service, struct dict *params, const char *key, char *dst, size_t sz)
+static struct query *
+lookup_query(int type)
{
- int ret;
-
- switch(service) {
- case K_ALIAS:
- case K_DOMAIN:
- case K_CREDENTIALS:
- case K_USERINFO:
- case K_MAILADDR:
- case K_MAILADDRMAP:
- case K_NETADDR:
- if ((ret = ldap_run_query(service, key, dst, sz)) >= 0) {
- return ret;
- }
- log_debug("debug: table-ldap: reconnecting");
- if (!ldap_open()) {
- log_warnx("warn: table-ldap: failed to connect");
- return -1;
- }
- return ldap_run_query(service, key, dst, sz);
- default:
- return -1;
- }
-}
-
-static int
-realloc_results(struct query_result **r, size_t *num)
-{
- struct query_result *new;
- size_t newsize = MAXIMUM(1, (*num)*2);
- if ((new = reallocarray(*r, newsize, sizeof(**r))) == NULL)
- return 0;
- *num = newsize;
- *r = new;
- return 1;
-}
-
-static int
-ldap_query(const char *filter, const char *key, char **attributes, size_t attrn, struct query_result **results, size_t *nresults)
-{
- struct aldap_message *m = NULL;
- struct aldap_page_control *pg = NULL;
- struct aldap_stringset *ldap_res;
- struct query_result *res = NULL;
- int ret;
- size_t i, j, k, found = 0, nres = 0;
-
- do {
- ret = -1;
- if (aldap_search(aldap, basedn, LDAP_SCOPE_SUBTREE,
- filter, key, attributes, 0, 0, 0, pg) == -1) {
- goto end;
- }
- if (pg != NULL) {
- aldap_freepage(pg);
- pg = NULL;
- }
-
- while ((m = aldap_parse(aldap)) != NULL) {
- if (aldap->msgid != m->msgid)
- goto end;
- if (m->message_type == LDAP_RES_SEARCH_RESULT) {
- if (m->page != NULL && m->page->cookie_len)
- pg = m->page;
- aldap_freemsg(m);
- m = NULL;
- ret = 0;
- break;
- }
- if (m->message_type != LDAP_RES_SEARCH_ENTRY)
- goto end;
-
- if (found >= nres) {
- if (!realloc_results(&res, &nres)) {
- goto end;
- }
- }
- memset(&res[found], 0, sizeof(res[found]));
- for (i = 0; i < attrn; ++i) {
- if (aldap_match_attr(m, attributes[i], &ldap_res) != 1) {
- goto end;
- }
- res[found].v[i] = calloc(ldap_res->len + 1, sizeof(*res[found].v[i]));
- for (j = 0; j < ldap_res->len; j++) {
- res[found].v[i][j] = strndup(ldap_res->str[j].ostr_val, ldap_res->str[j].ostr_len);
- }
- aldap_free_attr(ldap_res);
- }
- aldap_freemsg(m);
- m = NULL;
- found++;
- }
- } while (pg != NULL);
-
-end:
- if (ret == -1) {
- for (i = 0; i < found; i++) {
- for (j = 0; j < attrn; j++) {
- for (k = 0; res[i].v[j][k]; k++) {
- free(res[i].v[j][k]);
- }
- free(res[i].v[j]);
- }
- }
- free(res);
- } else {
- ret = found ? 1 : 0;
- *results = res;
- *nresults = found;
- }
-
- if (m)
- aldap_freemsg(m);
- log_debug("debug: table_ldap: ldap_query: filter=%s, key=%s, ret=%d", filter, key, ret);
- return ret;
-}
-
-static int
-ldap_run_query(int type, const char *key, char *dst, size_t sz)
-{
- struct query *q;
- struct query_result *res = NULL;
- int ret;
- size_t i, j, k, nres = 0;
- char *r, *user, *pwhash, *uid, *gid, *home;
-
+ struct query *q;
switch (type) {
case K_ALIAS: q = &queries[LDAP_ALIAS]; break;
case K_DOMAIN: q = &queries[LDAP_DOMAIN]; break;
@@ -517,103 +572,56 @@ ldap_run_query(int type, const char *key, char *dst, size_t sz)
case K_MAILADDRMAP: q = &queries[LDAP_MAILADDRMAP]; break;
case K_ADDRNAME: q = &queries[LDAP_ADDRNAME]; break;
default:
- return -1;
+ return NULL;
}
+ return q;
+}
- if (!q->filter) {
- /* XXX get the string of the type */
- log_warnx("warn: query %d without a filter configured", type);
- return -1;
+static void
+table_ldap_callback(struct request *req)
+{
+ char ldapid[sizeof(int)*2+1];
+ int ret;
+ struct query *q = lookup_query(req->s);
+ char * const *attrs;
+ int num;
+
+ if (!q) {
+ table_api_error(req->id, req->o, "service not configured");
+ return;
}
- ret = ldap_query(q->filter, key, q->attrs, q->attrn, &res, &nres);
- if (ret <= 0 || dst == NULL)
- goto end;
-
- switch (type) {
-
- case K_ALIAS:
- case K_MAILADDRMAP:
- memset(dst, 0, sz);
- for (i = 0; ret != -1 && i < nres; i++) {
- for (j = 0; res[i].v[0][j]; j++) {
- if ((i || j) && strlcat(dst, ", ", sz) >= sz) {
- ret = -1;
- break;
- }
- if (strlcat(dst, res[i].v[0][j], sz) >= sz) {
- ret = -1;
- break;
- }
- }
- }
- break;
- case K_DOMAIN:
- case K_MAILADDR:
- r = res[0].v[0][0];
- if (!r || strlcpy(dst, r, sz) >= sz)
- ret = -1;
+ switch (req->o) {
+ case O_UPDATE:
+ table_api_error(req->id, req->o, "update not implemented");
+ table_api_free_request(req);
+ return;
+ case O_FETCH:
+ table_api_error(req->id, req->o, "fetch not implemented");
+ table_api_free_request(req);
+ return;
+ case O_CHECK:
+ attrs = ldap_dn_attr;
+ num = 1;
break;
- case K_CREDENTIALS:
- user = res[0].v[0][0];
- pwhash = res[0].v[1][0];
- if (!user || !pwhash || snprintf(dst, sz, "%s:%s", user, pwhash) >= (int)sz)
- ret = -1;
- break;
- case K_USERINFO:
- uid = res[0].v[0][0];
- gid = res[0].v[1][0];
- home = res[0].v[2][0];
- if (!uid || !gid || !home || snprintf(dst, sz, "%s:%s:%s", uid, gid, home) >= (int)sz)
- ret = -1;
+ case O_LOOKUP:
+ attrs = q->attrs;
+ num = 100;
break;
default:
- log_warnx("warn: unsupported lookup kind");
- ret = -1;
+ table_api_error(req->id, req->o, "unknown operation not implemented");
+ table_api_free_request(req);
+ return;
}
- if (ret == -1)
- log_warnx("warn: could not format result");
-
-end:
- for (i = 0; i < nres; i++) {
- for (j = 0; j < q->attrn; ++j) {
- for (k = 0; res[i].v[j][k]; k++) {
- free(res[i].v[j][k]);
- }
- free(res[i].v[j]);
- }
- }
- free(res);
-
- return ret;
-}
-
-static int
-table_ldap_check(int service, struct dict *params, const char *key)
-{
- int ret;
-
- switch(service) {
- case K_ALIAS:
- case K_DOMAIN:
- case K_CREDENTIALS:
- case K_USERINFO:
- case K_MAILADDR:
- case K_MAILADDRMAP:
- case K_NETADDR:
- if ((ret = ldap_run_query(service, key, NULL, 0)) >= 0) {
- return ret;
- }
- log_debug("debug: table-ldap: reconnecting");
- if (!ldap_open()) {
- log_warnx("warn: table-ldap: failed to connect");
- return -1;
- }
- return ldap_run_query(service, key, NULL, 0);
- default:
- return -1;
+ ret = aldap_search(aldap, basedn, LDAP_SCOPE_SUBTREE, q->filter, req->key, attrs, false, num, 0, NULL);
+ if (ret < 0) {
+ table_api_error(req->id, req->o, NULL);
+ ldap_open();
+ return;
}
+ snprintf(ldapid, sizeof(ldapid), "%x", ret);
+ dict_xset(&requests, ldapid, req);
}
int
@@ -623,6 +631,7 @@ main(int argc, char **argv)
log_init(1);
log_setverbose(~0);
+ dict_init(&requests);
while ((ch = getopt(argc, argv, "")) != -1) {
switch (ch) {
@@ -647,10 +656,7 @@ main(int argc, char **argv)
fatalx("failed to connect");
log_debug("debug: connected");
- table_api_on_update(table_ldap_update);
- table_api_on_check(table_ldap_check);
- table_api_on_lookup(table_ldap_lookup);
- table_api_on_fetch(table_ldap_fetch);
+ table_api_on_request(table_ldap_callback);
table_api_dispatch();
return 0;
diff --git a/table_stdio.c b/table_stdio.c
deleted file mode 100644
index 289deab..0000000
--- a/table_stdio.c
+++ /dev/null
@@ -1,276 +0,0 @@
-/* $OpenBSD$ */
-
-/*
- * Copyright (c) 2024 Omar Polo <[email protected]>
- *
- * 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 "config.h"
-
-#include <sys/tree.h>
-
-#include <err.h>
-#include <limits.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "dict.h"
-#include "table_stdio.h"
-
-static int (*handler_update)(void);
-static int (*handler_check)(int, struct dict *, const char *);
-static int (*handler_lookup)(int, struct dict *, const char *, char *, size_t);
-static int (*handler_fetch)(int, struct dict *, char *, size_t);
-
-static char tablename[128];
-
-/*
- * backword compatibility:
- * register all the services since we don't have a clue yet what the
- * table will do
- */
-static int registered_services = K_ANY;
-
-/* Dummy; just kept for backward compatibility */
-static struct dict params;
-
-static int
-service_id(const char *service)
-{
- if (!strcmp(service, "alias"))
- return (K_ALIAS);
- if (!strcmp(service, "domain"))
- return (K_DOMAIN);
- if (!strcmp(service, "credentials"))
- return (K_CREDENTIALS);
- if (!strcmp(service, "netaddr"))
- return (K_NETADDR);
- if (!strcmp(service, "userinfo"))
- return (K_USERINFO);
- if (!strcmp(service, "source"))
- return (K_SOURCE);
- if (!strcmp(service, "mailaddr"))
- return (K_MAILADDR);
- if (!strcmp(service, "addrname"))
- return (K_ADDRNAME);
- if (!strcmp(service, "mailaddrmap"))
- return (K_MAILADDRMAP);
-
- errx(1, "unknown service %s", service);
-}
-
-static char *
-table_api_service_name(enum table_service s)
-{
- switch (s) {
- case K_ALIAS: return "alias";
- case K_DOMAIN: return "domain";
- case K_CREDENTIALS: return "credentials";
- case K_NETADDR: return "netaddr";
- case K_USERINFO: return "userinfo";
- case K_SOURCE: return "source";
- case K_MAILADDR: return "mailaddr";
- case K_ADDRNAME: return "addrname";
- case K_MAILADDRMAP: return "mailaddrmap";
- default: return "???";
- }
-}
-
-void
-table_api_register_services(int s)
-{
- registered_services = K_ANY & s;
-}
-
-void
-table_api_on_update(int(*cb)(void))
-{
- handler_update = cb;
-}
-
-void
-table_api_on_check(int(*cb)(int, struct dict *, const char *))
-{
- handler_check = cb;
-}
-
-void
-table_api_on_lookup(int(*cb)(int, struct dict *, const char *, char *, size_t))
-{
- handler_lookup = cb;
-}
-
-void
-table_api_on_fetch(int(*cb)(int, struct dict *, char *, size_t))
-{
- handler_fetch = cb;
-}
-
-const char *
-table_api_get_name(void)
-{
- return tablename;
-}
-
-int
-table_api_dispatch(void)
-{
- char buf[LINE_MAX];
- char *t, *vers, *tname, *type, *service, *id, *key;
- char *line = NULL;
- size_t linesize = 0;
- ssize_t linelen;
- int sid, r, configured = 0;
-
- dict_init(¶ms);
-
- while ((linelen = getline(&line, &linesize, stdin)) != -1) {
- if (line[linelen - 1] == '\n')
- line[--linelen] = '\0';
- t = line;
-
- if (!configured) {
- if (strncmp(t, "config|", 7) != 0)
- errx(1, "unexpected config line: %s", line);
- t += 7;
-
- if (!strcmp(t, "ready")) {
- configured = 1;
-
- for (int s = K_ALIAS; s <= K_MAILADDRMAP; s <<= 1) {
- printf("register|%s\n", table_api_service_name(s));
- }
-
- puts("register|ready");
- if (fflush(stdout) == EOF)
- err(1, "fflush");
- continue;
- }
-
- continue;
- }
-
- if (strncmp(t, "table|", 6))
- errx(1, "malformed line");
- t += 6;
-
- vers = t;
- if ((t = strchr(t, '|')) == NULL)
- errx(1, "malformed line: missing version");
- *t++ = '\0';
-
- if (strcmp(vers, "0.1") != 0)
- errx(1, "unsupported protocol version: %s", vers);
-
- /* skip timestamp */
- if ((t = strchr(t, '|')) == NULL)
- errx(1, "malformed line: missing timestamp");
- *t++ = '\0';
-
- tname = t;
- if ((t = strchr(t, '|')) == NULL)
- errx(1, "malformed line: missing table name");
- *t++ = '\0';
- strlcpy(tablename, tname, sizeof(tablename));
-
- type = t;
- if ((t = strchr(t, '|')) == NULL)
- errx(1, "malformed line: missing type");
- *t++ = '\0';
-
- if (!strcmp(type, "update")) {
- if (handler_update == NULL)
- errx(1, "no update handler registered");
-
- id = t;
- r = handler_update();
- printf("update-result|%s|%s\n", id,
- r == -1 ? "error" : "ok");
- if (fflush(stdout) == EOF)
- err(1, "fflush");
- continue;
- }
-
- service = t;
- if ((t = strchr(t, '|')) == NULL)
- errx(1, "malformed line: missing service");
- *t++ = '\0';
- sid = service_id(service);
-
- id = t;
-
- r = -1;
- if (!strcmp(type, "fetch")) {
- if (handler_fetch == NULL)
- errx(1, "no fetch handler registered");
-
- if (registered_services & sid) {
- r = handler_fetch(sid, ¶ms,
- buf, sizeof(buf));
- }
- if (r == 1)
- printf("fetch-result|%s|found|%s\n", id, buf);
- else if (r == 0)
- printf("fetch-result|%s|not-found\n", id);
- else
- printf("fetch-result|%s|error\n", id);
- if (fflush(stdout) == EOF)
- err(1, "fflush");
- memset(buf, 0, sizeof(buf));
- continue;
- }
-
- if ((t = strchr(t, '|')) == NULL)
- errx(1, "malformed line: missing key");
- *t++ = '\0';
- key = t;
-
- if (!strcmp(type, "check")) {
- if (handler_check == NULL)
- errx(1, "no check handler registered");
- if (registered_services & sid) {
- r = handler_check(sid, ¶ms, key);
- }
- if (r == 1)
- printf("check-result|%s|found\n", id);
- else if (r == 0)
- printf("check-result|%s|not-found\n", id);
- else
- printf("check-result|%s|error\n", id);
- } else if (!strcmp(type, "lookup")) {
- if (handler_lookup == NULL)
- errx(1, "no lookup handler registered");
- if (registered_services & sid) {
- r = handler_lookup(sid, ¶ms, key,
- buf, sizeof(buf));
- }
- if (r == 1)
- printf("lookup-result|%s|found|%s\n", id, buf);
- else if (r == 0)
- printf("lookup-result|%s|not-found\n", id);
- else
- printf("lookup-result|%s|error\n", id);
- memset(buf, 0, sizeof(buf));
- } else
- errx(1, "unknown action %s", type);
-
- if (fflush(stdout) == EOF)
- err(1, "fflush");
- }
-
- if (ferror(stdin))
- err(1, "getline");
-
- return (0);
-}